[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"env\",\n    \"react\"\n  ],\n  \"plugins\": [\n    \"transform-class-properties\",\n    \"transform-object-rest-spread\"\n  ],\n  \"ignore\": [\n    \"./biz/webui/htdocs/src/js/components/json/eval.js\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\nmax_line_length = 80\ntrim_trailing_whitespace = true\n\n[*.md]\nmax_line_length = 0\ntrim_trailing_whitespace = false\n\n[COMMIT_EDITMSG]\nmax_line_length = 0"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules\n/biz/webui/htdocs/js\n/biz/webui/htdocs/src/js/components\n/test/assets/values\n/test/plugins/whistle.test/assets\n/bin/import.js\n/test/all_whistles\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"extends\": [\n    \"eslint:recommended\",\n    \"plugin:react/recommended\"\n  ],\n  \"parser\": \"babel-eslint\",\n  \"parserOptions\": {\n    \"ecmaFeatures\": {\n      \"experimentalObjectRestSpread\": true,\n      \"jsx\": true\n    }\n  },\n  \"plugins\": [\n    \"react\"\n  ],\n  \"rules\": {\n    \"indent\": [\n      \"error\",\n      2\n    ],\n    \"react/display-name\": 0,\n    \"react/prop-types\": 0,\n    \"react/no-direct-mutation-state\": 0,\n    \"linebreak-style\": [\n      \"error\",\n      \"unix\"\n    ],\n    \"quotes\": [\n      \"error\",\n      \"single\"\n    ],\n    \"semi\": [\n      \"error\",\n      \"always\"\n    ],\n    \"no-unused-vars\": [\n      \"error\",\n      {\n        \"vars\": \"all\",\n        \"args\": \"none\"\n      }\n    ],\n    \"no-empty\": [\n      \"error\",\n      {\n        \"allowEmptyCatch\": true\n      }\n    ],\n    \"no-cond-assign\": \"off\"\n  }\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "biz/webui/htdocs/js/index.js diff=nodiff\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\n.DS_Store\ntmp\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# Commenting this out is preferred by some people, see\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-\nnode_modules\n\n# Users Environment Variables\n.lock-wscript\n\n# OS X\n.DS_Store*\nIcon?\n._*\n\n\n\n# Windows\nThumbs.db\nehthumbs.db\nDesktop.ini\n\n# Linux\n.directory\n.running\n*~\n\n\n# npm\nnode_modules\n*.log\n*.gz\n\n\n# Coveralls\ncoverage\n\n\n# test\n/test/.whistle\n\n# docs\n_book\ndist\n/.history\n/test/temp_files\n/test/all_whistles\n/docs/.vitepress/dist\n/docs/.vitepress/cache\n"
  },
  {
    "path": ".npmignore",
    "content": "# Logs\nlogs\n*.log\n.DS_Store\ntmp\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directory\n# Commenting this out is preferred by some people, see\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-\nnode_modules\n\n# Users Environment Variables\n.lock-wscript\n\n# OS X\n.DS_Store*\nIcon?\n._*\n\n\n\n# Windows\nThumbs.db\nehthumbs.db\nDesktop.ini\n\n# Linux\n.directory\n.running\n*~\n\n\n# npm\nnode_modules\n*.log\n*.gz\n.gitignore\n.gitattributes\n.npmignore\n.travis.yml\n.eslintrc\n.editorconfig\n.eslintignore\n.babelrc\ntest\nassets/launcher\n/biz/webui/htdocs/src\n/docs\n/.history\nCHANGELOG*\nDockerfile\n/README-en_US.md\n/biz/webui/htdocs/js/decode.js\n\n\n# Coveralls\ncoverage\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n  - \"10\"\n  - \"11\"\n  - \"12\"\n  - \"13\"\n  - \"14\"\n  - \"15\"\n  - \"16\"\n  - \"17\"\n  - \"18\"\n\ninstall:\n  - npm install\n\nscript:\n  - npm run cov\n\nafter_script:\n  - npm i codecov && codecov\n"
  },
  {
    "path": "CHANGELOG-en_US.md",
    "content": "[中文](./CHANGELOG.md) · English\n\n## v2.10.1\n1. fix: https://github.com/avwo/whistle/issues/1296\n2. fix: https://github.com/avwo/whistle/issues/1297\n3. feat: Quickly filter requests with matching rules\n\n## v2.10.0\n1. feat: Move the Raw Tab of Inspectors to the first position.\n2. feat: Local mapping paths are not allowed to contain `../` path segments.\n3. feat: Allow disabling or enabling custom certificates without deletion.\n4. feat: Plugin variable `%plugin-name=xxx` now supports all plugin hooks.\n5. refactor: Simplify the code passing mechanism between Whistle and plugins, improving efficiency.\n6. fix: Improve test cases and fix hidden bugs.\n\n## v2.9\n1. feat: UI and Experience Improvements:\n      - Redesigned Dark mode interface according to Dark mode specifications.\n      - Network supports filtering by type and manually saving captured packet data.\n      - Composer supports importing CURL commands and displaying SSE/WebSocket content.\n      - JSON Viewer right-click menu now supports copying selected text.\n      - Support for displaying TTFB, SSE content, and WebSocket Frames.\n      - Added `frameScript` for modifying WebSocket/TCP request content via JS.\n2. feat: Rule and Configuration Enhancements:\n     - Support loading default configurations via `~/.whistlerc`.\n     - Rules support viewing `Enabled Rules` and grouping functionality.\n     - Support setting matching probability: `includeFilter://chance:0.5`.\n     - Added `delete://` protocol to delete request parameters, cookies, etc.\n     - Support `req.passThrough({transformReq, transformRes, ...})`.\n     - Embedded values now have independent scopes (Rules, Plugins, Header Rules are isolated).\n3. feat: Plugin System Improvements:\n     - Support installing/uninstalling plugins via the UI.\n     - Added custom right-click menu to the plugin list.\n     - Plugins support `sharedStorage` for data sharing across different instances.\n     - Plugin Option pages support setting favicon and opening as dialog boxes.\n     - Support installing plugins via plugins.\n4. feat: Network and Proxy Features:\n     - Enabled `localhostCompatible` mode by default.\n     - Support for IPv6-only networks.\n     - Support for SOCKS proxy and custom DNS servers.\n     - Added `https-proxy` and `internal-http-proxy` protocols.\n     - Support for HTTP2 functionality (requires Node 10.16.0+).\n5. feat: Certificate and Security:\n     - Default certificate format changed to .cer for better compatibility with more devices.\n     - Support for custom client certificates and mutual TLS authentication.\n     - Root certificates can be updated via `w2 ca` upon expiration.\n\n## v2.8\n1. feat: Multi-process Support\n      - Added `--cluster [workers]` startup mode, supporting multi-process operation.\n      - Fixed an issue where plugin remote rules failed to access when binding to a non-local network interface during startup.\n2. feat: Certificate Management Optimization\n      - Auto-generated certificates are automatically renewed upon expiration (valid for one year).\n      - Prohibited uploading root certificate files (`root.key & root.crt`) via the web interface.\n      - Support for non-SNI requests to obtain custom certificates via plugins.\n      - Support for directly uploading and deleting user-defined certificates.\n3. feat: Plugin System Enhancements\n      - Plugin `server` hooks support setting dynamic rules via `req.setReqRules` and `req.setResRules`.\n      - Plugins can obtain client connection information (`originalReq.remoteAddress` and `originalReq.remotePort`).\n      - Fixed an issue where requests did not re-enter TUNNEL proxy when the plugin `sniCallback` returned `false`.\n      - Optimized plugin rule execution: req and res rules are now executed separately.\n4. feat: Proxy Rule Enhancements\n      - Support filtering `ignore://*` via `ignore://-*`.\n      - Support `proxy` and `pac` configurations with `lineProps://proxyHostOnly`, making the proxy effective only when the user configures a `host`.\n      - HTTPS Server started with `--httpsPort` can obtain certificates from plugins.\n5. feat: Filter Rule Extensions\n      - Support filtering requests by source using `excludeFilter://from=httpServer`, `includeFilter://from=httpsServer`, etc.\n      - Support modifying the domain or port of log and weinre requests via `enable://useLocalHost` and `enable://useSafePort`.\n6. feat: Request Header Control\n      - `x-forwarded-host` and `x-forwarded-proto` are disabled by default. They can be enabled via:\n      - Startup parameters `-M x-forwarded-host|x-forwarded-proto`.\n      - Request header `x-whistle-forwarded-props: host,proto,for,clientIp,ip`.\n7. feat: Custom Interface\n      - Support custom `inspectors tab`.\n      - The interface provides an `api.selectIndex` method to select captured packet data at a specific index.\n8. feat: Data Limits and Optimization\n      - Default displayed captured packet data is limited to 1.5MB, expandable to 2.5MB via `enable://bigData`.\n      - Support setting response rules via request headers.\n      - Optimized certificate fetching logic, merging multiple identical requests.\n9. File Path Handling\n      - Distinct handling of `reqWrite:///path/to/` and `reqWrite:///path/to`, where the former is automatically completed to `index.html`.\n10. Merge Operation Enhancement\n      - `resMerge://json1 resMerge://json2` uses shallow merge by default; deep merge can be enabled via `resMerge://true`.\n11. feat: Force Write Support\n      - Support forcing `reqWrite`, `reqWriteRaw`, `resWrite`, and `resWriteRaw` via `enable://forceReqWrite` and `enable://forceResWrite`.\n12. feat: Authentication Hook Optimization\n     - The plugin's auth hook now applies to resolved HTTPS requests by default.\n     - Support enabling the auth hook for tunnel proxy requests via `enable://authCapture`.\n13. fix: Issue Fixes\n     - Fixed a potential issue where saz files could not be imported.\n     - Fixed a memory leak issue in `sniCallback`.\n     - Handled `unhandledRejection` events.\n14. refactor: Code Optimizations\n     - When a plugin receives an HTTPS request, `req.url` will contain the full path.\n     - Optimized certificate fetching logic to reduce duplicate requests.\n     - Support disabling `enable://abort` via `disable://abort`.\n\n## v2.7\n\n1. feat: HTTP2 Feature Extensions\n      - **HTTP2 non-HTTPS Support**: HTTP2 protocol now supports non-HTTPS requests.\n      - **Performance Monitoring**: Third-party integrations can obtain runtime data like CPU, memory, request volume via `proxy.on('perfDataChange')`.\n2. feat: Comprehensive Plugin System Enhancements\n      - **Async Support**: Plugin hooks now support `async-await` syntax.\n      - **Authentication Integration**: Plugin `whistleConfig` supports configuring `inheritAuth` to reuse Whistle login accounts.\n      - **Plugin Management**: Support automatic loading of the plugin when executing `w2 run` in the plugin's root directory.\n      - **Configuration Sharing**: Plugins support obtaining certificates for specified domains via `options.getCert()`.\n      - **Dependency Reference**: Plugins can directly reference Whistle internal modules via `options.require`.\n3. feat: Command Line Tool Improvements\n      - **Configuration Management**: Support loading startup configurations via `--config localFile` (higher priority than command line).\n      - **Instance Management**: Optimized `w2 stop` command; displays all running instances when none is found.\n      - **Docker Support**: Added Dockerfile to the source directory.\n4. feat: DNS Resolution Enhancements\n      - **Custom DNS**: Support custom DNS servers via the `--dnsServer` parameter.\n      - **DNS over HTTPS**: Support for DNS-over-HTTPS services like `http://dns.alidns.com/resolve`.\n      - **IPv6 Support**: Manual specification of IPv6 DNS server addresses is possible.\n5. feat: Proxy Protocol Optimizations\n      - **PAC Authentication**: `pac` protocol supports setting username and password: `pac://user:pass@pacPath`.\n      - **Tunnel Proxy**: Support for plugins setting `tunnelKey` to pass tunnel proxy request headers.\n      - **Proxy Forwarding**: Optimized `lineProps://proxyHost|proxyTunnel|proxyFirst` configuration.\n6. feat: Request Header Processing\n      - **Forwarding Control**: Support disabling specific forwarding headers via `-M disableForwardedHost` and `-M disableForwardedProto`.\n      - **Custom Parsing**: `pipe://xxx` supports plugins internally obtaining original rule values.\n7. feat: Network View Upgrade\n      - **Tree View Support**: Network now supports displaying captured packet data in a tree view.\n      - **Raw URL Display**: Support for displaying the original URL (Raw Url).\n      - **Data Size Display**: The Body column in Network shows the request content size.\n      - **List Optimization**: Fixed data updating and sorting issues in Tree View and List View.\n8. feat: Interface Interaction Improvements\n      - **Composer History**: Optimized display of Composer history list.\n      - **Left Menu**: Optimized left menu layout and interaction.\n      - **Status Hints**: More obvious reminders when Rules and Plugins are disabled.\n      - **Shortcut Support**: Fixed shortcut key issues in Values right-click menu.\n9. feat: Rule Editor Enhancements\n      - **Rule Sorting**: Support adding Rules to the top.\n      - **Error Handling**: Fixed an error when entering `!` in the Rules editor.\n      - **Intelligent Suggestions**: `pipe` protocol now supports intelligent suggestions.\n10. feat: Authentication Feature Extensions\n        - **Plugin Authentication**: Plugin `auth` method supports `req.setRedirect(url)` for redirection.\n        - **Login Box Support**: Plugin `auth` supports setting `req.showLoginBox` to pop up a login box.\n        - **Internal Requests**: Plugin `auth` method now supports handling Whistle internal requests.\n11. feat: Certificate Management\n        - **Root Certificate Management**: Support displaying custom root certificates and deletion guidance.\n        - **Remote Certificates**: Support obtaining remote certificates via the plugin `sniCallback(req, options)` hook.\n12. feat: Custom Parsers\n        - **HTTP Parsing**: Regular HTTP requests now also support `customParser` (or `customFrames`).\n        - **Data Replacement**: Fixed inaccurate matching issues in `reqReplace` and `resReplace` due to packet splitting.\n13. feat: Protocol Support Extensions\n        - **Status Code Replacement**: WebSocket and Tunnel requests support `replaceStatus`.\n        - **Console Log Control**: Support preventing `log://` from intercepting `console` via `disable://interceptConsole`.\n        - **CORS Handling**: `resCors://enable` automatically ignores when the request header lacks an `origin`.\n14. feat: Memory Optimization\n        - **Empty Request Handling**: Removed `Empty Request` to reduce memory and CPU usage.\n        - **Stack Overflow Fix**: Fixed \"Maximum call stack size exceeded\" errors.\n        - **Storage Separation**: Plugins use different storage directories in different instances.\n15. feat: Startup and Runtime Optimization\n        - **Startup Speed**: Optimized Whistle startup speed.\n        - **Delay Implementation**: Optimized the implementation mechanism of `reqDelay` and `resDelay`.\n        - **Plugin Loading**: Third-party integrations can listen to plugin loading events.\n16. feat: Template String Enhancements\n        - **Port Variables**: Support obtaining port information via `clientPort` and `serverPort` in template strings.\n        - **Plugin Variables**: Support setting multiple `%plugin-name=xxxx` simultaneously in rules (up to 10).\n17. fix: Issue Fixes\n        - **Dialog Customization**: Browser built-in windows like `alert`, `confirm`, `prompt` now use custom implementations to prevent Chrome restrictions.\n        - **WebSocket Capture**: Fixed an issue where WebSocket packets could not be captured.\n        - **Plugin Storage**: Fixed an issue where `storage.setProperties` in plugins could fail.\n        - **Request Pause**: Fixed response stream pause issues under certain conditions.\n        - **Third-party Integration**: Fixed internal request forwarding issues during third-party integrations.\n18. feat: Feature Adjustments\n        - **Deletion Timing**: Adjusted the execution timing of `delete://reqH.xxxx`.\n        - **Forwarding Logic**: Optimized the implementation of internal request forwarding logic.\n19. feat: Other Improvements\n        - **Rule Mode**: Support setting `-M shadowRules` (capture + set rules) or `-M shadowRulesOnly` (rules only).\n        - **UI Requests**: Display UI request status.\n        - **Error Messages**: Optimized error messages.\n        - **Protocol Display**: Display the HTTP protocol type forwarded by plugins.\n\n## v2.6\n\n1. feat: System Monitoring and Status\n      - **Online Status Monitoring**: View current process request count, CPU, and memory status via the `Online` menu.\n      - **API Monitoring Interface**: Support obtaining runtime information via `proxy.getRuntimeInfo()`.\n      - **Performance Metrics**: The Online panel supports displaying QPS and maximum values of memory, CPU, and QPS.\n2. feat: Data Management\n      - **Recycle Bin Feature**: Deleted Rules or Values are first moved to the Recycle Bin (up to 120 cached items), with support for recovery.\n      - **Certificate Generation Tool**: `Network > Tools > ToolBox` supports generating certificates for corresponding domains.\n      - **HAR File Export**: Support exporting captured packet data as HAR format files.\n3. feat: Network Feature Enhancements\n      - **Advanced Search**: The search box supports filtering with up to 3 keywords.\n      - **View Source**: Right-click menu adds `Open/Source` to view captured packet data source code.\n      - **SVG Preview**: Support previewing SVG files.\n4. feat: Editor and Rule Management\n      - **Read-Only Mode**: Support setting Rules & Values editors to read-only mode via the `disabledEditor=1` parameter.\n      - **Line Number Comments**: When line numbers are enabled, double-clicking the line number can comment or uncomment.\n      - **Rule Highlighting**: Optimized editor highlighting for plugin rules.\n5. feat: Interaction Improvements\n      - **Toolset Extension**: `Network / Tools / Toolbox` supports converting objects to Query parameters.\n      - **Quick Operations**: Support extending the `util.openEditor(value)` method.\n6. feat: Rule Import/Export\n      - **Rule Import**: Support importing rules into Rules via `--shadowRules jsonString`.\n      - **Command Line Output**: Optimized information display when starting from the command line.\n7. feat: Mode Settings\n      - **Operation Modes**: Support setting multiple modes via the `-M` parameter:\n      - `disabledBackOption|disabledMultipleOption|notAllowDisableRules`\n      - `rulesOnly` and `pluginsOnly`\n8. feat: Internal Path Handling\n      - **Path Flexibility**: Internal paths support setting domain and port parameters, formats:\n      - `/...whistle-path.5b6af7b9884e1165...///__domain__port__/path/to`\n      - `/...whistle-path.5b6af7b9884e1165...///path/to?_whistleInternalHost_=__domain__port__`\n9. Memory and Connection Management\n      - **Connection Optimization**: Ensure timely closure of unused connections to reduce memory usage.\n      - **GC Optimization**: Adjusted GC parameters `--max-semi-space-size=64`.\n      - **Performance Monitoring**: Support obtaining the total number of currently processed requests via CGI or API.\n10. feat: Exception Handling\n        - **Plugin Exceptions**: Support obtaining exception information thrown by plugins via `process.on('pforkError')`.\n        - **Uncaught Exceptions**: Optimized handling of uncaught exceptions during request processing.\n        - **Timeout Handling**: Added `timeout` event to `onSocketEnd`, compatible with various abnormal situations.\n11. feat: Internal Connections\n        - **Connection Management**: Optimized internal connection management mechanism.\n12. feat: Filter Rules\n        - **Rule Fix**: Fixed result disorder when mixing `excludeFilter` and `includeFilter` configurations.\n        - **Domain Matching**: Domain wildcards now support obtaining submatch content.\n13. feat: Proxy Configuration\n        - **Proxy Connection Header**: Support changing the proxy forwarding header to `Proxy-Connection: close` via `disable://proxyConnection`.\n        - **Client Filtering**: Support filtering captured packet data via the `clientId` URL parameter.\n14. fix: Major Issue Fixes\n        - **Rule File Saving**: Fixed an issue where saving failed when rule file names were too long.\n        - **Editor Issue**: Fixed an issue with editor highlighting for plugin rules.\n        - **Management Interface Security**: Fixed an issue where CGI paths in the management interface could be arbitrarily concatenated.\n        - **Node Version Compatibility**: Fixed compatibility issues with specific Node versions (v15.5.0).\n        - **pipe Protocol**: Fixed issues that could lead to data loss.\n        - **Local File Replacement**: Response headers for local file replacements now include `content-length` by default.\n15. perf: Performance Issues\n        - **Freeze Issue**: Fixed potential freezes in some Node versions.\n        - **Connection Leakage**: Optimized connection management to reduce resource usage.\n16. feat: Response Header Handling\n        - **Content Length Control**: Local file replacements automatically add the `content-length` field, which can be disabled via `delete://resH.content-length`.\n17. Internal Request Handling\n        - **Async Handling**: Support listening for plugin process exceptions via `process.on('pforkError')`.\n\n## v2.5\n\n1. feat: Client Certificate Support\n      - **Custom Client Certificates**: Support configuring custom client certificates to enhance security authentication.\n      - **Connection Error Handling**: Optimized error handling mechanism when establishing connections.\n2. feat: Security Protocol Enhancements\n      - **Cipher Algorithm Extension**: Added `cipher` protocol to support custom fallback encryption algorithms.\n      - **Security Update**: Updated node-forge to address known security issues.\n      - **Plugin Security Control**: Startup parameters support controlling plugin loading via `allowPluginList` and `blockPluginList`.\n3. feat: Authentication Mode\n      - **Proxifier Mode**: Support `-M proxifier` to enable proxifier mode, automatically intercepting specific requests for certificate verification.\n4. feat: Tunnel Proxy Enhancement\n      - **Confirmation Mechanism**: Tunnel proxy supports a confirmation mechanism, improving proxy stability.\n      - **Connection Recovery**: Fixed an issue where HTTP requests going through tunnel proxy did not call `socket.resume()`.\n      - **Line-Level Proxy Configuration**: Added `lineProps://proxyHost|proxyTunnel` to apply only to the current line.\n5. feat: Multi-Layer Proxy Support\n      - **Two-Layer Proxy**: Added `enable://proxyTunnel` to support two-layer HTTP proxy.\n      - **Internal Request Support**: Internal path requests also support `enable://proxyTunnel`.\n      - **Protocol Conversion Fix**: Fixed an issue where `https2http-proxy` could not properly convert some requests.\n6. feat: WebSocket Handling\n      - **Auto Conversion**: WebSocket HTTPS requests configured in hosts support automatic conversion to HTTP.\n      - **Origin Fix**: Fixed an issue with automatic modification of WebSocket origin.\n      - **Data Retention**: Retain tunnel proxy request header data after intercepting HTTPS requests.\n7. feat: Plugin Management\n      - **Status Display**: Plugins disabled show a `Disabled` indicator on the page tab.\n      - **Command Customization**: Support customizing uninstall and install command names in the plugins list.\n      - **Rule Data Retrieval**: Plugins can obtain rule matching results via `req.originalReq.ruleUrl`.\n8. feat: Plugin Feature Enhancements\n      - **Trailer Support**: Plugins automatically add trailers, which can be disabled via `res.disableTrailer`.\n      - **Event Listening Optimization**: Optimized listening for `res.on('end', cb)` events to ensure triggering.\n9. feat: Data Deletion and Clearing\n      - **Content Deletion**: Support deleting request and response content via `delete://body`.\n      - **Selective Deletion**: Support `delete://req.body` and `delete://res.body` for separate deletion.\n      - **Content Clearing**: Support clearing request or response content via `reqBody://()` or `resBody://()`.\n10. feat: Trailer Operations\n      - **Trailer Passing**: Support passing `trailers`.\n      - **Trailer Deletion**: Support deleting specified trailers via `delete://trailer.xxx|trailer.yyy`.\n      - **Trailer Modification**: Support modification via `headerReplace://trailer.key:pattern=value` and `trailers://json`.\n11. feat: Protocol Optimization\n      - **SNI Control**: `enable://servername` removes SNI from HTTPS requests.\n      - **StatusCode Adjustment**: `statusCode` moved into `rule` at the same level as `file` and other protocols.\n      - **CORS Optimization**: `resCors://enable` automatically sets CORS fields for OPTIONS requests.\n12. feat: Interface Layout\n      - **Left Menu Control**: Support hiding the left menu via the `hideLeftMenu=true` request parameter.\n      - **Rule Toggle**: Left menu adds checkboxes to quickly disable or enable Rules/Plugins.\n      - **System Hosts**: Removed the option to sync system hosts.\n13. feat: Data Display\n      - **Time Precision**: Page time now supports displaying milliseconds.\n      - **Encoding Display**: Fixed incorrect Content Encoding display on pages.\n      - **Response Header Size**: Fixed header size display issues when h2 requests are converted to https requests in the UI.\n14. feat: Shortcut Optimization\n      - **General Replay**: Added shortcuts `ctrl[cmd] + r` or `ctrl[cmd] + shift + r` to replay requests.\n      - **Frames Replay**: Frames support shortcut `Ctrl/Cmd + R` to replay requests.\n15. feat: Interaction Enhancement\n      - **Request Header Display**: Bold whistle custom request headers in Composer.\n      - **Network Font**: Adjusted bold effect of Network font.\n      - **Data Marking**: Network right-click menu added `Actions>Mark` to mark captured packet data.\n16. feat: Command Line Tools\n      - **Docker Deployment**: Support `w2 run -M prod` for convenient Docker deployment.\n      - **Multi-Rule Mode**: Support enabling multi-select via `-M useMultipleRules`.\n      - **Plugin Path**: Fixed an issue where `--addon \"path1,path2\"` could not accept multiple paths.\n17. feat: Development Configuration\n      - **JSON5 Configuration**: Support json5 configuration files.\n      - **Node Requirement**: Node13~14 for HTTP2 functionality, minimum Node version requirement changed to 6.\n      - **Template Variables**: Template strings support obtaining system `os.hostname()` via `${hostname}`.\n18. feat: HTTP2 Support\n      - **HTTP2 Compatibility**: Fixed an issue where HTTP2 could not be used with `Node >= 14.1`.\n      - **Protocol Detection**: Automatic detection of whether request or response content supports gzip.\n19. feat: Data Compression\n      - **Gzip Optimization**: CGI returning captured packet data now uses gzip.\n      - **Performance Hint**: Composer input text length now prevents browser freezes.\n20. feat: Filter Rules\n      - **Rule Fix**: Restored matching order, fixing rule overriding issues.\n      - **Response Header Filtering**: Fixed an issue where `includeFilter://h:key=pattern` could not match response headers.\n21. fix: Issue Fixes\n      - **Proxy Request Headers**: Fixed disorder in proxy request headers `Host`.\n      - **Rule Conflict**: Fixed an issue where `reqHeaders://cookie=xxx` and `reqCookies://test=123` could not take effect simultaneously.\n      - **Path Handling**: Fixed issues with modifying request URL parameters via `urlParams` and `pathReplace`.\n      - **Composer Construction**: Fixed an issue where Composer constructing requests without a body did not set `content-length: 0`.\n22. fix: Interface Fixes\n      - **Overview Display**: Fixed script errors in the Overview interface when requests contained matching plugin rules.\n      - **Plugin Sorting**: Fixed sorting jumps that could occur with simultaneously installed plugins.\n      - **Frames Display**: Binary data font bolded in Frames page.\n23. feat: Feature Enhancement\n      - **JSON Operations**: JSON Tree supports copying child node data; JSONView right-click menu added `Collapse Parent`.\n      - **Data Copy**: Support copying request headers as JSON text.\n      - **WebSocket Status**: Support displaying WebSocket close error codes.\n24. feat: Search and Help\n      - **Search History**: Network search box added history function.\n      - **Rule Help**: Overview rule list hover allows clicking to view help documentation.\n      - **Plugin Installation**: Plugins added `ReinstallAll` button to copy plugin installation commands.\n25. feat: File Handling\n      - **Composer File Upload**: Support uploading local files.\n      - **Proxy Standards**: Fixed pending issues caused by some services not following HTTP standards.\n26. feat: Interface Access\n      - **Guest Mode**: Added interfaces accessible in guest mode.\n      - **Interface Security**: Reduced exposure of interfaces without login state.\n\n## v2.4\n\n1. feat: Mutual TLS Authentication Support\n      - **Client-Server Mutual Authentication**: Support custom client certificates for mutual TLS authentication.\n      - **Safe Mode**: Added startup parameter `-M safe` to enable safe mode with strict server certificate verification.\n      - **Certificate Management**: `HTTPS > View all custom certificates` highlights expired certificates and supports copying installation paths.\n2. feat: Certificate and Host Configuration\n      - **Local Host Configuration**: Fixed an issue where HTTPS requests might fail if the local hosts file lacked `127.0.0.1 localhost`.\n      - **Express Framework Fix**: Fixed an issue with the `x-powered-by` response header added by default by Express.\n3. feat: HTTP2 Optimization\n      - **Node Version Requirement**: Due to numerous bugs in the HTTP/2 module of lower Node versions, unified adjustment requires Node v12.12.0 or above for HTTP/2 support.\n      - **HTTP2 Session Error**: Fixed potential `ERR_HTTP2_SESSION_ERROR` on some websites.\n      - **DELETE Request Optimization**: HTTP2 `DELETE` requests with content automatically downgrade to HTTP/1.1.\n      - **HTTP/2 Compatibility**: HTTP/2 supports DELETE requests with body.\n3. feat: Proxy Feature Extensions\n      - **Internal HTTP Proxy**: Added `internal-http-proxy` protocol, similar to `internal-proxy` but uses tunnel proxy for WebSocket requests.\n      - **SOCKS Proxy Stability**: Fixed a crash issue when enabling `--socksServer port` and request exceptions occur.\n      - **HTTPS Downgrade**: Support automatic downgrade of HTTPS requests with content (like post) to HTTP requests.\n4. feat: Protocol Processing Improvements\n      - **pipe Protocol Fix**: Fixed issues where request exceptions in `pipe` were not caught and HTTP request pipe failure.\n      - **Rule Parsing Enhancement**: Support parsing pipe rules from request headers.\n5. feat: Network Customization\n      - **Custom Columns**: Support custom column display in Network via `style` protocol configuration.\n      - **Frames Page Enhancement**:\n        - Added Overview Tab for viewing basic frame data information.\n        - Binary data font bolded.\n      - **Search Filtering**: Fixed duplicate data issues in Network search filtering.\n6. feat: Data Display Optimization\n      - **Composer Response Data**: Response data passed to Composer changed to base64 format.\n      - **Saz File Support**: Support displaying non-text content in saz files.\n      - **Overview Panel**: Display matching `includeFilter` in Overview.\n      - **Time Display**: Support displaying time consumed by HTTPS auto-conversion to HTTP in Overview.\n7. feat: Rule Matching Display\n      - **Filter Rule Display**: Display matching `includeFilter` rules in the Overview panel.\n8. feat: Filter Rule Optimization\n      - **includeFilter Fix**: Fixed an issue where `includeFilter://b:pattern` failed.\n      - **Matching Logic Adjustment**: Adjusted `includeFilter` and `excludeFilter` matching:\n        - Must satisfy one of all `includeFilter`.\n        - Must not match any `excludeFilter`.\n        - i.e., `excludeFilter://p1 excludeFilter://p2 includeFilter://p3 includeFilter://p4` is equivalent to `!(p1 || p2) && (p3 || p4)`.\n9. feat: Remote Rule Management\n      - **Update Mechanism Optimization**: Optimized remote rule update mechanism to prevent misjudgment of pull failure leading to rule clearance.\n      - **Rule Conflict Fix**: Fixed an issue where setting `reqBody://(xxxx) method://post` could not take effect simultaneously.\n10. feat: Startup Parameter Enhancement\n      - **Version Notification Control**: Support disabling version update notifications via `-M disableUpdateTips` (for third-party integrations).\n      - **Installation Process Optimization**: Removed warning prompts during installation.\n      - **Configuration Fix**: Fixed host configuration errors introduced in the previous version.\n11. feat: Network Configuration\n      - **IPv6 Optimization**: Optimized IPv6 configuration.\n      - **Interface Streamlining**: Removed redundant interfaces.\n12. fix: Issue Fixes\n      - **Host Configuration**: Fixed HTTPS failures due to local hosts file configuration issues.\n      - **Express Response Header**: Fixed the `x-powered-by` response header added by default by Express.\n      - **HTTP2 Error**: Fixed potential `ERR_HTTP2_SESSION_ERROR` on some websites.\n      - **Rule Matching**: Fixed `includeFilter://b:pattern` failure.\n      - **Proxy Exception**: Fixed crashes when enabling `--socksServer` and request exceptions occur.\n      - **Rule Conflict**: Fixed simultaneous effect issue with `reqBody://(xxxx) method://post`.\n\n## v2.3\n\n1. feat: Significant List Performance Improvement\n      - **Virtual List Technology**: Introduced `react-virtualized` library, greatly improving Network list rendering performance.\n      - **Default Data Volume**: Default support for displaying 1500 captured packet items simultaneously.\n      - **Custom Quantity**: Adjustable via `Network > Filter > Max Rows Number`.\n      - **Memory Management**: Limited zlib concurrency to reduce memory leak risk.\n2. feat: Dependency Management and Build\n      - **Lock Dependency Versions**: Added `package-lock.json` to ensure dependency consistency.\n      - **Build Stability**: Improved project build and deployment stability.\n3. feat: Request Header Security Control\n      - **x-forwarded-for Fix**: Fixed `x-forwarded-for` chaos issues.\n      - **Custom Forwarding Headers**: Support custom forwarding addresses via `forwardedFor://ip` or `reqHeaders://x-forwarded-for=ip`.\n      - **Proxy Forwarding Logic**: Direct requests do not carry `x-forwarded-for` by default; proxy forwarding automatically adds non-local IPs.\n4. feat: HTTPS and Certificate Processing\n      - **Non-SNI Request Support**: Fixed issues where non-SNI HTTPS requests could not be unpacked.\n      - **HTTP2 Compatibility**: Fixed overly strict format requirements for request/response in HTTP2 module.\n5. feat: Content Injection Security\n      - **Strict HTML Check**: `enable://strictHtml` ensures injection only when the first non-whitespace character is `<`.\n      - **Safe HTML Check**: `enable://safeHtml` prevents accidental injection into non-standard interfaces (checks not starting with `{{`).\n      - **Mis-injection Protection**: Unified script injection for domains prevents accidental injection into non-HTML response types.\n6. feat: Plugin Development Experience\n      - **Runtime Identifier**: `@url` requests automatically carry `x-whistle-runtime-id`, helping plugins identify request source.\n      - **Plugin Status Query**: Added `options.isEnable()` method to get plugin enable status.\n      - **Template Variable Support**: Plugin configuration files support placeholders:\n        - `{{whistlePluginName}}` - Gets plugin short name (excluding `whistle.`).\n        - `{{whistlePluginPackage.xx.yy.zzz}}` - Gets values from plugin `package.json`.\n      - **Plugin Sorting**: Fixed sorting jumps that could occur with simultaneously installed plugins.\n7. feat: WebSocket Handling\n      - **Status Code Passing**: When WebSocket returns a non-101 status, it's passed to the browser.\n      - **Proxy Client IP**: Fixed an issue where WebSocket using `internal-proxy` could not accurately carry clientIp.\n8. Proxy and Connection Management\n      - **Proxy Retry Fix**: Fixed duplicate path addition after setting proxy retry.\n      - **Socket Error Handling**: Force-set empty errorHandler for all sockets to prevent uncaught exceptions.\n      - **Compression Control**: Support disabling `gzip` for all requests via startup parameter `-M noGzip`.\n9. Cache Policy Adjustment\n      - **Cache Priority**: `cache://xxx` now has the highest priority.\n      - **Response Header Retention**: Support forced retention of original request headers via `cache://reserve`.\n      - **Injection and Cache Coordination**: Fixed conflicts between `log://xxx`/`weinre://xxx` injection and caching.\n10. feat: Network Interface Optimization\n      - **Column Width Adjustment**: Network `URL` column supports width modification.\n      - **Layout Improvement**: Optimized interface layout and interaction.\n\n## v2.2\n\n1. feat: Global HTTP2 Switch\n      - **Interface Control**: Support enabling/disabling HTTP2 requests via `Network -> HTTPS -> Enable HTTP/2`.\n      - **Local Enablement**: Can enable HTTP2 for specific requests locally via `pattern enable://h2`.\n      - **Mode Switching**: After deselecting HTTP2, both receiving and sending requests switch to non-HTTP2.\n2. feat: HTTP2 Performance Optimization\n      - **Session Caching**: Optimized H2 session caching strategy, improving connection reuse efficiency.\n      - **Node.js Compatibility**: Fixed Node.js version compatibility issues (issue #27384).\n      - **Timeout Setting Optimization**: Removed request timeout settings, improved connection management.\n3. feat: Command Line Rule Configuration\n      - **Remote Rule Loading**: Support loading rules from remote URLs via `w2 start -r \"@https://xxx\"`.\n      - **Local Rule File**: Support loading rules from local files via `w2 start -r \"@filepath\"`.\n      - **Direct Rule Setting**: Support direct rule setting via `w2 start -r \"www.test.com/path/to reqHeaders://x-test=1\"`.\n      - **Combined Configuration**: Support JSON.stringify format for combining multiple rule sources.\n4. feat: Rule Execution Optimization\n      - **Response Phase Rules**: Some rules executed only in the response phase are matched after request response.\n      - **Performance Improvement**: Optimized rule matching timing, improving execution efficiency.\n5. feat: HAR File Support\n      - **Encoding Detection**: Support automatic detection of whether `xxx.har` files use base64 encoding.\n      - **Data Compatibility**: Improved HAR file import compatibility and accuracy.\n6. feat: Internal Request Handling\n      - **Retry Mechanism Fix**: Fixed potential infinite loops due to internal request retries.\n      - **Connection Stability**: Improved internal request handling logic to avoid infinite retries.\n8. feat: Overall Optimization\n      - **Code Refactoring**: Optimized H2 session management logic.\n      - **Configuration Simplification**: Removed unnecessary request timeout settings.\n\n## v2.1\n1. feat: JSON Object Handling\n      - **Array and Nested Support**: Multi-line JSON objects now support setting array and multi-level nested values.\n      - **Smart Conversion Rules**:\n        - Previous version: `[1]: \"test\"` converted to `{ \"[1]\": \"test\" }`\n        - Current version: `[1]: \"test\"` converted to `var arr = []; arr[1] = 'test';`\n      - **Complex Structure Support**: Support for complex nested structures like `[a.b[3].c]: abc`.\n      - **Escape Control**: Using double quotes `\"[xxx.yyy[n]]\"` avoids automatic escaping.\n2. feat: Domain Matching Extension\n      - **Flexible Matching Syntax**:\n        - `.test.com` - Matches `x.test.com` and `test.com`\n        - `*.test.com` - Matches `x.test.com`, not `test.com`\n        - `**.test.com` - Matches `x.test.com` and all its descendant domains\n        - `***.test.com` - Matches `test.com` and all its descendant domains\n      - **Precise Control**: Support adding protocols and paths for more precise matching, e.g., `http://.test.com/path/to/*`\n3. feat: Custom Method Support\n      - **Method Extension**: Composer interface supports custom request methods.\n      - **Flexible Construction**: Users can construct and send various types of requests more flexibly.\n4. feat: HTTP2 Performance Optimization\n      - **Session Reuse**: Reduced HTTP2 session count; each client can fully reuse HTTP2 sessions.\n      - **Connection Efficiency**: Improved HTTP2 connection reuse efficiency, reducing resource usage.\n5. feat: Plugin Development Experience\n      - **Hint Optimization**: Fixed an issue where custom plugin hints with only one completion data wouldn't display.\n      - **Environment Variable Support**: Support setting Node path for forked plugin processes via environment variable `env.WHISTLE_PLUGIN_EXEC_PATH` or startup parameter `-M buildIn`.\n      - **Path Control**: Defaults to global Node; specific Node paths can be specified as needed.\n6. feat: Overview Panel Enhancement\n      - **Compression Information Display**: Overview panel supports displaying request size before and after gzip.\n      - **Data Size Display**: Fixed an issue where empty request/response content wouldn't show size.\n7. fix: URL Replacement Fix\n      - **Path Handling**: Fixed incorrect path handling in URL replacement `url replacementUrl`.\n      - **Replacement Accuracy**: Ensure URL replacement operations accurately handle path parameters.\n8. perf: Performance Optimization\n      - **HTTP2 Connection**: Optimized HTTP2 session management, reducing connection creation overhead.\n      - **Data Display**: Improved data display logic for empty content.\n\n## v2.0.0\n1. feat: **Support for HTTP2 Functionality**\n\t> Please ensure the Node version is [the latest LTS (>= 10.16.0) or Stable (>= 12.12.0) version](https://nodejs.org/en/), otherwise anomalies may occur, such as: [#24037](https://github.com/nodejs/node/issues/24037), [#24470](https://github.com/nodejs/node/issues/24470)\n2. feat: `**/path/to` If `path/to` contains `*`, e.g., `*/cgi-*`, it is equivalent to `^*/cgi-*`.\n\n## v1.17\n1. feat: Support establishing connections between browser and whistle via HTTP2 (**requires updating [Node](https://nodejs.org) to version `v10.16.0` or above**)\n2. refactor: Adjusted connection caching strategy; no long caching for any connections to reduce memory usage.\n\n## v1.16\n1. feat: Support plugins obtaining plugin Rules, Values, and custom certificate information via `options.getRules(cb), options.getValues(cb), options.getCustomCertsInfo(cb)` respectively.\n2. feat: HTTPS menu dialog added `View Custom Certs` button for managing custom certificates.\n3. feat: Support enabling debugging mode via `w2 run --inspect` or `w2 run --inspectBrk`.\n4. feat: Plugin list added `Sync` button to fetch plugin rules or values and set them in the UI Rules or Values.\n5. feat: Support custom auto-completion list functionality via plugin package.json configuration, e.g., `\"whistleConfig: { \"hintUrl\": \"/cgi-xxx/xxx\" }\"`.\n6. feat: Support data transfer between plugin server hooks via `req.sessionStorage.[get|set|remove]`.\n\n## v1.15\n1. feat: Support starting a SOCKS v5 service on a specified port via command line parameter `--socksPort 1080` (currently only supports regular TCP requests).\n2. feat: Plugin server added `req.passThrough()`, `req.request(url/options, cb)`, `req.writeHead(code, message, headers)` methods for forwarding requests from plugins to specified services.\n3. feat: Support a no-capture-page mode via command line parameter `-M rules`, where the UI won't show Network, cannot capture packets, and plugins cannot obtain captured packet data via `req.getSession(cb)`.\n4. feat: Support setting request font color/style/background color via `style://color=!xxx&fontStyle=xxxxx&bgColor=red`.\n5. feat: Support adjusting proxy configuration priority above host via `enable://proxyFirst` (default: host > proxy).\n6. fix: Fixed various issues encountered during operation.\n\n## v1.14\n1. feat: Added new protocols [pipe](https://wproxy.org/docs/rules/pipe.html), [headerReplace](https://wproxy.org/docs/rules/headerReplace.html).\n2. fix: Fixed `querystring.parse('+')` automatically converting to space ' ' or `%2B` issue.\n3. refactor: Optimized startup parameter [--max-http-header-size=size](https://nodejs.org/dist/latest-v10/docs/api/cli.html#cli_max_http_header_size_size).\n4. feat: Added command line parameters `--httpPort` and `--httpsPort` for starting regular HTTP and HTTPS servers respectively, useful for reverse proxying, and can also start another `http proxy` (same function as default HTTP proxy) and `https proxy` (can serve as an HTTPS proxy server).\n5. fix: Fixed various issues encountered during operation.\n\n## v1.13\n1. feat: Enhanced plugin functionality, supporting `resRules.txt`.\n2. feat: Added new protocols [excludeFilter](http://wproxy.org/docs/rules/excludeFilter.html) and [includeFilter](http://wproxy.org/docs/rules/includeFilter.html).\n3. feat: [log](http://wproxy.org/docs/rules/log.html) supports injecting `whistle.onWhistleLogSend(level, logStr)` to obtain page log information for custom reporting.\n4. feat: Plugins support custom interface URL via package.json configuration `\"pluginHomepage\": \"http://xxx.xxx.com/\"`.\n5. feat: Local replacement added response `206` functionality, supporting iOS playback of locally replaced video files.\n6. feat: Command line added `--no-prev-options` startup option, supporting `w2 restart` without reusing previous options.\n\n## v1.12\n1. feat: Support adjusting rule priority order in `Rules > Settings > The later rules first`. Default is top-to-bottom, with rules in Default having the lowest priority. This setting only applies to rules configured in Rules, not to [reqScript](https://wproxy.org/docs/rules/reqScript.html) or plugin-set rules.\n2. feat: Added new protocol [https-proxy](https://wproxy.org/docs/rules/https-proxy.html).\n3. feat: [resCookies](https://wproxy.org/docs/rules/resCookies.html) supports setting [SameSite](https://www.owasp.org/index.php/SameSite).\n4. feat: Support loading remote rules via configuration `@url` or `@filepath` or `@whistle.xxx/path` in Rules.\n5. feat: Support setting `shadowRules` via command line `-r, --shadowRules [shadowRules]`.\n6. feat: Support embedded Values.\n7. feat: Template strings support `replace`, and support submatching.\n    ```\n    pattern protocol://`${search.replace(pattern1,replacment)}`\n    www.test.com file://`${search.replace(/Course_(id)\\,?/ig,$1cid)}${test.html}`\n    ```\n    `pattern1` is a regex or regular string (no quotes needed).\n\n## v1.11\n1. feat: HTTPS request auto-downgrade ([https://github.com/avwo/whistle/issues/176](https://github.com/avwo/whistle/issues/176)).\n2. feat: Support displaying HexView (binary).\n3. feat: Added new protocol [xhost](https://wproxy.org/docs/rules/xhost.html).\n4. feat: Adjusted strategy; some protocols support multiple simultaneous matches.\n5. fix: Fixed various issues encountered during operation.\n\n## v1.10\n1. feat: Enhanced interface functionality.\n2. feat: Support port matching `:12345 operation`.\n3. feat: Support setting multiple domains for accessing webui `-l \"webui1.example.com|webui2.example.com\"`.\n4. feat: Support obtaining rule configuration output by `.whistle.js` in the current directory via command line `w2 add`. See: [Command Line Parameters](https://wproxy.org/docs/cli.html).\n5. feat: Display current whistle running status via `w2 status [-S storage]` or `w2 --all`.\n\n## v1.9\n1. feat: Support setting to capture-only mode via command line `-M network`. In this mode, only packet capture can be viewed; rules cannot be set nor plugins loaded.\n2. feat: Support customizing domains for accessing plugins via command line `-L \"script=a.b.com&vase=x.y.com&nohost=imweb.nohost.pro\"`.\n3. feat: Composer supports setting Rules.\n\n## v1.8\n1. feat: Added new protocols [htmlPrepend](https://wproxy.org/docs/rules/htmlPrepend.html), [htmlBody](https://wproxy.org/docs/rules/htmlBody.html), [htmlAppend](https://wproxy.org/docs/rules/htmlAppend.html), [cssPrepend](https://wproxy.org/docs/rules/cssPrepend.html), [cssBody](https://wproxy.org/docs/rules/cssBody.html), [cssAppend](https://wproxy.org/docs/rules/cssAppend.html), [jsPrepend](https://wproxy.org/docs/rules/jsPrepend.html), [jsBody](https://wproxy.org/docs/rules/jsBody.html), [jsAppend](https://wproxy.org/docs/rules/jsAppend.html).\n2. feat: Support wildcard matching.\n3. feat: Support `Copy As CURL`.\n4. feat: Support importing HAR files.\n5. feat: Support third-party extensions for the `@` symbol function in Rules.\n\n## v1.7\n1. feat: Added new protocols [resScript](https://wproxy.org/docs/rules/resScript.html), [responseFor](https://wproxy.org/docs/rules/responseFor.html), [resforwardedForScript](https://wproxy.org/docs/rules/forwardedFor.html).\n2. fix: Fixed various issues encountered during operation.\n\n## v1.6\n1. feat: Support WebSocket request mapping, `ws://www.test.com/xxx https://www.abc.com/a/b`.\n2. feat: Adjusted certificate strategy to prevent invalid characters in domains causing Chrome certificate validation failures.\n3. fix: Fixed various issues encountered during operation.\n\n## v1.5\n1. feat: Composer supports constructing WebSocket and TCP requests.\n2. feat: Support custom Whistle management interface port via command line parameter `-P uiPort`.\n3. feat: Enhanced plugin functionality, supporting setting plugin-private Values via root directory file `_values.txt` (does not support `values.txt`), matching private rules `_rules.txt`.\n4. feat: Interface supports left menu mode and displays client and server port numbers.\n5. fix: Fixed various issues encountered during operation.\n\n## v1.4\n1. feat: Support modifying the root path of Whistle storage directory via command line parameters `-D, -baseDir`.\n2. perf: Optimized performance of `os.networkInterfaces`.\n3. fix: Fixed various issues encountered during operation.\n\n## v1.3\n1. feat: Pattern added support for schema-less mode `//xxx`.\n2. fix: Fixed various issues encountered during operation.\n\n## v1.2\n1. feat: Added new protocols [reqScript](https://wproxy.org/docs/rules/reqScript.html), [ignore](https://wproxy.org/docs/rules/ignore.html), [enable](https://wproxy.org/docs/rules/enable.html).\n2. feat: Enhanced plugin functionality, added `statsServer`.\n3. feat: Added short link for downloading root certificate `http://rootca.pro`.\n\n## v1.1\n1. feat: Added new protocols [pac](https://wproxy.org/docs/rules/pac.html), [delete](https://wproxy.org/docs/rules/delete.html), [reqHeaders](https://wproxy.org/docs/rules/reqHeaders.html), [resHeaders](https://wproxy.org/docs/rules/resHeaders.html).\n2. feat: Enhanced plugin functionality.\n3. fix: Fixed various issues encountered during operation.\n\n## v1.0\n1. feat: Enhanced plugin functionality, added `tunnelServer`, support for new protocol `whistle.xxx://`.\n2. feat: Enhanced `socks`, `proxy` protocol functionality.\n3. feat: Added command line parameter `-l, --localUIHost` to modify the domain for accessing the configuration page, default is `local.whistlejs.com`.\n4. feat: Proxy requests added `x-whistle-policy` for setting Whistle policies.\n5. fix: Fixed various issues encountered during operation.\n6. feat: Replaced with a new logo, thanks to our department's visual designer **[@wjdgh1031(鬼刀)](https://github.com/wjdgh1031)** for designing the new logo.\n7. feat: Enhanced protocol functionality [pathReplace](https://wproxy.org/docs/rules/pathReplace.html), [log](https://wproxy.org/docs/rules/log.html), [replaceStatus](https://wproxy.org/docs/rules/replaceStatus.html), [rawfile](https://wproxy.org/docs/rules/rawfile.html), [xrawfile](https://wproxy.org/docs/rules/xrawfile.html), [reqAppend](https://wproxy.org/docs/rules/reqAppend.html), [resAppend](https://wproxy.org/docs/rules/resAppend.html), [reqType](https://wproxy.org/docs/rules/reqType.html), [resType](https://wproxy.org/docs/rules/resType.html), [reqCharset](https://wproxy.org/docs/rules/reqCharset.html), [ua](https://wproxy.org/docs/rules/ua.html), [reqWriter](https://wproxy.org/docs/rules/reqWriter.html), [reqWriterRaw](https://wproxy.org/docs/rules/reqWriterRaw.html), [reqReplace](https://wproxy.org/docs/rules/reqReplace.html), [resReplace](https://wproxy.org/docs/rules/resReplace.html), etc.\n8. feat: Support exporting captured packet data.\n9. feat: Support starting multiple instances `w2 start -S newStorageDir -p newPort`.\n10. feat: Support custom plugins.\n11. fix: Fixed various issues encountered during operation.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "中文 · [English](./CHANGELOG-en_US.md)\n\n## v2.10.1\n1. fix: https://github.com/avwo/whistle/issues/1296\n2. fix: https://github.com/avwo/whistle/issues/1297\n2. feat: 支持快速筛选出匹配规则的请求\n\n## v2.10.0\n1. feat: Inspectors 的 Raw Tab 放到第一个位置\n2. feat: 映射到本地的路径不允许包含 `../` 路径片段\n3. feat: 允许禁用或开启自定义证书，无需删除\n4. feat: 插件变量 `%plugin-name=xxx` 支持所有插件 hooks\n5. refactor: 简化 Whistle 与插件间的代码传递方式，提升效率\n6. fix: 完善测试用例，修复隐藏 bug\n\n## v2.9\n1. feat: 界面与体验优化：\n      - 根据 Dark 模式规范重新优化界面 Dark 模式\n      - Network 支持通过类型快速过滤、手动保存抓包数据\n      - Composer 支持导入 CURL、显示 SSE/Websocket 内容\n      - JSON Viewer 右键支持复制选中文本\n      - 支持显示 TTFB、SSE 内容、WebSocket Frames\n      - 新增 frameScript 用于通过 JS 修改 WebSocket/TCP 请求内容\n2. feat: 规则与配置增强：\n     - 支持通过 `~/.whistlerc` 加载默认配置\n     - Rules 支持查看 `Enabled Rules`、分组功能\n     - 支持设置匹配概率 `includeFilter://chance:0.5`\n     - 新增 `delete://` 协议支持删除请求参数、cookie等\n     - 支持 `req.passThrough({transformReq, transformRes, ...})`\n     - 内嵌值添加作用域（Rules、插件、Header Rules 相互独立）\n3. feat: 插件系统改进：\n     - 支持通过界面安装/卸载插件\n     - 插件列表添加自定义右键菜单\n     - 插件支持 `sharedStorage` 在不同实例中共享数据\n     - 插件 Option 页面支持设置 favicon、以对话框形式打开\n     - 支持通过插件安装插件\n4. feat: 网络与代理功能：\n     - 默认启用 `localhostCompatible` 模式\n     - 支持 IPv6-only 网络\n     - 支持 socks 代理、自定义 DNS server\n     - 新增 `https-proxy`、`internal-http-proxy` 协议\n     - 支持 HTTP2 功能（需 Node 10.16.0+）\n5. feat: 证书与安全：\n     - 证书默认格式改为 cer 适配更多机型\n     - 支持自定义客户端证书和双向认证\n     - 根证书过期后可通过 `w2 ca` 更新\n\n## v2.8\n1. feat: 多进程支持\n      - 新增 `--cluster [workers]` 启动模式，支持多进程运行\n      - 修复启动时绑定非本地网卡时插件远程规则访问失败问题\n2. feat: 证书管理优化\n      - 自动生成的证书过期时自动续期（有效期一年）\n      - 禁止通过页面上传根证书文件（`root.key & root.crt`）\n      - 支持非 SNI 请求通过插件自定义证书\n      - 支持直接上传和删除用户自定义证书\n3. feat: 插件系统增强\n      - 插件 `server` 钩子支持通过 `req.setReqRules` 和 `req.setResRules` 设置动态规则\n      - 插件可获取客户端连接信息（`originalReq.remoteAddress` 与 `originalReq.remotePort`）\n      - 修复插件 `sniCallback` 返回 `false` 时请求未重新走 TUNNEL 代理的问题\n      - 优化插件规则执行：req 和 res rules 分开执行\n4. feat: 代理规则增强\n      - 支持通过 `ignore://-*` 过滤 `ignore://*`\n      - 支持 `proxy` 和 `pac` 配置 `lineProps://proxyHostOnly`，仅当用户配置了 `host` 时代理生效\n      - `--httpsPort` 启动的 HTTPS Server 支持从插件获取证书\n5. feat: 过滤规则扩展\n      - 支持通过 `excludeFilter://from=httpServer`、`includeFilter://from=httpsServer` 等按来源过滤请求\n      - 支持通过 `enable://useLocalHost` 和 `enable://useSafePort` 修改 log 和 weinre 请求的域名或端口\n6. feat: 请求头控制\n      - 默认不启用 `x-forwarded-host` 和 `x-forwarded-proto`，可通过以下方式启用：\n      - 启动参数 `-M x-forwarded-host|x-forwarded-proto`\n      - 请求头 `x-whistle-forwarded-props: host,proto,for,clientIp,ip`\n7. feat: 自定义界面\n      - 支持自定义 `inspectors tab`\n      - 界面提供 `api.selectIndex` 方法选中指定下标的抓包数据\n8. feat: 数据限制与优化\n      - 默认显示的抓包数据不超过 1.5MB，可通过 `enable://bigData` 扩大到 2.5MB\n      - 支持通过请求头设置响应规则\n      - 优化获取证书逻辑，合并多次相同请求\n9. 文件路径处理\n      - `reqWrite:///path/to/` 和 `reqWrite:///path/to` 区别处理，前者自动补全为 `index.html`\n10. 合并操作增强\n      - `resMerge://json1 resMerge://json2` 默认使用浅合并，可通过 `resMerge://true` 启用深合并\n11. feat: 强制写入支持\n      - 支持通过 `enable://forceReqWrite` 和 `enable://forceResWrite` 强制 `reqWrite`、`reqWriteRaw` 和 `resWrite`、`resWriteRaw`\n12. feat: 认证钩子优化\n     - 插件的 auth hook 默认对解析后的 HTTPS 请求生效\n     - 支持通过 `enable://authCapture` 使 auth hook 对隧道代理请求生效\n13. fix: 问题修复\n     - 修复可能无法导入 saz 文件的问题\n     - 修复 `sniCallback` 内存泄露问题\n     - 处理 `unhandledRejection` 事件\n14. refactor: 代码优化\n     - 插件接收到 HTTPS 请求时，`req.url` 将包含完整路径\n     - 优化获取证书逻辑，减少重复请求\n     - 支持通过 `disable://abort` 禁用 `enable://abort`\n\n## v2.7\n\n1. feat: HTTP2 功能扩展\n      - **HTTP2 非 HTTPS 支持**：HTTP2 协议现在支持非 HTTPS 请求\n      - **性能监控**：第三方集成可通过 `proxy.on('perfDataChange')` 获取 CPU、内存、请求量等运行数据\n2. feat: 插件系统全面增强\n      - **异步支持**：插件 hook 现在支持 `async-await` 语法\n      - **认证集成**：插件 `whistleConfig` 支持配置 `inheritAuth` 复用 Whistle 登录账号\n      - **插件管理**：支持在插件根目录执行 `w2 run` 时自动加载该插件\n      - **配置共享**：插件支持通过 `options.getCert()` 获取指定域名证书\n      - **依赖引用**：插件可通过 `options.require` 直接引用 Whistle 内部模块\n3. feat: 命令行工具改进\n      - **配置管理**：支持通过 `--config localFile` 加载启动配置（优先级高于命令行）\n      - **实例管理**：优化 `w2 stop` 命令，找不到实例时显示所有运行实例\n      - **Docker 支持**：源码目录添加 Dockerfile\n4. feat: DNS 解析增强\n      - **自定义 DNS**：支持通过 `--dnsServer` 参数自定义 DNS 服务器\n      - **DNS over HTTPS**：支持类似 `http://dns.alidns.com/resolve` 的 DNS-over-HTTPS 服务\n      - **IPv6 支持**：可手动指定 IPv6 DNS 服务器地址\n5. feat: 代理协议优化\n      - **PAC 认证**：`pac` 协议支持设置用户名密码：`pac://user:pass@pacPath`\n      - **隧道代理**：支持插件设置 `tunnelKey` 传递隧道代理请求头\n      - **代理转发**：优化 `lineProps://proxyHost|proxyTunnel|proxyFirst` 配置\n6. feat: 请求头处理\n      - **转发控制**：支持通过 `-M disableForwardedHost` 和 `-M disableForwardedProto` 禁用特定转发头\n      - **自定义解析**：`pipe://xxx` 支持插件内部获取原始规则值\n7. feat: Network 视图升级\n      - **Tree View 支持**：Network 支持树形视图展示抓包数据\n      - **原始 URL 显示**：支持显示原始 URL（Raw Url）\n      - **数据量显示**：Network 的 Body 列显示请求内容大小\n      - **列表优化**：修复 Tree View 和 List View 的数据更新和排序问题\n8. feat: 界面交互改进\n      - **Composer 历史**：优化 Composer 历史记录列表显示\n      - **左侧菜单**：优化左侧菜单布局和交互\n      - **状态提示**：禁用 Rules 和 Plugins 时显示更明显的提醒\n      - **快捷键支持**：Values 右键菜单修复快捷键问题\n9. feat: 规则编辑器增强\n      - **规则排序**：支持将 Rules 添加到最前面\n      - **错误处理**：修复 Rules 编辑器输入 `!` 时报错的问题\n      - **智能提示**：`pipe` 协议支持智能提示功能\n10. feat: 认证功能扩展\n        - **插件认证**：插件 `auth` 方法支持 `req.setRedirect(url)` 重定向\n        - **登录框支持**：插件 `auth` 支持设置 `req.showLoginBox` 弹出登录框\n        - **内部请求**：插件 `auth` 方法现在支持处理 Whistle 内部请求\n11. feat: 证书管理\n        - **根证书管理**：支持显示自定义根证书及删除导引\n        - **远程证书**：支持通过插件 `sniCallback(req, options)` hook 获取远程证书\n12. feat: 自定义解析器\n        - **HTTP 解析**：普通 HTTP 请求现在也支持 `customParser`（或 `customFrames`）\n        - **数据替换**：修复 `reqReplace` 及 `resReplace` 因拆包导致的匹配不准确问题\n13. feat: 协议支持扩展\n        - **状态码替换**：WebSocket 和 Tunnel 请求支持 `replaceStatus`\n        - **控制台日志**：支持通过 `disable://interceptConsole` 禁止 `log://` 拦截 `console`\n        - **CORS 处理**：`resCors://enable` 在请求头不存在 `origin` 时自动忽略\n14. feat: 内存优化\n        - **空请求处理**：去掉 `Empty Request` 减少内存和 CPU 占用\n        - **栈溢出修复**：修复 Maximum call stack size exceeded 错误\n        - **存储分离**：插件在不同实例使用不同的存储目录\n15. feat: 启动与运行优化\n        - **启动速度**：优化 Whistle 启动速度\n        - **延迟实现**：优化 `reqDelay` 和 `resDelay` 的实现机制\n        - **插件加载**：第三方集成可监听插件加载事件\n16. feat: 模板字符串增强\n        - **端口变量**：支持在模板字符串中通过 `clientPort` 和 `serverPort` 获取端口信息\n        - **插件变量**：支持在规则中同时设置多个 `%plugin-name=xxxx`（最多10个）\n17. fix: 问题修复\n        - **对话框定制**：`alert`、`confirm`、`prompt` 等浏览器内置窗口改用自定义实现，防止 Chrome 限制\n        - **WebSocket 抓包**：修复 WebSocket 无法抓包的问题\n        - **插件存储**：修复插件使用 `storage.setProperties` 失效问题\n        - **请求暂停**：修复某些情况下响应 stream pause 问题\n        - **第三方集成**：修复第三方集成时的内部请求转发问题\n18. feat: 功能调整\n        - **删除时机**：调整 `delete://reqH.xxxx` 的执行时机\n        - **转发逻辑**：优化内部请求转发逻辑的实现方式\n19. feat: 其他改进\n        - **规则模式**：支持设置 `-M shadowRules`（抓包+设置规则）或 `-M shadowRulesOnly`（仅规则）\n        - **UI 请求**：显示 UI 请求情况\n        - **错误提示**：优化错误提示信息\n        - **协议显示**：显示插件转发的 HTTP 协议类型\n\n## v2.6\n\n1. feat: 系统监控与状态\n      - **在线状态监控**：通过 `Online` 菜单查看当前进程的请求数、CPU、内存状态\n      - **API 监控接口**：支持通过 `proxy.getRuntimeInfo()` 获取运行时信息\n      - **性能指标**：Online 面板支持显示 QPS 及内存、CPU、QPS 的最大值\n2. feat: 数据管理\n      - **回收站功能**：删除的 Rules 或 Values 先存放到回收站（最多缓存120条），支持恢复\n      - **证书生成工具**：`Network > Tools > ToolBox` 支持通过域名生成对应证书\n      - **HAR 文件导出**：支持导出抓包数据为 HAR 格式文件\n3. feat: Network 功能增强\n      - **高级搜索**：搜索框支持最多3个关键字过滤\n      - **源码查看**：右键菜单新增 `Open/Source` 查看抓包数据源码\n      - **SVG 预览**：支持预览 SVG 文件\n4. feat: 编辑器与规则管理\n      - **只读模式**：支持通过 `disabledEditor=1` 参数将 Rules & Values 编辑器设置为只读模式\n      - **行号注释**：启用行号显示时，双击行数可注释或取消注释\n      - **规则高亮**：优化编辑器对插件规则的高亮显示\n5. feat: 交互改进\n      - **工具集扩展**：`Network / Tools / Toolbox` 支持将对象转成 Query 参数\n      - **快捷操作**：支持扩展 `util.openEditor(value)` 方法\n6. feat: 规则导入导出\n      - **规则导入**：支持通过 `--shadowRules jsonString` 导入规则到 Rules\n      - **命令行输出**：优化命令行启动时的信息显示\n7. feat: 模式设置\n      - **运行模式**：支持 `-M` 参数设置多种模式：\n      - `disabledBackOption|disabledMultipleOption|notAllowDisableRules`\n      - `rulesOnly` 及 `pluginsOnly`\n8. feat: 内部路径处理\n      - **路径灵活性**：内部路径支持设置域名和端口参数，格式：\n      - `/...whistle-path.5b6af7b9884e1165...///__domain__port__/path/to`\n      - `/...whistle-path.5b6af7b9884e1165...///path/to?_whistleInternalHost_=__domain__port__`\n9. 内存与连接管理\n      - **连接优化**：确保及时关闭无用连接，减少内存占用\n      - **GC 优化**：调整 GC 参数 `--max-semi-space-size=64`\n      - **性能监控**：支持通过 CGI 或 API 获取当前处理的请求总数\n10. feat: 异常处理\n        - **插件异常**：支持通过 `process.on('pforkError')` 获取插件抛出的异常信息\n        - **未捕获异常**：优化处理请求过程中无法捕获的异常\n        - **超时处理**：`onSocketEnd` 添加 `timeout` 事件，兼容各种异常情况\n11. feat: 内部连接\n        - **连接管理**：优化内部连接管理机制\n12. feat: 过滤规则\n        - **规则修复**：修复 `excludeFilter` 和 `includeFilter` 混合配置时结果错乱问题\n        - **域名匹配**：域名通配符现在支持获取子匹配内容\n13. feat: 代理配置\n        - **代理连接头**：支持通过 `disable://proxyConnection` 将代理转发头改为 `Proxy-Connection: close`\n        - **客户端过滤**：支持通过 URL 参数的 clientId 过滤抓包数据\n14. fix: 主要问题修复\n        - **规则文件保存**：修复规则文件名称过长时保存失败的问题\n        - **编辑器问题**：修复编辑器高亮显示插件规则的问题\n        - **管理界面安全**：修复管理界面 CGI 路径可以随意拼接的问题\n        - **Node 版本兼容**：修复特定 Node 版本（v15.5.0）的兼容性问题\n        - **pipe 协议**：修复可能导致数据丢失的问题\n        - **本地文件替换**：本地文件替换的响应头默认加入 `content-length` 字段\n15. perf: 性能问题\n        - **卡死问题**：修复部分 Node 版本可能卡死的问题\n        - **连接泄漏**：优化连接管理，减少资源占用\n16. feat: 响应头处理\n        - **内容长度控制**：本地文件替换时自动添加 `content-length` 字段，可通过 `delete://resH.content-length` 禁用\n17. 内部请求处理\n        - **异步处理**：支持通过 `process.on('pforkError')` 监听插件进程异常\n\n## v2.5\n\n1. feat: 客户端证书支持\n      - **自定义客户端证书**：支持配置自定义客户端证书，增强安全认证能力\n      - **连接错误处理**：优化建立连接时的错误处理机制\n2. feat: 安全协议增强\n      - **加密算法扩展**：添加 `cipher` 协议支持自定义兜底加密算法\n      - **安全更新**：更新 node-forge 解决已知安全问题\n      - **插件安全控制**：启动参数支持通过 `allowPluginList` 和 `blockPluginList` 控制插件加载\n3. feat: 认证模式\n      - **Proxifier 模式**：支持 `-M proxifier` 开启 proxifier 模式，自动拦截特定请求进行证书判断\n4. feat: 隧道代理增强\n      - **确认机制**：tunnel 代理支持确认机制，提升代理稳定性\n      - **连接恢复**：修复 HTTP 请求走 tunnel 代理未调用 `socket.resume()` 的问题\n      - **行级代理配置**：新增 `lineProps://proxyHost|proxyTunnel` 只对当前行生效\n5. feat: 多层代理支持\n      - **双层代理**：添加 `enable://proxyTunnel` 支持两层 HTTP 代理\n      - **内部请求支持**：内部路径请求也支持 `enable://proxyTunnel`\n      - **协议转换修复**：解决 `https2http-proxy` 部分请求无法正常转换问题\n6. feat: WebSocket 处理\n      - **自动转换**：配置 hosts 的 WebSocket HTTPS 请求支持自动转 HTTP\n      - **Origin 修复**：修复自动修改 WebSocket origin 问题\n      - **数据保留**：拦截 HTTPS 请求后保留 tunnel 代理请求头数据\n7. feat: 插件管理\n      - **状态显示**：插件禁用后在页面标签显示 `Disabled` 标识\n      - **命令自定义**：支持自定义 plugins 列表的卸载及安装命令名称\n      - **规则数据获取**：插件可通过 `req.originalReq.ruleUrl` 获取规则匹配结果\n8. feat: 插件功能增强\n      - **Trailer 支持**：插件自动添加 trailers，可通过 `res.disableTrailer` 禁用\n      - **事件监听优化**：优化监听 `res.on('end', cb)` 事件，确保事件触发\n9. feat: 数据删除与清空\n      - **内容删除**：支持通过 `delete://body` 删除请求及响应内容\n      - **选择性删除**：支持 `delete://req.body` 和 `delete://res.body` 分别删除\n      - **内容清空**：支持通过 `reqBody://()` 或 `resBody://()` 清空请求或响应内容\n10. feat: Trailer 操作\n      - **Trailer 传递**：支持传递 `trailers`\n      - **Trailer 删除**：支持通过 `delete://trailer.xxx|trailer.yyy` 删除指定 trailer\n      - **Trailer 修改**：支持通过 `headerReplace://trailer.key:pattern=value` 及 `trailers://json` 修改\n11. feat: 协议优化\n      - **SNI 控制**：`enable://servername` 删除 HTTPS 请求的 SNI\n      - **状态码调整**：`statusCode` 移入 `rule` 里面跟 `file` 等协议同级\n      - **CORS 优化**：`resCors://enable` 自动设置 OPTIONS 请求的跨域字段\n12. feat: 界面布局\n      - **左侧菜单控制**：支持通过请求参数 `hideLeftMenu=true` 隐藏左菜单\n      - **规则开关**：左侧菜单新增 checkbox 快速禁用或启用 Rules/Plugins\n      - **系统 hosts**：去掉同步系统 hosts 设置\n13. feat: 数据显示\n      - **时间精度**：页面时间支持显示毫秒\n      - **编码显示**：修复页面 Content Encoding 显示错误问题\n      - **响应头大小**：修复 h2 请求转成 https 请求时界面显示响应头大小问题\n14. feat: 快捷键优化\n      - **通用重放**：添加快捷键 `ctrl[cmd] + r` 或 `ctrl[cmd] + shift + r` 重放请求\n      - **Frames 重放**：Frames 支持快捷键 `Ctrl/Cmd + R` 重放请求\n15. feat: 交互增强\n      - **请求头显示**：加粗 Composer 里面的 whistle 自定义请求头\n      - **Network 字体**：调整 Network 字体加粗效果\n      - **数据标记**：Network 右键菜单添加 `Actions>Mark` 标记抓包数据\n16. feat: 命令行工具\n      - **Docker 部署**：支持 `w2 run -M prod` 方便 Docker 部署\n      - **多规则模式**：支持通过 `-M useMultipleRules` 启用多选\n      - **插件路径**：修复 `--addon \"path1,path2\"` 无法填多个路径问题\n17. feat: 开发配置\n      - **JSON5 配置**：支持 json5 配置文件\n      - **节点要求**：Node13~14 开启 http2 功能，Node 版本最低要求改为 6\n      - **模板变量**：模板字符串支持通过 `${hostname}` 获取系统的 `os.hostname()`\n18. feat: HTTP2 支持\n      - **HTTP2 兼容性**：修复 `Node >= 14.1` 无法使用 http2 问题\n      - **协议检测**：自动检测请求或响应内容是否支持 gzip\n19. feat: 数据压缩\n      - **Gzip 优化**：gzip 返回抓包数据的 CGI\n      - **性能提示**：Composer 输入的文本长度现在防止浏览器卡死\n20. feat: 过滤规则\n      - **规则修复**：还原匹配顺序，修复规则覆盖问题\n      - **响应头过滤**：修复 `includeFilter://h:key=pattern` 无法匹配响应头问题\n21. fix: 问题修复\n      - **代理请求头**：修复代理请求头 `Host` 错乱问题\n      - **规则冲突**：修复 `reqHeaders://cookie=xxx` 和 `reqCookies://test=123` 无法同时生效问题\n      - **路径处理**：修复通过 `urlParams` 和 `pathReplace` 修改请求 URL 参数的问题\n      - **Composer 构造**：修复 Composer 构造没有 body 的请求不设置 `content-length: 0` 问题\n22. fix: 界面修复\n      - **Overview 显示**：修复请求包含匹配的插件规则时 Overview 界面脚本报错问题\n      - **插件排序**：修复同时安装的插件可能出现排序跳动问题\n      - **Frames 显示**：Frames 页面二进制数据字体加粗\n23. feat: 功能增强\n      - **JSON 操作**：JSON Tree 支持复制子节点数据，JSONView 右键菜单新增 `Collapse Parent`\n      - **数据拷贝**：支持将请求头以 JSON 文本拷贝\n      - **WebSocket 状态**：支持显示 WebSocket 关闭的错误码\n24. feat: 搜索与帮助\n      - **搜索历史**：Network 搜索框添加历史记录功能\n      - **规则帮助**：Overview 规则列表 hover 可点击查看帮助文档\n      - **插件安装**：Plugins 添加 `ReinstallAll` 按钮，可复制插件安装命令\n25. feat: 文件处理\n      - **Composer 文件上传**：支持上传本地文件\n      - **代理标准**：修复某些服务未按 HTTP 标准执行导致的 pending 问题\n26. feat: 接口访问\n      - **访客模式**：新增访客模式可以访问的接口\n      - **接口安全**：减少暴露无登录态的接口\n\n## v2.4\n\n1. feat: 双向认证支持\n      - **客户端服务端双向认证**：支持自定义客户端证书，实现客户端与服务端双向认证\n      - **安全模式**：新增启动参数 `-M safe` 开启安全模式，严格校验服务端证书\n      - **证书管理**：`HTTPS > View all custom certificates` 支持高亮显示过期证书，并支持复制证书安装路径\n2. feat: 证书与主机配置\n      - **本地主机配置**：修复本地 hosts 文件未配置 `127.0.0.1 localhost` 可能导致 HTTPS 请求失败问题\n      - **Express 框架修复**：修复 Express 框架默认添加的 `x-powered-by` 响应头问题\n3. feat: HTTP2 优化\n      - **Node 版本要求**：鉴于低版本 Node 的 HTTP/2 模块 bug 较多，统一调整为 Node v12.12.0 及以上版本才支持 HTTP/2\n      - **HTTP2 会话错误**：修复部分网站可能出现的 `ERR_HTTP2_SESSION_ERROR`\n      - **DELETE 请求优化**：HTTP2 的 `DELETE` 请求如果携带请求内容，则自动降级为 HTTP/1.1\n      - **HTTP/2 兼容性**：HTTP/2 支持 delete 请求携带 body\n3. feat: 代理功能扩展\n      - **内部 HTTP 代理**：新增 `internal-http-proxy` 协议，功能与 `internal-proxy` 类似，但对 WebSocket 请求使用 tunnel 代理\n      - **SOCKS 代理稳定性**：修复启用 `--socksServer port` 后请求异常可能导致程序 crash 问题\n      - **HTTPS 降级**：支持 post 等包含请求内容的 HTTPS 请求自动降级到 HTTP 请求\n4. feat: 协议处理改进\n      - **pipe 协议修复**：修复使用 `pipe` 时请求异常导致没有捕获问题，以及 HTTP 请求 pipe 失效问题\n      - **规则解析增强**：支持从请求 headers 里面的规则解析出 pipe 规则\n5. feat: Network 自定义化\n      - **自定义列**：支持在 Network 中自定义列显示，通过 `style` 协议配置\n      - **Frames 页面增强**：\n        - 新增 Overview Tab 用于查看帧数据的基本信息\n        - 二进制数据字体加粗显示\n      - **搜索过滤**：修复 Network 搜索过滤可能出现重复数据的问题\n6. feat: 数据展示优化\n      - **Composer 响应数据**：传给 Composer 的响应数据改成 base64 格式\n      - **Saz 文件支持**：支持显示 saz 文件里面的非文本内容\n      - **Overview 面板**：在 Overview 里面显示匹配的 `includeFilter`\n      - **时间显示**：支持在 Overview 里显示 HTTPS 自动转 HTTP 所消耗的时间\n7. feat: 规则匹配显示\n      - **过滤规则显示**：在 Overview 面板中显示匹配的 `includeFilter` 规则\n8. feat: 过滤规则优化\n      - **includeFilter 修复**：修复 `includeFilter://b:pattern` 失效问题\n      - **匹配逻辑调整**：调整 `includeFilter` 和 `excludeFilter` 匹配方式\n        - 需要满足所有 `includeFilter` 中的一个\n        - 不能匹配到任何 `excludeFilter`\n        - 即 `excludeFilter://p1 excludeFilter://p2 includeFilter://p3 includeFilter://p4` 相当于 `!(p1 || p2) && (p3 || p4)`\n9. feat: 远程规则管理\n      - **更新机制优化**：优化远程规则更新机制，防止误判拉取失败导致远程规则被清空\n      - **规则冲突修复**：修复设置 `reqBody://(xxxx) method://post` 无法同时生效问题\n10. feat: 启动参数增强\n      - **版本通知控制**：支持通过 `-M disableUpdateTips` 禁用版本升级通知（适用于第三方应用集成）\n      - **安装过程优化**：去掉安装过程中的 warning 提示\n      - **配置修复**：修复上一个版本引入的配置 host 出错问题\n11. feat: 网络配置\n      - **IPv6 优化**：优化 IPv6 配置\n      - **接口精简**：去掉多余的接口\n12. fix: 问题修复\n      - **主机配置**：修复本地 hosts 文件配置问题导致的 HTTPS 失败\n      - **Express 响应头**：修复 Express 框架默认添加的 `x-powered-by` 响应头问题\n      - **HTTP2 错误**：修复部分网站可能出现的 `ERR_HTTP2_SESSION_ERROR`\n      - **规则匹配**：修复 `includeFilter://b:pattern` 失效问题\n      - **代理异常**：修复启用 `--socksServer` 后请求异常导致程序 crash 问题\n      - **规则冲突**：修复设置 `reqBody://(xxxx) method://post` 无法同时生效问题\n\n## v2.3\n\n1. feat: 列表性能显著提升\n      - **虚拟列表技术**：引入 `react-virtualized` 库，极大提升 Network 列表渲染性能\n      - **默认数据量**：默认支持同时显示 1500 条抓包数据\n      - **自定义数量**：可通过 `Network > Filter > Max Rows Number` 调整显示数量\n      - **内存管理**：限制 zlib 的并发量，减少内存泄露风险\n2. feat: 依赖管理与构建\n      - **锁定依赖版本**：添加 `package-lock.json` 确保依赖一致性\n      - **构建稳定性**：提升项目构建和部署的稳定性\n3. feat: 请求头安全控制\n      - **x-forwarded-for 修复**：修复 `x-forwarded-for` 混乱问题\n      - **自定义转发头**：支持通过 `forwardedFor://ip` 或 `reqHeaders://x-forwarded-for=ip` 自定义转发地址\n      - **代理转发逻辑**：直接请求默认不带 `x-forwarded-for`，代理转发自动带上非本地 IP\n4. feat: HTTPS 与证书处理\n      - **非 SNI 请求支持**：修复非 SNI 的 HTTPS 请求无法解包问题\n      - **HTTP2 兼容性**：修复 HTTP2 模块对请求响应格式要求过于严格的问题\n5. feat: 内容注入安全\n      - **严格 HTML 检查**：`enable://strictHtml` 确保仅当第一个非空白字符是 `<` 时才注入内容\n      - **安全 HTML 检查**：`enable://safeHtml` 防止误注入到非标准接口（检查非 `{{` 开头）\n      - **误注入防护**：统一给域名注入脚本时，防止非 HTML 响应类型被误注入\n6. feat: 插件开发体验\n      - **运行时标识**：`@url` 请求自动带上 `x-whistle-runtime-id`，便于插件判断请求来源\n      - **插件状态查询**：新增 `options.isEnable()` 方法获取插件启用状态\n      - **模板变量支持**：插件配置文件支持占位符：\n        - `{{whistlePluginName}}` - 获取插件短名称（不包含 `whistle.`）\n        - `{{whistlePluginPackage.xx.yy.zzz}}` - 获取插件 `package.json` 的值\n      - **插件排序**：修复同时安装的插件可能出现的排序跳动问题\n7. feat: WebSocket 处理\n      - **状态码透传**：WebSocket 返回非 101 状态时，透传给浏览器\n      - **代理客户端 IP**：修复 WebSocket 使用 `internal-proxy` 时无法准确带上 clientIp 的问题\n8. 代理与连接管理\n      - **代理重试修复**：修复设置代理重试后重复添加路径问题\n      - **Socket 错误处理**：强制为所有 socket 设置空的 errorHandler，防止未捕获异常\n      - **压缩控制**：支持通过启动参数 `-M noGzip` 禁用所有请求的 `gzip` 功能\n9. 缓存策略调整\n      - **缓存优先级**：`cache://xxx` 现在拥有最高优先级\n      - **响应头保留**：支持通过 `cache://reserve` 强制保留原始请求头\n      - **注入与缓存协调**：修复 `log://xxx` 和 `weinre://xxx` 注入时与缓存的冲突问题\n10. feat: Network 界面优化\n      - **列宽调整**：Network 的 `URL` 列支持修改宽度\n      - **布局改进**：优化界面布局和交互体验\n\n## v2.2\n\n1. feat: 全局 HTTP2 开关\n      - **界面控制**：支持通过 `Network -> HTTPS -> Enable HTTP/2` 关闭或开启 HTTP2 请求\n      - **局部启用**：可以通过 `pattern enable://h2` 为特定请求局部开启 HTTP2\n      - **模式切换**：取消选择 HTTP2 后，接收和发送请求的方式都改用非 HTTP2\n2. feat: HTTP2 性能优化\n      - **Session 缓存**：优化 H2 session 缓存策略，提升连接复用效率\n      - **Node.js 兼容性**：修复 Node.js 版本兼容性问题（issue #27384）\n      - **超时设置优化**：去掉请求超时设置，改进连接管理\n3. feat: 命令行规则配置\n      - **远程规则加载**：支持通过 `w2 start -r \"@https://xxx\"` 从远程 URL 加载规则\n      - **本地规则文件**：支持通过 `w2 start -r \"@filepath\"` 从本地文件加载规则\n      - **直接规则设置**：支持通过 `w2 start -r \"www.test.com/path/to reqHeaders://x-test=1\"` 直接设置规则\n      - **组合配置**：支持 JSON.stringify 格式组合多个规则源\n4. feat: 规则执行优化\n      - **响应阶段规则**：部分在响应阶段才会执行的规则放到请求响应后再做匹配\n      - **性能改进**：优化规则匹配时机，提高执行效率\n5. feat: HAR 文件支持\n      - **编码检测**：支持自动检测 `xxx.har` 文件是否使用 base64 编码\n      - **数据兼容性**：改进 HAR 文件导入的兼容性和准确性\n6. feat: 内部请求处理\n      - **重试机制修复**：修复内部请求重试可能导致死循环的问题\n      - **连接稳定性**：改进内部请求处理逻辑，避免无限重试\n8. feat: 整体优化\n      - **代码重构**：优化 H2 session 管理逻辑\n      - **配置简化**：去掉不必要的请求超时设置\n\n## v2.1\n1. feat: JSON 对象处理\n      - **数组与嵌套支持**：多行形式的 JSON 对象现在支持设置数组及多层嵌套的值\n      - **智能转换规则**：\n        - 以前版本：`[1]: \"test\"` 转换为 `{ \"[1]\": \"test\" }`\n        - 现在版本：`[1]: \"test\"` 转换为 `var arr = []; arr[1] = 'test';`\n      - **复杂结构支持**：支持类似 `[a.b[3].c]: abc` 的复杂嵌套结构\n      - **转义控制**：使用双引号 `\"[xxx.yyy[n]]\"` 可以避免自动转义\n2. feat: 域名匹配扩展\n      - **灵活匹配语法**：\n        - `.test.com` - 匹配 `x.test.com` 与 `test.com`\n        - `*.test.com` - 匹配 `x.test.com`，不匹配 `test.com`\n        - `**.test.com` - 匹配 `x.test.com` 及其所有子孙代域名\n        - `***.test.com` - 匹配 `test.com` 及其所有子孙代域名\n      - **精确控制**：支持添加协议及路径进行更精确匹配，如 `http://.test.com/path/to/*`\n3. feat: 自定义方法支持\n      - **方法扩展**：Composer 界面支持自定义请求方法\n      - **灵活构造**：用户可以更灵活地构造和发送各种类型的请求\n4. feat: HTTP2 性能优化\n      - **Session 复用**：减少 HTTP2 的 session 数，每个客户端到 HTTP2 session 可以完全复用\n      - **连接效率**：提升 HTTP2 连接的复用效率，减少资源占用\n5. feat: 插件开发体验\n      - **提示优化**：修复自定义插件 hint 只有一个补全数据不显示的问题\n      - **环境变量支持**：支持通过环境变量 `env.WHISTLE_PLUGIN_EXEC_PATH` 或启动参数 `-M buildIn` 设置 fork 插件进程的 Node 路径\n      - **路径控制**：默认为全局 Node，可根据需要指定特定 Node 路径\n6. feat: Overview 面板增强\n      - **压缩信息显示**：Overview 面板支持显示请求的 gzip 前后大小对比\n      - **数据大小展示**：修复请求和响应内容为空时不显示大小的问题\n7. fix: URL 替换修复\n      - **路径处理**：修复 URL 替换 `url replacementUrl` 时路径取错的问题\n      - **替换准确性**：确保 URL 替换操作准确处理路径参数\n8. perf: 性能优化\n      - **HTTP2 连接**：优化 HTTP2 session 管理，减少连接创建开销\n      - **数据展示**：改进空内容的数据显示逻辑\n\n## v2.0.0\n1. feat: **支持 HTTP2 功能**\n\t> 请确保运行的 Node 版本为 [LTS(>= 10.16.0) 或 Stable(>= 12.12.0) 的最新版本](https://nodejs.org/en/)，否则可能会出现一些异常，如：[#24037](https://github.com/nodejs/node/issues/24037)、[#24470](https://github.com/nodejs/node/issues/24470)\n2. feat: `**/path/to` 如果 `path/to` 里面包含 `*`，如 `*/cgi-*`，则等价与 `^*/cgi-*`\n\n## v1.17\n1. feat: 浏览器和 whistle 之间支持通过 HTTP2 建立连接，（**需要把 [Node](https://nodejs.org) 更新到 `v10.16.0` 及以上版本**）\n2. refactor: 调整连接缓存策略，任何连接不做长缓存，减少内存占用\n\n## v1.16\n1. feat: 支持插件通过 `options.getRules(cb), options.getValues(cb), options.getCustomCertsInfo(cb)`，分别获取插件 Rules、Values、自定义证书信息\n2. feat: HTTPS 菜单的对话框添加 `View Custom Certs` 按钮，用于管理自定义证书\n3. feat: 支持通过 `w2 run --inspect` 或 `w2 run --inspectBrk` 开启调试模式\n4. feat: 插件列表添加 `Sync` 按钮可用于获取插件的规则或值并设置到界面的Rules或Values\n5. feat: 支持通过插件 package.json 配置 `\"whistleConfig: { \"hintUrl\": \"/cgi-xxx/xxx\" }\"`  等方式自定义自动补全列表功能\n6. feat: 支持通过 `req.sessionStorage.[get|set|remove]` 实现插件各个server hooks之间的数据传递\n\n\n## v1.15\n1. feat: 支持通过命令行参数 `--socksPort 1080` 启动指定监听端口的 SOCKS v5 服务（目前只支持普通 TCP 请求）\n2. feat: 插件server添加了 `req.passThrough()`，`req.request(url/options, cb)`，\t`req.writeHead(code, message, headers)` 等方法用于将插件里面的请求转发到指定服务\n3. feat: 支持通过命令行参数 `-M rules` 启动无抓包页面模式，这种模式下UI将看不到Network，无法抓包且插件无法通过 `req.getSession(cb)` 获取抓包数据\n4. feat: 支持通过 `style://color=!xxx&fontStyle=xxxxx&bgColor=red` 设置请求的字体颜色/样式/背景颜色\n5. feat: 支持通过 `enable://proxyFirst` 调整 proxy 配置的优先级高于 host （默认：host > proxy）\n6. fix: 修复一些运行过程中遇到的问题\n\n\n## v1.14\n1. feat: 新增协议 [pipe](https://wproxy.org/docs/rules/pipe.html)、[headerReplace](https://wproxy.org/docs/rules/headerReplace.html)\n2. fix: `querystring.parse('+')` 自动转转成空格 ' ' 或 `%2B` 问题\n3. refactor: 优化启动参数 [--max-http-header-size=size](https://nodejs.org/dist/latest-v10/docs/api/cli.html#cli_max_http_header_size_size) \n4. feat: 新增命令行参数 `--httpPort` 和 `--httpsPort`，分别用于启动普通的 http 和 https server，方便做反向代理，且可用于再启动一个 `http proxy` （跟默认的 http 代理功能一致）和 `https proxy` （可作为https代理服务器） 功能\n5. fix: 修复一些运行过程中遇到的问题\n\n\n## v1.13\n1. feat: 优化插件功能，支持 `resRules.txt`\n2. feat: 新增协议 [excludeFilter](http://wproxy.org/docs/rules/excludeFilter.html) 和 [includeFilter](http://wproxy.org/docs/rules/includeFilter.html)\n3. feat: [log](http://wproxy.org/docs/rules/log.html) 支持注入 `whistle.onWhistleLogSend(level, logStr)` 获取页面日志信息自己做上报\n4. feat: 插件支持通过 package.json 配置 `\"pluginHomepage\": \"http://xxx.xxx.com/\"` 自定义界面 URL\n5. feat: 本地替换新增响应 `206` 功能，支持 iOS 播放本地替换的视频文件\n6. feat: 命令行添加 `--no-prev-options` 启动选项，支持通过 `w2 restart` 时不复用先前设置的选项\n\n## v1.12\n1. feat: 支持在 `Rules > Settings > The later rules first` 调整规则的优先顺序，默认从上到下，其中Default里面的规则优先级最低，这个设置只对在 Rules 配置的规则生效，对 [reqScript](https://wproxy.org/docs/rules/reqScript.html) 和插件设置的规则不生效\n2. feat: 新增协议 [https-proxy](https://wproxy.org/docs/rules/https-proxy.html)\n3. feat: [resCookies](https://wproxy.org/docs/rules/resCookies.html) 支持设置 [SameSite](https://www.owasp.org/index.php/SameSite)\n4. feat: 支持通过在 Rules 配置 `@url` 或 `@filepath` 或 `@whistle.xxx/path` 加载远程规则\n5. feat: 支持通过命令行 `-r, --shadowRules [shadowRules]` 设置 `shadowRules`\n6. feat: 支持内嵌 Values\n7. feat: 模板字符串支持 `replace`，且支持子匹配\n    ```\n    pattern protocol://`${search.replace(pattern1,replacment)}`\n    www.test.com file://`${search.replace(/Course_(id)\\,?/ig,$1cid)}${test.html}`\n    ```\n    `pattern1` 为正则或普通字符串(不需要加引号)\n\n## v1.11\n1. feat: HTTPS 请求自动降级([https://github.com/avwo/whistle/issues/176](https://github.com/avwo/whistle/issues/176))\n2. feat: 支持显示HexView（二进制）\n3. feat: 新增协议 [xhost](https://wproxy.org/docs/rules/xhost.html)\n4. feat: 调整策略，部分协议支持同时匹配多个\n5. fix: 修复一些运行过程中遇到的问题\n\n## v1.10\n1. feat: 完善界面功能\n2. feat: 支持端口匹配 `:12345 operation`\n3. feat: 支持设置多个访问webui的域名 `-l \"webui1.example.com|webui2.example.com\"`\n4. feat: 支持通过命令行 `w2 add` 获取当前目录 `.whistle.js` 输出的规则配置，具体参见：[命令行参数](https://wproxy.org/docs/cli.html)\n5. feat: 通过 `w2 status [-S storage]` 或 `w2 --all` 显示当前whistle运行状态\n\n\n## v1.9\n1. feat: 支持通过命令行 `-M network` 设置为抓包模式，该模式只能查看抓包不能设置规则及加载插件\n2. feat: 支持通过命令行 `-L \"script=a.b.com&vase=x.y.com&nohost=imweb.nohost.pro\"` 自定义访问插件的域名\n3. feat: Composer 支持设置 Rules\n\n## v1.8\n1. feat: 新增协议 [htmlPrepend](https://wproxy.org/docs/rules/htmlPrepend.html)、[htmlBody](https://wproxy.org/docs/rules/htmlBody.html)、[htmlAppend](https://wproxy.org/docs/rules/htmlAppend.html)、[cssPrepend](https://wproxy.org/docs/rules/cssPrepend.html)、[cssBody](https://wproxy.org/docs/rules/cssBody.html)、[cssAppend](https://wproxy.org/docs/rules/cssAppend.html)、[jsPrepend](https://wproxy.org/docs/rules/jsPrepend.html)、[jsBody](https://wproxy.org/docs/rules/jsBody.html)、[jsAppend](https://wproxy.org/docs/rules/jsAppend.html)\n2. feat: 支持通配符匹配\n3. feat: 支持 `Copy As CURL`\n4. feat: 支持导入 HAR 文件\n5. feat: 支持第三方扩展 Rules 里面的 `@` 符号功能\n\n## v1.7\n1. feat: 新增协议 [resScript](https://wproxy.org/docs/rules/resScript.html)、 [responseFor](https://wproxy.org/docs/rules/responseFor.html)、 [resforwardedForScript](https://wproxy.org/docs/rules/forwardedFor.html)\n2. fix: 修复一些运行过程中遇到的问题\n\n## v1.6\n1. feat: 支持 WebSocket 请求映射，`ws://www.test.com/xxx https://www.abc.com/a/b`\n2. feat: 调整证书策略，防止域名里面有不合规的字符，导致 Chrome 出现证书校验失败\n3. fix: 修复一些运行过程中遇到的问题\n\n## v1.5\n1. feat: Composer 支持构造 WebSocket 和 TCP 请求\n2. feat: 支持通过命令行参数 `-P uiPort` 自定义 Whistle 管理界面端口\n3. feat: 完善插件功能，支持通过插件的根目录文件 `_values.txt` 设置插件私有的 Values (不支持 `values.txt`)，与私有规则 `_rules.txt` 配套使用\n4. feat: 界面支持左侧菜单模式，并支持显示请求客户端的端口号和服务器的端口号\n5. fix: 修复一些运行过程中遇到的问题\n\n## v1.4\n1. feat: 支持通过命令行参数 `-D, -baseDir` 修改 Whistle 存储目录的根路径\n2. perf: 优化 `os.networkInterfaces` 的性能\n3. fix: 修复一些运行过程中遇到的问题\n\n## v1.3\n1. feat: pattern 新增支持无 schema 模式 `//xxx`\n2. fix: 修复一些运行过程中遇到的问题\n\n## v1.2\n1. feat: 新增协议 [reqScript](https://wproxy.org/docs/rules/reqScript.html)、[ignore](https://wproxy.org/docs/rules/ignore.html)、[enable](https://wproxy.org/docs/rules/enable.html)\n2. feat: 完善插件功能，新增 `statsServer`\n3. feat: 新增下载根证书短链接 `http://rootca.pro`\n\n## v1.1\n1. feat: 新增协议 [pac](https://wproxy.org/docs/rules/pac.html)、[delete](https://wproxy.org/docs/rules/delete.html)、[reqHeaders](https://wproxy.org/docs/rules/reqHeaders.html)、[resHeaders](https://wproxy.org/docs/rules/resHeaders.html)\n2. feat: 完善插件功能\n3. fix: 修复一些运行过程中遇到的问题\n\n## v1.0\n1. feat: 完善插件功能，新增 `tunnelServer`、支持新协议 `whistle.xxx://`\n2. feat: 完善 `socks`、`proxy` 协议功能\n3. feat: 新增命令行参数 `-l, --localUIHost` 支持修改访问配置页面的域名，默认为 `local.whistlejs.com`\n4. feat: 代理请求新增 `x-whistle-policy` 用于设置 Whistle 策略\n5. fix: 修复一些运行过程中遇到的问题\n6. feat: 替换全新的 Logo，感谢部门的视觉设计同事 **[@wjdgh1031(鬼刀)](https://github.com/wjdgh1031)** 帮忙设计了新logo\n7. feat: 完善协议功能 [pathReplace](https://wproxy.org/docs/rules/pathReplace.html)、[log](https://wproxy.org/docs/rules/log.html)、[replaceStatus](https://wproxy.org/docs/rules/replaceStatus.html)、[rawfile](https://wproxy.org/docs/rules/rawfile.html)、[xrawfile](https://wproxy.org/docs/rules/xrawfile.html)、[reqAppend](https://wproxy.org/docs/rules/reqAppend.html)、[resAppend](https://wproxy.org/docs/rules/resAppend.html)、[reqType](https://wproxy.org/docs/rules/reqType.html)、[resType](https://wproxy.org/docs/rules/resType.html)、[reqCharset](https://wproxy.org/docs/rules/reqCharset.html)、[ua](https://wproxy.org/docs/rules/ua.html) 、[reqWriter](https://wproxy.org/docs/rules/reqWriter.html) 、[reqWriterRaw](https://wproxy.org/docs/rules/reqWriterRaw.html) 、[reqReplace](https://wproxy.org/docs/rules/reqReplace.html)、[resReplace](https://wproxy.org/docs/rules/resReplace.html) 等\n8. feat: 支持导出抓包数据\n9. feat: 支持启动多个实例 `w2 start -S newStorageDir -p newPort`\n10. feat: 支持自定义插件\n11. fix: 修复一些运行过程中遇到的问题\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 avwo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README-en_US.md",
    "content": "<p align=\"center\">\n  <a href=\"https://avwo.github.io/whistle/\">\n    <img alt=\"whistle logo\" src=\"https://user-images.githubusercontent.com/11450939/168828068-99e38862-d5fc-42bc-b5ab-6262b2ca27d6.png\">\n  </a>\n</p>\n\n# whistle\n\n[![NPM version](https://img.shields.io/npm/v/whistle.svg?style=flat-square)](https://npmjs.org/package/whistle)\n[![node version](https://img.shields.io/badge/node.js-%3E=_8-green.svg?style=flat-square)](http://nodejs.org/download/)\n[![npm download](https://img.shields.io/npm/dm/whistle.svg?style=flat-square)](https://npmjs.org/package/whistle)\n[![NPM count](https://img.shields.io/npm/dt/whistle.svg?style=flat-square)](https://www.npmjs.com/package/whistle)\n[![License](https://img.shields.io/github/license/avwo/whistle?style=flat-square)](https://www.npmjs.com/package/whistle)\n\n[中文](./README.md) · English\n\nWhistle (pronounced /ˈwisəl/) is a cross-platform network debugging and proxy tool built on Node.js. It provides powerful packet capture, request/response inspection and modification, a rule-based modification engine, and extensibility through plugins.\n\nKey features:\n1. **Powerful**\n   - Capture and modify HTTP, HTTPS, HTTP/2, WebSocket, and TCP traffic\n   - Supports HTTP, HTTPS, Socks and reverse proxy modes\n   - Built-in tools: Weinre (remote DOM inspection), Console (console logs), Composer (request replay/editing), etc.\n2. **Easy to use**\n   - Rule-based request/response modification\n   - Unified UI for captures, rules, plugins, Weinre/Console/Composer and more\n3. **Extensible**\n   - SPlugin support for extending rules and UI\n   - Can be used as an NPM module in projects\n4. **Cross-platform**\n   - Supports macOS, Windows, Linux (Ubuntu/Fedora) desktop systems\n   - Supports headless Linux server environments\n\n# Installation (recommended)\n\nDesktop users (macOS/Windows/Linux) should use the Whistle client: https://github.com/avwo/whistle-client\n> The client skips manual installation and configuration steps\n\n# Headless Linux / Server Install (CLI)\n\nFollow these 4 steps to deploy Whistle on a headless server:\n\n1. Install Whistle (recommended via npm)\n   - Install Node.js first: https://nodejs.org/\n   - Install: `npm i -g whistle`\n      > Alternatively via Homebrew: `brew install whistle`\n1. Start Whistle\n   - Command: `w2 start`\n2. Install CA certificate (required for HTTPS capture)\n   - Command: `w2 ca`\n   - Manual confirmation may be required:\n     - Windows: confirm with “Yes (Y)”\n     - macOS: may require entering password or Touch ID\n3. Configure proxy\n   - Command: `w2 proxy`\n   - Specify host:port: `w2 proxy \"10.x.x.x:8888\"`\n   - Disable system proxy: `w2 proxy 0`\n\nOther proxy options:\n- Recommended: use a Chrome proxy extension ZeroOmega for easy switching\n  > Chrome Web Store (or manual install if blocked): https://chromewebstore.google.com/detail/proxy-switchyomega-3-zero/pfnededegaaopdmhkdmcofjmoldfiped\n- Use browser/devtools built-in proxy settings (e.g. Firefox, WeChat DevTools)\n- For apps that can't set proxies directly, use Proxifier (Windows/macOS)\n\n# Quick Start\n\nSee the official guide for usage and examples: https://wproxy.org/docs/getting-started.html\n\n# Common Commands\n\n- Start: `w2 start`\n- Stop: `w2 stop`\n- Restart: `w2 restart`\n- Status: `w2 status`\n- Install CA: `w2 ca`\n- Set proxy: `w2 proxy [host:port]` (use `w2 proxy 0` to disable)\n\n# License\n\n[MIT — see the LICENSE file](./LICENSE)\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://avwo.github.io/whistle/\">\n    <img alt=\"whistle logo\" src=\"https://user-images.githubusercontent.com/11450939/168828068-99e38862-d5fc-42bc-b5ab-6262b2ca27d6.png\">\n  </a>\n</p>\n\n# whistle\n\n[![NPM version](https://img.shields.io/npm/v/whistle.svg?style=flat-square)](https://npmjs.org/package/whistle)\n[![node version](https://img.shields.io/badge/node.js->=_8-green.svg?style=flat-square)](http://nodejs.org/download/)\n[![npm download](https://img.shields.io/npm/dm/whistle.svg?style=flat-square)](https://npmjs.org/package/whistle)\n[![NPM count](https://img.shields.io/npm/dt/whistle.svg?style=flat-square)](https://www.npmjs.com/package/whistle)\n[![License](https://img.shields.io/github/license/avwo/whistle?style=flat-square)](https://www.npmjs.com/package/whistle)\n\n中文 · [English](./README-en_US.md)\n\nWhistle（发音 /ˈwisəl/）是基于 Node.js 的跨平台网络抓包与调试工具，特点如下：\n1. **功能强大**\n   - 支持 HTTP、HTTPS、HTTP/2、WebSocket、TCP 的抓包与修改请求/响应\n   - 支持 HTTP、HTTPS、Socks、反向代理等多种代理模式\n   - 内置常用调试工具：Weinre（远程 DOM 检查）、Console（查看 console 日志）、Composer（请求重放与编辑）等\n2. **操作简单**\n   - 通过规则配置即可修改请求/响应\n   - 提供一站式管理界面：抓包、规则、插件、Weinre/Console/Composer 等集中管理\n3. **可扩展**\n   - 支持插件扩展规则与界面功能\n   - 可作为 NPM 模块在项目中引用\n4. **跨平台**\n   - 支持 macOS、Windows、Linux（Ubuntu / Fedora）等桌面系统\n   - 支持无界面 Linux 服务器环境\n\n# 安装（推荐）\n\n桌面用户（macOS/Windows/Linux）推荐使用 Whistle 客户端：https://github.com/avwo/whistle-client\n\n> 客户端可以免去大部分手动安装与配置步骤\n\n# 无界面 Linux / 服务器 安装（命令行）\n\n按以下 4 步在无界面服务器上快速部署：\n1. 安装 Whistle（推荐使用 npm）\n   - 需要先安装 Node.js：https://nodejs.org/\n   - 安装命令：`npm i -g whistle`\n      > 也支持 Homebrew：`brew install whistle`\n1. 启动 Whistle\n   - 命令：`w2 start`\n2. 安装根证书（用于 HTTPS 抓包）\n   - 命令：`w2 ca`\n   - 安装过程中可能需手动确认：\n      - Windows：最后选择 “是 (Y)” 确认\n      - macOS：可能需要输入开机密码或 Touch ID 验证\n3. 设置代理\n   - 命令：`w2 proxy`\n   - 设置指定 IP: `w2 proxy \"10.x.x.x:8888\"`\n   - 关闭系统代理： `w2 proxy 0`\n\n其它代理方式：\n- 推荐：使用 Chrome 插件 ZeroOmega（便于在浏览器间切换代理）\n  > Chrome 商店地址（若无法访问可手动安装）：https://chromewebstore.google.com/detail/proxy-switchyomega-3-zero/pfnededegaaopdmhkdmcofjmoldfiped\n- 浏览器或开发者工具自带代理设置（例如 Firefox、微信开发者工具）\n- 对于无法直接设置代理的应用，可使用 Proxifier（Windows / macOS）\n\n# 快速上手\n\n详细使用指南与示例请查看官方文档：https://wproxy.org/docs/getting-started.html\n\n# 常见命令速查\n- 启动：`w2 start`\n- 停止：`w2 stop`\n- 重启：`w2 restart`\n- 查看状态：`w2 status`\n- 安装证书：`w2 ca`\n- 设置代理：`w2 proxy [host:port]`（`w2 proxy 0` 关闭）\n\n# License\n\n[MIT（详见 LICENSE 文件）](./LICENSE)\n\n"
  },
  {
    "path": "assets/fiddler/meta.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Session SID=\"${SID}\">\n  <SessionTimers ClientConnected=\"${ClientConnected}\"\n   ClientDoneRequest=\"${ClientDoneRequest}\" GatewayTime=\"${GatewayTime}\"\n   DNSTime=\"${DNSTime}\" TCPConnectTime=\"${TCPConnectTime}\"\n   ServerGotRequest=\"${ServerGotRequest}\"\n   ServerBeginResponse=\"${ServerBeginResponse}\"\n   ServerDoneResponse=\"${ServerDoneResponse}\"\n   ClientBeginResponse=\"${ClientBeginResponse}\"\n   ClientDoneResponse=\"${ClientDoneResponse}\" />\n  <PipeInfo />\n  <SessionFlags>\n    <SessionFlag N=\"x-ttfb\" V=\"${ttfb}\" />\n    <SessionFlag N=\"x-processinfo\" V=\"whistle\" />\n    <SessionFlag N=\"x-transfer-size\" V=\"${transfer-size}\" />\n    <SessionFlag N=\"x-clientip\" V=\"${clientip}\" />\n    <SessionFlag N=\"x-ttlb\" V=\"${ttlb}\" />\n    <SessionFlag N=\"x-hostip\" V=\"${hostip}\" />\n    <SessionFlag N=\"x-clientport\" V=\"${clientport}\" />\n    <SessionFlag N=\"x-serverport\" V=\"${serverport}\" />\n    <SessionFlag N=\"ui-comments\" V=\"${ui-comments}\" />\n  </SessionFlags>\n</Session>\n"
  },
  {
    "path": "assets/js/log.js",
    "content": "\n;(function() {\n  if (typeof window === 'undefined' || typeof Image === 'undefined') {\n    return;\n  }\n\n  if (window._whistleConsole) {\n    return;\n  }\n  var console = window.console = window.console || {};\n  var wConsole = window._whistleConsole = {};\n  var JSON = window.JSON || patchJSON();\n  function patchJSON() {\n    var JSON = {};\n    var rx_escapable = /[\\\\\\\"\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\n\n    function f(n) {\n      return n < 10\n              ? '0' + n\n              : n;\n    }\n\n    function this_value() {\n      return this.valueOf();\n    }\n\n    if (typeof Date.prototype.toJSON !== 'function') {\n\n      Date.prototype.toJSON = function () {\n\n        return isFinite(this.valueOf())\n                  ? this.getUTCFullYear() + '-' +\n                          f(this.getUTCMonth() + 1) + '-' +\n                          f(this.getUTCDate()) + 'T' +\n                          f(this.getUTCHours()) + ':' +\n                          f(this.getUTCMinutes()) + ':' +\n                          f(this.getUTCSeconds()) + 'Z'\n                  : null;\n      };\n\n      Boolean.prototype.toJSON = this_value;\n      Number.prototype.toJSON = this_value;\n      String.prototype.toJSON = this_value;\n    }\n\n    var gap,\n      indent,\n      meta,\n      rep;\n\n\n    function quote(string) {\n\n      rx_escapable.lastIndex = 0;\n      return rx_escapable.test(string)\n              ? '\"' + string.replace(rx_escapable, function (a) {\n                var c = meta[a];\n                return typeof c === 'string'\n                      ? c\n                      : '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n              }) + '\"'\n              : '\"' + string + '\"';\n    }\n\n\n    function str(key, holder) {\n\n      var i,\n        k,\n        v,\n        length,\n        mind = gap,\n        partial,\n        value = holder[key];\n\n      if (value && typeof value === 'object' &&\n                  typeof value.toJSON === 'function') {\n        value = value.toJSON(key);\n      }\n\n      if (typeof rep === 'function') {\n        value = rep.call(holder, key, value);\n      }\n\n      switch (typeof value) {\n      case 'string':\n        return quote(value);\n\n      case 'number':\n\n        return isFinite(value)\n                  ? String(value)\n                  : 'null';\n\n      case 'boolean':\n      case 'null':\n\n        return String(value);\n\n      case 'object':\n\n        if (!value) {\n          return 'null';\n        }\n\n        gap += indent;\n        partial = [];\n\n        if (Object.prototype.toString.apply(value) === '[object Array]') {\n\n          length = value.length;\n          for (i = 0; i < length; i += 1) {\n            partial[i] = str(i, value) || 'null';\n          }\n\n          v = partial.length === 0\n                      ? '[]'\n                      : gap\n                          ? '[\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + ']'\n                          : '[' + partial.join(',') + ']';\n          gap = mind;\n          return v;\n        }\n\n        if (rep && typeof rep === 'object') {\n          length = rep.length;\n          for (i = 0; i < length; i += 1) {\n            if (typeof rep[i] === 'string') {\n              k = rep[i];\n              v = str(k, value);\n              if (v) {\n                partial.push(quote(k) + (\n                                  gap\n                                      ? ': '\n                                      : ':'\n                              ) + v);\n              }\n            }\n          }\n        } else {\n\n          for (k in value) {\n            if (Object.prototype.hasOwnProperty.call(value, k)) {\n              v = str(k, value);\n              if (v) {\n                partial.push(quote(k) + (\n                                  gap\n                                      ? ': '\n                                      : ':'\n                              ) + v);\n              }\n            }\n          }\n        }\n\n        v = partial.length === 0\n                  ? '{}'\n                  : gap\n                      ? '{\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + '}'\n                      : '{' + partial.join(',') + '}';\n        gap = mind;\n        return v;\n      }\n    }\n\n    if (typeof JSON.stringify !== 'function') {\n      meta = {\n        '\\b': '\\\\b',\n        '\\t': '\\\\t',\n        '\\n': '\\\\n',\n        '\\f': '\\\\f',\n        '\\r': '\\\\r',\n        '\"': '\\\\\"',\n        '\\\\': '\\\\\\\\'\n      };\n      JSON.stringify = function (value, replacer, space) {\n\n        var i;\n        gap = '';\n        indent = '';\n\n        if (typeof space === 'number') {\n          for (i = 0; i < space; i += 1) {\n            indent += ' ';\n          }\n\n        } else if (typeof space === 'string') {\n          indent = space;\n        }\n\n        rep = replacer;\n        if (replacer && typeof replacer !== 'function' &&\n                      (typeof replacer !== 'object' ||\n                      typeof replacer.length !== 'number')) {\n          throw new Error('JSON.stringify');\n        }\n\n\n        return str('', {'': value});\n      };\n    }\n\n    return JSON;\n  }\n\n  function stringify(obj) {\n    if (typeof obj === 'function') {\n      return obj.toString();\n    }\n\n    if (obj instanceof Error) {\n      var stack = obj.stack;\n      if (stack && typeof stack === 'string') {\n        if (obj.message && stack.indexOf(obj.message) === -1) {\n          return 'Error: ' + obj.message + '\\n' + stack;\n        }\n      }\n      return 'Error: ' + obj.message;\n    }\n\n    return obj === undefined ? 'undefined' : obj;\n  }\n  var prefixPath;\n  function getPathPrefix() {\n    if (prefixPath) {\n      return prefixPath;\n    }\n    prefixPath = window.__WHISTLE_PATH_PREFIX__;\n    if (/^\\/[\\w./-]+$/.test(prefixPath) && prefixPath.length <= 128) {\n      var len = prefixPath.length - 1;\n      if (prefixPath[len] === '/') {\n        prefixPath = prefixPath.substring(0, len);\n      }\n    } else {\n      prefixPath = '';\n    }\n    return prefixPath;\n  }\n\n  var index = 0;\n  var MAX_LEN = 1024 * 56;\n  function addLog(level, text) {\n    var img = new Image();\n    var timer;\n    if (index > 9999) {\n      index = 0;\n    }\n    var logStr = encodeURIComponent(text && (text + ''));\n    if (logStr.length > MAX_LEN) {\n      logStr = logStr.substring(0, MAX_LEN);\n      var percIndex = logStr.indexOf('%', MAX_LEN - 3);\n      if (percIndex !== -1) {\n        logStr = logStr.substring(0, percIndex);\n      }\n    }\n    var baseUrl = '$BASE_URL' + getPathPrefix();\n    img.src = baseUrl + '$LOG_CGI?id=$LOG_ID&level=' + level + '&text=' + logStr\n      + '&t=' + new Date().getTime() + '&' + ++index;\n    var preventGC = function() {\n      img.onload = img.onerror = null;\n      clearTimeout(timer);\n    };\n    img.onload = img.onerror = preventGC;\n    timer = setTimeout(preventGC, 3000);\n    if (typeof window.onWhistleLogSend === 'function') {\n      window.onWhistleLogSend(level, text);\n    }\n  }\n\n  function getPageInfo() {\n    return '\\r\\nPage URL: ' + location.href + '\\r\\nUser Agent: ' + navigator.userAgent;\n  }\n\n  function getErrorStack(error, message) {\n    var stack = (error.stack || error.message) + '';\n    message = message || error.message;\n    if (typeof message === 'string') {\n      var msg = message.substring(message.indexOf(':') + 1);\n      if (stack.indexOf(msg) === -1) {\n        stack = message + '\\n' + stack;\n      }\n    }\n    return stack + getPageInfo();\n  }\n\n  function arrayIndexOf(arr, value) {\n    if (arr.indexOf) {\n      return arr.indexOf(value);\n    }\n    for (var i = 0, len = arr.length; i < len; i++) {\n      if (arr[i] === value) {\n        return i;\n      }\n    }\n    return -1;\n  }\n\n  function stringifyObj(obj) {\n    if (typeof obj === 'string') {\n      return obj;\n    }\n    try {\n      return JSON.stringify(obj);\n    } catch(e) {}\n    try {\n      var keyList = [];\n      var valList = [];\n      return JSON.stringify(obj, function(key, value) {\n        if (value && typeof value === 'object') {\n          var index = arrayIndexOf(valList, value);\n          valList.push(value);\n          keyList.push(key);\n          if (index !== -1) {\n            return '[Circular ' + keyList[index] + ']';\n          }\n        }\n        return value;\n      });\n    } catch(e) {}\n  }\n\n  var levels = ['fatal', 'error', 'warn', 'info', 'debug', 'log'];\n  var noop = function() {};\n  var slice = Array.prototype.slice;\n  for (var i = 0, len = levels.length; i < len; i++) {\n    (function(level) {\n      var fn = console[level] || noop;\n      var pending;\n      var wFn = wConsole[level] = function() {\n        var result = slice.call(arguments);\n        if (!result.length) {\n          result = [undefined];\n        }\n        if (typeof window.onBeforeWhistleLogSend === 'function') {\n          pending = true;\n          try {\n            window.onBeforeWhistleLogSend(result, level);\n          } catch(e) {\n            result.push('onBeforeWhistleLogSend' + stringify(e));\n          } finally {\n            pending = false;\n          }\n        }\n        var len = result.length;\n        if (!len) {\n          return;\n        }\n        for (var i = 0; i < len; i++) {\n          result[i] = stringify(result[i]);\n        }\n        result = stringifyObj(result);\n        result && addLog(level, result);\n      };\n      if ($INTERCEPT_CONSOLE) {\n        console[level] = function() {\n          if (pending) {\n            return;\n          }\n          pending = true;\n          var weinreFn = console['_weinre_' + level];\n          if (typeof weinreFn === 'function') {\n            try {\n              weinreFn.apply(this, arguments);\n            } catch (e) {}\n          }\n          wFn.apply(null, arguments);\n          try {\n            fn.apply(this, arguments);\n          } catch(e) {\n            fn(arguments.length < 2 ? arguments[0] : slice.apply(arguments));\n          } finally {\n            pending = false;\n          }\n        };\n      }\n    })(levels[i]);\n  }\n  /*eslint no-console: \"off\"*/\n  var onerror = function(message, filename, lineno, colno, error) {\n    if (error) {\n      wConsole.error(getErrorStack(error, message));\n    } else {\n      wConsole.error('Error: ' + message + '(' + filename\n          + ':' + lineno + ':' + (colno || 0) + ')' + getPageInfo());\n    }\n  };\n  var isWeinreConsole = function(curConsole) {\n    if (curConsole === console || typeof curConsole.log !== 'function') {\n      return;\n    }\n    return curConsole.log.toString().indexOf('this._generic(MessageLevel.Log,') !== -1;\n  };\n  var attachOnError = function() {\n    if (window.onerror !== onerror) {\n      window.onerror = onerror;\n    }\n    var curConsole = window.console;\n    if (!curConsole) {\n      window.console = console;\n    } else if ($INTERCEPT_CONSOLE && isWeinreConsole(curConsole)) {\n      for (var i = 0, len = levels.length; i < len; i++) {\n        var level = levels[i];\n        var fn = console[level];\n        var curFn = curConsole[level];\n        if (fn !== curFn) {\n          console['_weinre_' + level] = curFn;\n          curConsole[level] = fn;\n        }\n      }\n    }\n    setTimeout(attachOnError, 600);\n  };\n  attachOnError();\n\n  if (typeof  window.addEventListener === 'function') {\n    window.addEventListener('unhandledrejection', function(e) {\n      var reason = 'UnhandledRejection';\n      if (e) {\n         e = stringifyObj(e.reason || e) || String(e);\n         reason += (/^[\\w.-]*:/.test(e) ? ' ' : ': ') + e;\n      }\n      wConsole.error(reason);\n    });\n  }\n})();\n"
  },
  {
    "path": "assets/js/weinre.js",
    "content": "\n;(function() {\n  if (typeof window === 'undefined' || window.WeinreServerURL) {\n    return;\n  }\n  var prefixPath = window.__WHISTLE_PATH_PREFIX__;\n  if (/^\\/[\\w./-]+$/.test(prefixPath) && prefixPath.length <= 128) {\n    var len = prefixPath.length - 1;\n    if (prefixPath[len] === '/') {\n      prefixPath = prefixPath.substring(0, len);\n    }\n  } else {\n    prefixPath = '';\n  }\n  var baseUrl = '$BASE_URL' + prefixPath;\n  window.WeinreServerURL = baseUrl + '$WEINRE_PATH';\n  var head = document.head || document.getElementsByTagName('head')[0] || document.documentElement;\n  var script = document.createElement('script');\n  script.async = true;\n  script.charset = 'utf8';\n  script.src = baseUrl + '$WEINRE_URL';\n  if (head.firstChild) {\n    head.insertBefore(script, head.firstChild);\n  } else {\n    head.appendChild(script);\n  }\n})();\n"
  },
  {
    "path": "assets/js/worker.js",
    "content": "\nvar exports = {};\nvar module = { exports: exports };\n\n;(function() {\n  var self = null;\n  try {\n    (function() {\n      /*sourcecode*/\n    })();\n  } catch (e) {\n    setTimeout(function() {\n      throw e;\n    }, 20);\n  }\n})();\n\n;(function() {\n  !function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p=\"\",t(0)}([function(e,t,n){\"use strict\";function r(e){try{return i(e)}catch(t){}}var o,i=n(1).toByteArray,a=n(2).Base64.decode,s=n(3);if(self.TextDecoder)try{o=new self.TextDecoder(\"GB18030\")}catch(l){}self.getText=function(e){var t=e&&r(e);if(!t)return\"\";if(!s(t))try{if(o)return o.decode(t)}catch(n){}try{return a(e)}catch(n){}return\"\"}},function(e,t){\"use strict\";function n(e){var t=e.length;if(t%4>0)throw new Error(\"Invalid string. Length must be a multiple of 4\");var n=e.indexOf(\"=\");-1===n&&(n=t);var r=n===t?0:4-n%4;return[n,r]}function r(e){var t=n(e),r=t[0],o=t[1];return 3*(r+o)/4-o}function o(e,t,n){return 3*(t+n)/4-n}function i(e){var t,r,i=n(e),a=i[0],s=i[1],l=new d(o(e,a,s)),c=0,p=s>0?a-4:a;for(r=0;p>r;r+=4)t=u[e.charCodeAt(r)]<<18|u[e.charCodeAt(r+1)]<<12|u[e.charCodeAt(r+2)]<<6|u[e.charCodeAt(r+3)],l[c++]=t>>16&255,l[c++]=t>>8&255,l[c++]=255&t;return 2===s&&(t=u[e.charCodeAt(r)]<<2|u[e.charCodeAt(r+1)]>>4,l[c++]=255&t),1===s&&(t=u[e.charCodeAt(r)]<<10|u[e.charCodeAt(r+1)]<<4|u[e.charCodeAt(r+2)]>>2,l[c++]=t>>8&255,l[c++]=255&t),l}function a(e){return c[e>>18&63]+c[e>>12&63]+c[e>>6&63]+c[63&e]}function s(e,t,n){for(var r,o=[],i=t;n>i;i+=3)r=(e[i]<<16&16711680)+(e[i+1]<<8&65280)+(255&e[i+2]),o.push(a(r));return o.join(\"\")}function l(e){for(var t,n=e.length,r=n%3,o=[],i=16383,a=0,l=n-r;l>a;a+=i)o.push(s(e,a,a+i>l?l:a+i));return 1===r?(t=e[n-1],o.push(c[t>>2]+c[t<<4&63]+\"==\")):2===r&&(t=(e[n-2]<<8)+e[n-1],o.push(c[t>>10]+c[t>>4&63]+c[t<<2&63]+\"=\")),o.join(\"\")}t.byteLength=r,t.toByteArray=i,t.fromByteArray=l;for(var c=[],u=[],d=\"undefined\"!=typeof Uint8Array?Uint8Array:Array,p=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",f=0,g=p.length;g>f;++f)c[f]=p[f],u[p.charCodeAt(f)]=f;u[\"-\".charCodeAt(0)]=62,u[\"_\".charCodeAt(0)]=63},function(e,t,n){var r,o;(function(n){!function(t,n){e.exports=n(t)}(\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:\"undefined\"!=typeof n?n:this,function(n){\"use strict\";n=n||{};var i,a=n.Base64,s=\"2.6.4\",l=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",c=function(e){for(var t={},n=0,r=e.length;r>n;n++)t[e.charAt(n)]=n;return t}(l),u=String.fromCharCode,d=function(e){if(e.length<2){var t=e.charCodeAt(0);return 128>t?e:2048>t?u(192|t>>>6)+u(128|63&t):u(224|t>>>12&15)+u(128|t>>>6&63)+u(128|63&t)}var t=65536+1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320);return u(240|t>>>18&7)+u(128|t>>>12&63)+u(128|t>>>6&63)+u(128|63&t)},p=/[\\uD800-\\uDBFF][\\uDC00-\\uDFFFF]|[^\\x00-\\x7F]/g,f=function(e){return e.replace(p,d)},g=function(e){var t=[0,2,1][e.length%3],n=e.charCodeAt(0)<<16|(e.length>1?e.charCodeAt(1):0)<<8|(e.length>2?e.charCodeAt(2):0),r=[l.charAt(n>>>18),l.charAt(n>>>12&63),t>=2?\"=\":l.charAt(n>>>6&63),t>=1?\"=\":l.charAt(63&n)];return r.join(\"\")},h=n.btoa&&\"function\"==typeof n.btoa?function(e){return n.btoa(e)}:function(e){if(e.match(/[^\\x00-\\xFF]/))throw new RangeError(\"The string contains invalid characters.\");return e.replace(/[\\s\\S]{1,3}/g,g)},m=function(e){return h(f(String(e)))},A=function(e){return e.replace(/[+\\/]/g,function(e){return\"+\"==e?\"-\":\"_\"}).replace(/=/g,\"\")},M=function(e,t){return t?A(m(e)):m(e)},w=function(e){return M(e,!0)};n.Uint8Array&&(i=function(e,t){for(var n=\"\",r=0,o=e.length;o>r;r+=3){var i=e[r],a=e[r+1],s=e[r+2],c=i<<16|a<<8|s;n+=l.charAt(c>>>18)+l.charAt(c>>>12&63)+(\"undefined\"!=typeof a?l.charAt(c>>>6&63):\"=\")+(\"undefined\"!=typeof s?l.charAt(63&c):\"=\")}return t?A(n):n});var b,y=/[\\xC0-\\xDF][\\x80-\\xBF]|[\\xE0-\\xEF][\\x80-\\xBF]{2}|[\\xF0-\\xF7][\\x80-\\xBF]{3}/g,v=function(e){switch(e.length){case 4:var t=(7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3),n=t-65536;return u((n>>>10)+55296)+u((1023&n)+56320);case 3:return u((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return u((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},T=function(e){return e.replace(y,v)},x=function(e){var t=e.length,n=t%4,r=(t>0?c[e.charAt(0)]<<18:0)|(t>1?c[e.charAt(1)]<<12:0)|(t>2?c[e.charAt(2)]<<6:0)|(t>3?c[e.charAt(3)]:0),o=[u(r>>>16),u(r>>>8&255),u(255&r)];return o.length-=[0,0,2,1][n],o.join(\"\")},N=n.atob&&\"function\"==typeof n.atob?function(e){return n.atob(e)}:function(e){return e.replace(/\\S{1,4}/g,x)},C=function(e){return N(String(e).replace(/[^A-Za-z0-9\\+\\/]/g,\"\"))},I=function(e){return T(N(e))},E=function(e){return String(e).replace(/[-_]/g,function(e){return\"-\"==e?\"+\":\"/\"}).replace(/[^A-Za-z0-9\\+\\/]/g,\"\")},D=function(e){return I(E(e))};n.Uint8Array&&(b=function(e){return Uint8Array.from(C(E(e)),function(e){return e.charCodeAt(0)})});var L=function(){var e=n.Base64;return n.Base64=a,e};if(n.Base64={VERSION:s,atob:C,btoa:h,fromBase64:D,toBase64:M,utob:f,encode:M,encodeURI:w,btou:T,decode:D,noConflict:L,fromUint8Array:i,toUint8Array:b},\"function\"==typeof Object.defineProperty){var S=function(e){return{value:e,enumerable:!1,writable:!0,configurable:!0}};n.Base64.extendString=function(){Object.defineProperty(String.prototype,\"fromBase64\",S(function(){return D(this)})),Object.defineProperty(String.prototype,\"toBase64\",S(function(e){return M(this,e)})),Object.defineProperty(String.prototype,\"toBase64URI\",S(function(){return M(this,!0)}))}}return n.Meteor&&(Base64=n.Base64),\"undefined\"!=typeof e&&e.exports?e.exports.Base64=n.Base64:(r=[],o=function(){return n.Base64}.apply(t,r),!(void 0!==o&&(e.exports=o))),{Base64:n.Base64}})}).call(t,function(){return this}())},function(e,t){\"use strict\";function n(e,t){t=t||0;for(var n=Math.min(e.length,r);n>t;t++){var o=e[t];if(!(9==o||10==o||13==o||o>=32&&127>=o)){++t;var i=e[t];if(o>=194&&223>=o){if(i>=128&&191>=i)continue;return!i}++t;var a=e[t];if(224==o){if(i>=160&&191>=i&&a>=128&&191>=a)continue;return!a}if(o>=225&&236>=o||238==o||239==o){if(i>=128&&191>=i&&a>=128&&191>=a)continue;return!a}if(237==o){if(i>=128&&159>=i&&a>=128&&191>=a)continue;return!a}++t;var s=e[t];if(240==o){if(i>=144&&191>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}if(o>=241&&243>=o){if(i>=128&&191>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}if(244==o){if(i>=128&&143>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}return!1}}return!0}var r=32768;e.exports=function(e){return n(e)?!0:0===e[0]&&n(e,5)}}]);\n\n  var getText = function(obj) {\n    return self.getText(obj.base64);\n  };\n  var parseJson = function(str) {\n    if (!str || !/^(?:\\{[\\s\\S]*\\}|\\[[\\s\\S]*\\])$/) {\n      return;\n    }\n    try {\n      return JSON.parse(str);\n    } catch (e) {}\n  };\n\n  var defineProps = function (obj) {\n    if (Object.defineProperties) {\n      var body, json;\n      var getBody = function () {\n        if (body === undefined) {\n          body = getText(obj);\n        }\n        return body;\n      };\n      Object.defineProperties(obj, {\n        body: {  get: getBody  },\n        json: {\n          get: function () {\n            if (json === undefined) {\n              json = parseJson(getBody()) || null;\n            }\n            return json;\n          }\n        }\n      });\n    } else {\n      obj.body = getText(obj) || '';\n      obj.json = parseJson(obj) || null;\n    }\n    return obj;\n  };\n  var handle = module.exports;\n  if (typeof handle !== 'function') {\n    handle = null;\n  }\n\n  self.onmessage = function(e) {\n    var data = handle && e.data;\n    var id = data && data.id;\n    if (!id) {\n      return;\n    }\n    data.res = defineProps(data.res || {});\n    data.req = defineProps(data.req || {});\n    data.req.headers = data.req.headers || {};\n    data.res.headers = data.res.headers || {};\n    handle(data, function(result) {\n      result && self.postMessage({\n        id: id,\n        plugin: '$PLUGIN_NAME',\n        data: result\n      });\n    });\n  };\n})();\n\nself.postMessage(true);\n\n"
  },
  {
    "path": "assets/menu.html",
    "content": "<style>html {background: #fff;}</style>\n<script>\n  ;(function() {\n    var config = window.whistleMenuConfig;\n    var list = [];\n    var networkListeners = [];\n    var rulesListeners = [];\n    var valuesListeners = [];\n    var pluginsListeners = [];\n    var handleOnLoad = function() {\n      timer = null;\n      list.forEach(emit);\n      list = null;\n    };\n    var timer = setTimeout(handleOnLoad, 12000);\n\n    function execListeners(listeners, options) {\n      listeners.forEach(function(fn) {\n        try {\n          fn(options)\n        } catch (e) {\n          console.error(e);\n        }\n      });\n    }\n\n    function emit(options) {\n      switch(options.type) {\n        case 'rules':\n          execListeners(rulesListeners, options);\n          break;\n        case 'values':\n          execListeners(valuesListeners, options);\n          break;\n        case 'plugins':\n          execListeners(pluginsListeners, options);\n          break;\n        default:\n          execListeners(networkListeners, options);\n      }\n    }\n    window.onWhistleContextMenuClick = function(options) {\n      if (list) {\n        list.push(options);\n      } else {\n        emit(options);\n      }\n    };\n    var delayOnLoad = function() {\n      if (timer) {\n        setTimeout(handleOnLoad, 100);\n        clearTimeout(timer);\n        timer = null;\n      }\n    };\n    window.onload = delayOnLoad;\n    try {\n      window.addEventListener('load', delayOnLoad);\n    } catch (e) {}\n    var toast = {};\n    var addNetworkListener = function(l) {\n      if (typeof l === 'function' && networkListeners.indexOf(l) === -1) {\n        networkListeners.push(l);\n      }\n    };\n    var removeNetworkListener = function(l) {\n      l = networkListeners.indexOf(l);\n      if (l !== -1) {\n        networkListeners.splice(l, 1);\n      }\n    };\n    var removeAllNetworkListeners = function() {\n      networkListeners = [];\n    };\n    var addRulesListener = function(l) {\n      if (typeof l === 'function' && rulesListeners.indexOf(l) === -1) {\n        rulesListeners.push(l);\n      }\n    };\n    var removeRulesListener = function(l) {\n      l = rulesListeners.indexOf(l);\n      if (l !== -1) {\n        rulesListeners.splice(l, 1);\n      }\n    };\n    var removeAllRulesListeners = function() {\n      rulesListeners = [];\n    };\n    var addValuesListener = function(l) {\n      if (typeof l === 'function' && valuesListeners.indexOf(l) === -1) {\n        valuesListeners.push(l);\n      }\n    };\n    var removeValuesListener = function(l) {\n      l = valuesListeners.indexOf(l);\n      if (l !== -1) {\n        valuesListeners.splice(l, 1);\n      }\n    };\n    var removeAllValuesListeners = function() {\n      valuesListeners = [];\n    };\n    var addPluginsListener = function(l) {\n      if (typeof l === 'function' && pluginsListeners.indexOf(l) === -1) {\n        pluginsListeners.push(l);\n      }\n    };\n    var removePluginsListener = function(l) {\n      l = pluginsListeners.indexOf(l);\n      if (l !== -1) {\n        pluginsListeners.splice(l, 1);\n      }\n    };\n    var removeAllPluginsListeners = function(l) {\n      pluginsListeners = [];\n    };\n\n    function on(type, l) {\n      if (typeof l !== 'function') {\n        return;\n      }\n      switch(type) {\n        case 'network':\n          return addNetworkListener(l);\n        case 'rules':\n          return addRulesListener(l);\n        case 'values':\n          return addValuesListener(l);\n        case 'plugins':\n          return addPluginsListener(l);\n      }\n    }\n\n    function off(type, l) {\n      switch(type) {\n        case 'network':\n          if (l) {\n            removeNetworkListener(l);\n          } else {\n            removeAllNetworkListeners();\n          }\n          return;\n        case 'rules':\n          if (l) {\n            removeRulesListener(l);\n          } else {\n            removeAllRulesListeners();\n          }\n          return;\n        case 'values':\n          if (l) {\n            removeValuesListener(l);\n          } else {\n            removeAllValuesListeners();\n          }\n          return;\n        case 'plugins':\n          if (l) {\n            removePluginsListener(l);\n          } else {\n            removeAllPluginsListeners();\n          }\n          return;\n      }\n    }\n\n    var whistleBridge = {\n      config: config,\n      toast: toast,\n      on: on,\n      off: off,\n      addNetworkListener: addNetworkListener,\n      removeNetworkListener: removeNetworkListener,\n      removeAllNetworkListeners: removeAllNetworkListeners,\n      addRulesListener: addRulesListener,\n      removeRulesListener: removeRulesListener,\n      removeAllRulesListeners: removeAllRulesListeners,\n      addValuesListener: addValuesListener,\n      removeValuesListener: removeValuesListener,\n      removeAllValuesListeners: removeAllValuesListeners,\n      addPluginsListener: addPluginsListener,\n      removePluginsListener: removePluginsListener,\n      removeAllPluginsListeners: removeAllPluginsListeners\n    };\n    try {\n      window.initWhistleBridge = function(options) {\n        window.initWhistleBridge = function() {};\n        Object.keys(options.msgBox).forEach(function(name) {\n          toast[name] = options.msgBox[name];\n        });\n        whistleBridge.getSelectedSessionList = options.getSelectedSessionList;\n        whistleBridge.getActiveSession = whistleBridge.getSession = whistleBridge.getSelectedSession = options.getActiveSession;\n        whistleBridge.showOption = options.showOption;\n        whistleBridge.hideOption = options.hideOption;\n        whistleBridge.updateUI = options.updateUI;\n        whistleBridge.copyText = options.copyText;\n        whistleBridge.pageId = options.pageId;\n        whistleBridge.compose = options.compose;\n        whistleBridge.composeInterrupt = options.createComposeInterrupt();\n        whistleBridge.getWhistleId = options.getWhistleId;\n        whistleBridge.hasWhistleToken = options.hasWhistleToken;\n        whistleBridge.decodeBase64 = options.decodeBase64;\n        whistleBridge.joinBase64 = options.joinBase64;\n        whistleBridge.getReqId = options.getReqId;\n        whistleBridge.onComposeData = options.onComposeData;\n        whistleBridge.offComposeData = options.offComposeData;\n        whistleBridge.importSessions = options.importSessions;\n        whistleBridge.exportSessions = options.exportSessions;\n        whistleBridge.importMockData = options.importMockData;\n        whistleBridge.download = options.download;\n        whistleBridge.setNetworkSettings = options.setNetworkSettings;\n        whistleBridge.setRulesSettings = options.setRulesSettings;\n        whistleBridge.setValuesSettings = options.setValuesSettings;\n        whistleBridge.setComposerData = options.setComposerData;\n        whistleBridge.showHttpsSettings = options.showHttpsSettings;\n        whistleBridge.showCustomCerts = options.showCustomCerts;\n        whistleBridge.uploadCustomCerts = options.uploadCustomCerts;\n        whistleBridge.showNetwork = options.showNetwork;\n        whistleBridge.showRules = options.showRules;\n        whistleBridge.showValues = options.showValues;\n        whistleBridge.showPlugins = options.showPlugins;\n        whistleBridge.showService = options.showService;\n        whistleBridge.hideService = options.hideService;\n        whistleBridge.getInstalledPlugins = options.getInstalledPlugins;\n        whistleBridge.showInstallPlugins = options.showInstallPlugins;\n        whistleBridge.showUpdatePlugins = options.showUpdatePlugins;\n        whistleBridge.readFileAsText = options.readFileAsText;\n        whistleBridge.readFileAsBase64 = options.readFileAsBase64;\n        whistleBridge.getVersion = options.getVersion;\n        whistleBridge.request = options.request;\n        whistleBridge.createRequest = options.createRequest;\n        whistleBridge.parseRules = options.parseRules;\n        whistleBridge.showModal = options.showModal;\n        whistleBridge.createModal = options.createModal;\n        whistleBridge.importRules = options.importRules;\n        whistleBridge.importValues = options.importValues;\n        whistleBridge.getServerInfo = options.getServerInfo;\n        whistleBridge.alert = options.alert;\n        whistleBridge.confirm = options.confirm;\n        whistleBridge.syncData = options.syncData;\n        whistleBridge.syncRules = options.syncRules;\n        whistleBridge.syncValues = options.syncValues;\n      };\n      window.parent.onPluginContextMenuReady(window);\n    } catch (e) {}\n    window.whistleBridge = whistleBridge;\n  })();\n</script>\n"
  },
  {
    "path": "assets/modal.html",
    "content": "<!DOCTYPE html>\n<style>html {background: #fff;}</style>\n<script>\n  ;(function() {\n    var toast = {};\n    var whistleBridge = {\n      toast: toast\n    };\n    try {\n      window.parent.onWhistlePluginOptionModalReady(function(options) {\n        Object.keys(options.msgBox).forEach(function(name) {\n          toast[name] = options.msgBox[name];\n        });\n        whistleBridge.getSelectedSessionList = options.getSelectedSessionList;\n        whistleBridge.getActiveSession = whistleBridge.getSession = whistleBridge.getSelectedSession = options.getActiveSession;\n        whistleBridge.showOption = options.showOption;\n        whistleBridge.hideOption = options.hideOption;\n        whistleBridge.updateUI = options.updateUI;\n        whistleBridge.copyText = options.copyText;\n        whistleBridge.pageId = options.pageId;\n        whistleBridge.compose = options.compose;\n        whistleBridge.composeInterrupt = options.createComposeInterrupt();\n        whistleBridge.getWhistleId = options.getWhistleId;\n        whistleBridge.hasWhistleToken = options.hasWhistleToken;\n        whistleBridge.decodeBase64 = options.decodeBase64;\n        whistleBridge.joinBase64 = options.joinBase64;\n        whistleBridge.getReqId = options.getReqId;\n        whistleBridge.onComposeData = options.onComposeData;\n        whistleBridge.offComposeData = options.offComposeData;\n        whistleBridge.importSessions = options.importSessions;\n        whistleBridge.exportSessions = options.exportSessions;\n        whistleBridge.importMockData = options.importMockData;\n        whistleBridge.download = options.download;\n        whistleBridge.setNetworkSettings = options.setNetworkSettings;\n        whistleBridge.setRulesSettings = options.setRulesSettings;\n        whistleBridge.setValuesSettings = options.setValuesSettings;\n        whistleBridge.setComposerData = options.setComposerData;\n        whistleBridge.showHttpsSettings = options.showHttpsSettings;\n        whistleBridge.showCustomCerts = options.showCustomCerts;\n        whistleBridge.uploadCustomCerts = options.uploadCustomCerts;\n        whistleBridge.showNetwork = options.showNetwork;\n        whistleBridge.showRules = options.showRules;\n        whistleBridge.showValues = options.showValues;\n        whistleBridge.showPlugins = options.showPlugins;\n        whistleBridge.showService = options.showService;\n        whistleBridge.hideService = options.hideService;\n        whistleBridge.getInstalledPlugins = options.getInstalledPlugins;\n        whistleBridge.showInstallPlugins = options.showInstallPlugins;\n        whistleBridge.showUpdatePlugins = options.showUpdatePlugins;\n        whistleBridge.readFileAsText = options.readFileAsText;\n        whistleBridge.readFileAsBase64 = options.readFileAsBase64;\n        whistleBridge.getVersion = options.getVersion;\n        whistleBridge.request = options.request;\n        whistleBridge.createRequest = options.createRequest;\n        whistleBridge.parseRules = options.parseRules;\n        whistleBridge.showModal = options.showModal;\n        whistleBridge.importRules = options.importRules;\n        whistleBridge.importValues = options.importValues;\n        whistleBridge.getServerInfo = options.getServerInfo;\n        whistleBridge.alert = options.alert;\n        whistleBridge.confirm = options.confirm;\n        whistleBridge.syncData = options.syncData;\n        whistleBridge.syncRules = options.syncRules;\n        whistleBridge.syncValues = options.syncValues;\n      }, window);\n    } catch (e) {}\n    window.whistleBridge = whistleBridge;\n  })();\n</script>\n"
  },
  {
    "path": "assets/tab.html",
    "content": "<style>html {background: #fff;}</style>\n<script>\n  ;(function() {\n    var config = window.whistleInspectorConfig;\n    var activeList = [];\n    var stateList = [1];\n    var getActiveSession;\n    var getSelectedSessionList;\n    var listenersList = [];\n    var frameChangeListeners = [];\n    var composeListeners = [];\n    var tabChangeListeners = [];\n    var curFrames = null;\n    var firstFrame;\n    var lastFrame;\n    var selected = true;\n    var composerItem;\n    var frameTimer;\n\n    function initState(flag) {\n      stateList[1] = flag;\n      stateList[2] = flag;\n      stateList[3] = flag;\n    }\n\n    function updateState(item) {\n      if (!item || item.lost || item.endTime) {\n        return  initState(1);\n      }\n      initState();\n      if (item.responseTime) {\n        stateList[2] = 1;\n      }\n      if (item.requestTime) {\n        stateList[1] = 1;\n      }\n    }\n\n    function emitTabChange(_selected) {\n      if (selected !== _selected) {\n        selected = _selected;\n        tabChangeListeners.forEach(function(l) {\n          l(selected);\n        });\n      }\n    }\n\n    function emitFrameChange() {\n      var curItem = getActiveSession();\n      var frames = curItem && curItem.frames || null;\n      var hasChanged;\n      var listenersLen = frameChangeListeners.length;\n      if (curFrames !== frames) {\n        curFrames = frames;\n        hasChanged = listenersLen;\n      } else if (curFrames && listenersLen) {\n        var curLen = curFrames.length;\n        var len = frames.length;\n        hasChanged = curLen !== len || firstFrame != frames[0] || lastFrame != frames[len - 1];\n      }\n      if (hasChanged) {\n        if (curFrames) {\n          firstFrame = curFrames[0];\n          lastFrame = curFrames[curFrames.length - 1];\n        } else {\n          firstFrame = lastFrame = undefined;\n        }\n        frameChangeListeners.forEach(function(l) {\n          l(curFrames);\n        });\n      }\n      frameTimer = setTimeout(emitFrameChange, 1000);\n    }\n\n    function emitListeners(item, state) {\n      if (stateList[state] && item !== activeList[state]) {\n        activeList[state] = item;\n        var listeners = listenersList[state];\n        listeners.forEach(function(l) {\n          l(item);\n        });\n      }\n    }\n\n    function emitAll(item, hide, comItem) {\n      if (comItem) {\n        composerItem = comItem;\n        composeListeners.forEach(function(l) {\n          l(comItem);\n        });\n        return;\n      }\n      if (frameTimer) {\n        clearTimeout(frameTimer);\n        frameTimer = null;\n      }\n      if (hide) {\n        return emitTabChange(false);\n      }\n      emitTabChange(true);\n      updateState(item);\n      emitListeners(item, 0);\n      emitListeners(item, 1);\n      emitListeners(item, 2);\n      emitListeners(item, 3);\n      emitFrameChange();\n    }\n\n    window.__pushWhistle5b6af7b9884e1165SessionActive__ = emitAll;\n\n    function getAddEventHandler(state) {\n      state = state || 0;\n      listenersList[state] = [];\n      return function(l) {\n        if (typeof l !== 'function') {\n          return;\n        }\n        var item = getActiveSession();\n        emitAll(item);\n        activeList[state] = item;\n        if (stateList[state]) {\n          l(item);\n        }\n        var listeners = listenersList[state];\n        listeners.indexOf(l) === -1 && listeners.push(l);\n      };\n    }\n\n    function getRemoveEventListener(state) {\n      state = state || 0;\n      return function(l) {\n        var listeners = listenersList[state];\n        var index = listeners.indexOf(l);\n        index !== -1 && listeners.splice(index, 1);\n      };\n    }\n\n    function getRemoveEventListeners(state) {\n      state = state || 0;\n      return function(l) {\n        listenersList[state] = [];\n      };\n    }\n\n    var toast = {};\n    var addSessionActiveListener = getAddEventHandler();\n    var removeSessionActiveListener = getRemoveEventListener();\n    var removeSessionActiveListeners = getRemoveEventListeners();\n    var addSessionRequestListener = getAddEventHandler(1);\n    var removeSessionRequestListener = getRemoveEventListener(1);\n    var removeSessionRequestListeners = getRemoveEventListeners(1);\n    var addSessionResponseListener = getAddEventHandler(2);\n    var removeSessionResponseListener = getRemoveEventListener(2);\n    var removeSessionResponseListeners = getRemoveEventListeners(2);\n    var addSessionCompleteListener = getAddEventHandler(3);\n    var removeSessionCompleteListener = getRemoveEventListener(3);\n    var removeSessionCompleteListeners = getRemoveEventListeners(3);\n\n    function on(type, l) {\n      if (typeof l !== 'function') {\n        return;\n      }\n      switch(type) {\n        case 'sessionActive':\n          return addSessionActiveListener(l);\n        case 'sessionRequest':\n          return addSessionRequestListener(l);\n        case 'sessionResponse':\n          return addSessionResponseListener(l);\n        case 'sessionComplete':\n          return addSessionCompleteListener(l);\n        case 'tabChange':\n          if (tabChangeListeners.indexOf(l) === -1) {\n            tabChangeListeners.push(l);\n          }\n          return;\n        case 'frameChange':\n          if (frameChangeListeners.indexOf(l) === -1) {\n            frameChangeListeners.push(l);\n          }\n          return;\n        case 'compose':\n          if (composeListeners.indexOf(l) === -1) {\n            composeListeners.push(l);\n          }\n           composerItem && l(composerItem);\n          return;\n      }\n    }\n\n    function off(type, l) {\n      switch(type) {\n        case 'sessionActive':\n          if (l) {\n            removeSessionActiveListener(l);\n          } else {\n            removeSessionActiveListeners();\n          }\n          return;\n        case 'sessionRequest':\n          if (l) {\n            removeSessionRequestListener(l);\n          } else {\n            removeSessionRequestListeners();\n          }\n          return;\n        case 'sessionResponse':\n          if (l) {\n            removeSessionResponseListener(l);\n          } else {\n            removeSessionResponseListeners();\n          }\n          return;\n        case 'sessionComplete':\n          if (l) {\n            removeSessionCompleteListener(l);\n          } else {\n            removeSessionCompleteListeners();\n          }\n          return;\n        case 'tabChange':\n          if (l) {\n            var index = tabChangeListeners.indexOf(l);\n            if (index !== -1) {\n              tabChangeListeners.splice(index, 1);\n            }\n          } else {\n            tabChangeListeners = [];\n          }\n          return;\n        case 'frameChange':\n          if (l) {\n            var index = frameChangeListeners.indexOf(l);\n            if (index !== -1) {\n              frameChangeListeners.splice(index, 1);\n            }\n          } else {\n            frameChangeListeners = [];\n          }\n          return;\n        case 'compose':\n          if (l) {\n            var index = composeListeners.indexOf(l);\n            if (index !== -1) {\n              composeListeners.splice(index, 1);\n            }\n          } else {\n            composeListeners = [];\n          }\n          return;\n      }\n    }\n\n    var whistleBridge = {\n      config: config,\n      toast: toast,\n      on: on,\n      off: off,\n      isSelected: function() {\n        return selected;\n      },\n      getFrames: function() {\n        return curFrames;\n      },\n      addSessionActiveListener: addSessionActiveListener,\n      removeSessionActiveListener: removeSessionActiveListener,\n      removeSessionActiveListeners: removeSessionActiveListeners,\n      addSessionRequestListener: addSessionRequestListener,\n      removeSessionRequestListener: removeSessionRequestListener,\n      removeSessionRequestListeners: removeSessionRequestListeners,\n      addSessionResponseListener: addSessionResponseListener,\n      removeSessionResponseListener: removeSessionResponseListener,\n      removeSessionResponseListeners: removeSessionResponseListeners,\n      addSessionCompleteListener: addSessionCompleteListener,\n      removeSessionCompleteListener: removeSessionCompleteListener,\n      removeSessionCompleteListeners: removeSessionCompleteListeners\n    };\n\n    try {\n      window.parent.onWhistleInspectorCustomTabReady(function(options) {\n        Object.keys(options.msgBox).forEach(function(name) {\n          toast[name] = options.msgBox[name];\n        });\n        getActiveSession = options.getActiveSession;\n        getSelectedSessionList = options.getSelectedSessionList;\n        whistleBridge.showOption = options.showOption;\n        whistleBridge.hideOption = options.hideOption;\n        whistleBridge.updateUI = options.updateUI;\n        whistleBridge.copyText = options.copyText;\n        whistleBridge.pageId = options.pageId;\n        whistleBridge.compose = options.compose;\n        whistleBridge.composeInterrupt = options.createComposeInterrupt();\n        whistleBridge.getWhistleId = options.getWhistleId;\n        whistleBridge.hasWhistleToken = options.hasWhistleToken;\n        whistleBridge.decodeBase64 = options.decodeBase64;\n        whistleBridge.joinBase64 = options.joinBase64;\n        whistleBridge.getReqId = options.getReqId;\n        whistleBridge.onComposeData = options.onComposeData;\n        whistleBridge.offComposeData = options.offComposeData;\n        whistleBridge.importSessions = options.importSessions;\n        whistleBridge.exportSessions = options.exportSessions;\n        whistleBridge.importMockData = options.importMockData;\n        whistleBridge.download = options.download;\n        whistleBridge.setNetworkSettings = options.setNetworkSettings;\n        whistleBridge.setRulesSettings = options.setRulesSettings;\n        whistleBridge.setValuesSettings = options.setValuesSettings;\n        whistleBridge.setComposerData = options.setComposerData;\n        whistleBridge.showHttpsSettings = options.showHttpsSettings;\n        whistleBridge.showCustomCerts = options.showCustomCerts;\n        whistleBridge.uploadCustomCerts = options.uploadCustomCerts;\n        whistleBridge.showNetwork = options.showNetwork;\n        whistleBridge.showRules = options.showRules;\n        whistleBridge.showValues = options.showValues;\n        whistleBridge.showPlugins = options.showPlugins;\n        whistleBridge.showService = options.showService;\n        whistleBridge.hideService = options.hideService;\n        whistleBridge.getInstalledPlugins = options.getInstalledPlugins;\n        whistleBridge.showInstallPlugins = options.showInstallPlugins;\n        whistleBridge.showUpdatePlugins = options.showUpdatePlugins;\n        whistleBridge.readFileAsText = options.readFileAsText;\n        whistleBridge.readFileAsBase64 = options.readFileAsBase64;\n        whistleBridge.getVersion = options.getVersion;\n        whistleBridge.request = options.request;\n        whistleBridge.createRequest = options.createRequest;\n        whistleBridge.parseRules = options.parseRules;\n        whistleBridge.showModal = options.showModal;\n        whistleBridge.getSelectedSessionList = getSelectedSessionList;\n        whistleBridge.getActiveSession = whistleBridge.getSession = whistleBridge.getSelectedSession = getActiveSession;\n        whistleBridge.importRules = options.importRules;\n        whistleBridge.importValues = options.importValues;\n        whistleBridge.getServerInfo = options.getServerInfo;\n        whistleBridge.alert = options.alert;\n        whistleBridge.confirm = options.confirm;\n        whistleBridge.syncData = options.syncData;\n        whistleBridge.syncRules = options.syncRules;\n        whistleBridge.syncValues = options.syncValues;\n      }, window);\n    } catch (e) {}\n    window.whistleBridge = whistleBridge;\n  })();\n</script>\n"
  },
  {
    "path": "bin/ca/cli.js",
    "content": "var net = require('net');\nvar fs = require('fs');\nvar path = require('path');\nvar installRootCA = require('./index');\nvar util = require('../util');\nvar fileMgr = require('../../lib/util/file-mgr');\nvar httpMgr = require('../../lib/util/http-mgr');\nvar commonUtil = require('../../lib/util/common');\nvar config = require('../../lib/config');\n\nvar NUM_RE = /^\\d+$/;\nvar HOST_SUFFIX_RE = /\\:(\\d+|auto)?$/;\nvar HOST_RE = /^[a-z\\d_-]+(?:\\.[a-z\\d_-]+)*$/i;\nvar MAX_LEN = 1024 * 1024;\n\nfunction installCert(certFile, url) {\n  try {\n    installRootCA(fileMgr.convertSlash(certFile));\n    util.info('Successfully installed Root CA from (' + (url || certFile) + ')');\n  } catch (e) {\n    util.error('Certificate installation failed: ' + e.message);\n  }\n}\n\nfunction install(addr, useDefault) {\n  if (addr.file) {\n    return installCert(addr.file);\n  }\n  addr.needRawData = true;\n  addr.maxLength = MAX_LEN;\n  addr.headers = {\n    'user-agent': config.appName + '/' + config.version\n  };\n  httpMgr.request(addr, function(err, body, res) {\n    if (err) {\n      if (useDefault && err.code === 'ECONNREFUSED') {\n        return installCert(path.join(commonUtil.getWhistlePath(), '.whistle/certs/root.crt'));\n      }\n      return util.error(err.message);\n    }\n    if (res.statusCode != 200) {\n      return util.error('Bad response (' + res.statusCode + ')');\n    }\n    if (!body || !body.length) {\n      return util.error('Empty certificate content');\n    }\n    var tempFile = path.join(commonUtil.getWhistlePath(), Date.now() + '-' + util.getHash(addr.url) + '.crt');\n    fs.writeFileSync(tempFile, body);\n    installCert(tempFile, addr.url);\n    fs.unlinkSync(tempFile);\n  });\n}\n\nmodule.exports = function(argv) {\n  var options = {};\n  argv.forEach(function(arg) {\n    if (NUM_RE.test(arg)) {\n      delete options.addr;\n      options.port = parseInt(arg, 10) || options.port;\n    } else if (net.isIP(arg)) {\n      delete options.addr;\n      options.host = arg || options.host;\n    } else if (HOST_SUFFIX_RE.test(arg)) {\n      delete options.port;\n      delete options.addr;\n      var port = RegExp.$1;\n      if (port > 0) {\n        options.port = parseInt(port, 10) || options.port;\n      }\n      var host = arg.slice(0, - port.length - 1);\n      if (host[0] === '[') {\n        host = host.substring(1);\n      }\n      var lastIndex = host.length - 1;\n      if (host[lastIndex] === ']') {\n        host = host.substring(0, lastIndex);\n      }\n      if (host && (net.isIP(host) || HOST_RE.test(host))) {\n        options.host = host || options.host;\n      }\n    } else {\n      delete options.port;\n      delete options.host;\n      if (commonUtil.isUrl(arg)) {\n        options.addr = { url: arg };\n      } else {\n        options.addr = { file: arg };\n      }\n    }\n  });\n  if (!options.addr) {\n    var host = options.host || '127.0.0.1';\n    var port = options.port || util.getDefaultPort();\n    options.addr = { url: 'http://' + util.joinIpPort(host, + port) + '/cgi-bin/rootca' };\n  }\n  var url = options.addr && options.addr.url;\n  if (url) {\n    options.addr.url = url.replace(/#.*$/, '') + (url.indexOf('?') === -1 ? '?' : '&') + 'enableHttps=1';\n  }\n  install(options.addr, !options.host && !options.port);\n};\n"
  },
  {
    "path": "bin/ca/index.d.ts",
    "content": "export default function (certFile: String): void;\n"
  },
  {
    "path": "bin/ca/index.js",
    "content": "var cp = require('child_process');\nvar fs = require('fs');\nvar common = require('../../lib/util/common');\n\nvar spawnSync = cp.spawnSync;\nvar execSync = cp.execSync;\n\nfunction checkSuccess(result) {\n  var stderr = result.stderr;\n  if (stderr && stderr.length) {\n    throw new Error(stderr + '');\n  }\n}\n\nfunction getKeyChain() {\n  var result = spawnSync('security', ['default-keychain']);\n  checkSuccess(result);\n  return (result.stdout + '').split('\"')[1];\n}\n\nfunction installMac(certPath) {\n  var result = spawnSync('security', ['add-trusted-cert', '-k', getKeyChain(), certPath]);\n  checkSuccess(result);\n  var msg = result.stdout + '';\n  if (/Error:/i.test(msg)) {\n    throw new Error(msg);\n  }\n}\n\n\nfunction installWin(certFile) {\n  var result = spawnSync('certutil', ['-addstore', '-user', 'Root', certFile]);\n  checkSuccess(result);\n  if (/ERROR_CANCELLED/i.test(result.stdout + '')) {\n    throw new Error('The authorization was canceled by the user');\n  }\n}\n\nvar UBUNTU_CA_DIR = '/usr/local/share/ca-certificates/';\nvar FEDORA_CA_DIR = '/etc/pki/ca-trust/source/anchors/';\n\nfunction getCAConfig() {\n  var stats = common.getStatSync(UBUNTU_CA_DIR);\n  if (stats && stats.isDirectory()) {\n    return {\n      dir: UBUNTU_CA_DIR,\n      cmd: 'update-ca-certificates'\n    };\n  }\n  return {\n    dir: FEDORA_CA_DIR,\n    cmd: 'update-ca-trust'\n  };\n}\n\nfunction installLinux(certFile, execFunc) {\n  var config = getCAConfig();\n  var certPem = fs.readFileSync(certFile);\n  var caFile = config.dir + 'whistle-' + common.createHash(certPem) + '.crt';\n  certFile = '\"' + certFile.replace(/\"/g, '\\\\\"') + '\"';\n  if (typeof execFunc === 'function') {\n    execFunc('cp ' + certFile + ' ' + caFile + '&&' + config.cmd);\n  } else {\n    execSync('sudo cp ' + certFile + ' ' + caFile + '&&sudo ' + config.cmd);\n  }\n  return true;\n}\n\nmodule.exports = function(certFile, execFunc) {\n  var platform = process.platform;\n  if (platform === 'darwin') {\n    return installMac(certFile);\n  }\n  if (platform === 'win32') {\n    return installWin(certFile);\n  }\n  if (platform === 'linux') {\n    return installLinux(certFile, execFunc);\n  }\n  throw new Error('Platform ' + platform + ' is currently unsupported for Root CA installation');\n};\n"
  },
  {
    "path": "bin/import.js",
    "content": "var fs = require('fs');\n\nfunction importModle(filepath, callback) {\n  return import(filepath).then(callback);\n}\n\nmodule.exports = function(filepath, callback) {\n  try {\n    return callback(require(filepath));\n  } catch (e) {\n    var code =e && e.code;\n    if (code === 'ERR_REQUIRE_ESM') {\n      // ignore eslint & fix type=module\n      return importModle(filepath, callback);\n    } else if (code === 'MODULE_NOT_FOUND' && /\\.js$/i.test(filepath)) {\n      filepath = filepath.slice(0, -3) + '.mjs';\n      if (fs.existsSync(filepath)) {\n        return importModle(filepath, callback);\n      }\n    }\n    throw e;\n  }\n};\n"
  },
  {
    "path": "bin/plugin.d.ts",
    "content": "\nexport function getWhistlePath(): string;\n\nexport function install(cmd: string, argv: string[]): void;\n\nexport function uninstall(argv: string[]): void;\n\nexport function formatCmdOptions(options: any): any;\n"
  },
  {
    "path": "bin/plugin.js",
    "content": "var cp = require('child_process');\nvar fs = require('fs');\nvar path = require('path');\nvar fse = require('fs-extra2');\nvar getHash = require('./util').getHash;\nvar common = require('../lib/util/common');\n\nvar getWhistlePath = common.getWhistlePath;\nvar WHISTLE_PLUGIN_RE = common.WHISTLE_PLUGIN_RE;\nvar getPlugins = common.getPlugins;\nvar CMD_SUFFIX = process.platform === 'win32' ? '.cmd' : '';\nvar CUSTOM_PLUGIN_PATH = process.env.WHISTLE_CUSTOM_PLUGINS_PATH || path.join(getWhistlePath(), 'custom_plugins');\nvar DEFAULT_PATH = common.getDefaultWhistlePath();\nvar REGISTRY_LIST = path.join(DEFAULT_PATH, '.registry.list');\nvar PACKAGE_JSON = '{\"repository\":\"https://github.com/avwo/whistle\",\"license\":\"MIT\"}';\nvar LICENSE = 'Copyright (c) 2019 avwo';\nvar RESP_URL = 'https://github.com/avwo/whistle';\nvar MAX_REG_COUNT = 100;\n\nfunction getInstallPath(name, dir) {\n  return path.join(dir || CUSTOM_PLUGIN_PATH, name);\n}\n\nfunction removeDirSync(installPath) {\n  try {\n    fse.removeSync(installPath);\n  } catch (e) {}\n}\n\nfunction removeTempFiles(argv) {\n  argv.forEach(function(file) {\n    if (file.indexOf('file:') === 0) {\n      fs.unlink(file.substring(5), common.noop);\n    }\n  });\n}\n\nfunction renameDirSync(installPath, realPath) {\n  fse.ensureDirSync(realPath);\n  fse.copySync(installPath, realPath);\n  removeDirSync(installPath);\n}\n\nfunction getTempName(name) {\n  if (name.indexOf('/') === -1) {\n    return '.' + name;\n  }\n  name = name.split('/');\n  var lastIndex = name.length - 1;\n  name[lastIndex] = '.' + name[lastIndex];\n  return name.join('/');\n}\n\nfunction formatCmdOptions(options) {\n  if (CMD_SUFFIX) {\n    options.shell = true;\n    options.windowsHide = true;\n  }\n  return options;\n}\n\nexports.formatCmdOptions = formatCmdOptions;\n\nfunction getValue(str) {\n  if (str[0] !== '\"') {\n    return str;\n  }\n  var len = str.length - 1;\n  return str[len] === '\"' ? str.substring(1, len) : str;\n}\n\nfunction parseDir(dir) {\n  if (!dir) {\n    return;\n  }\n  dir = getValue(dir);\n  if (/^~(~)?(?:$|[\\/])/.test(dir)) {\n    var wave = RegExp.$1;\n    var all = RegExp['$&'];\n    return path.resolve(wave ? getWhistlePath() : common.getHomedir(), dir.substring(all.length));\n  }\n  if (common.isWhistleName(dir)) {\n    return path.resolve(getWhistlePath(), 'all_whistles/' + dir + '/custom_plugins');\n  }\n  return path.resolve(dir);\n}\n\nfunction getInstallDir(argv) {\n  argv = argv.slice();\n  var result = { argv: argv };\n  for (var i = 0, len = argv.length; i < len; i++) {\n    var option = argv[i];\n    if (option === '--dir') {\n      result.dir = parseDir(argv[i + 1]);\n      argv.splice(i, 2);\n      return result;\n    }\n    if (option && option.indexOf('--dir=') === 0) {\n      var dir = option.substring(option.indexOf('=') + 1);\n      result.dir = parseDir(dir);\n      argv.splice(i, 1);\n      return result;\n    }\n  }\n  return result;\n}\n\nfunction getPluginNameFormDeps(deps) {\n  var keys = Object.keys(deps);\n  for (var i = 0, len = keys.length; i < len; i++) {\n    if (WHISTLE_PLUGIN_RE.test(keys[i])) {\n      return RegExp.$1;\n    }\n  }\n}\n\nfunction getPkgName(name) {\n  if (/[/\\\\](whistle\\.[a-z\\d_-]+)(?:\\.git)?$/.test(name)) {\n    return RegExp.$1;\n  }\n  return getHash(name);\n}\n\nfunction install(cmd, name, argv, ver, pluginsCache, callback, handleError) {\n  var result = getInstallDir(argv.slice());\n  var isPkg = WHISTLE_PLUGIN_RE.test(name);\n  var pkgName = isPkg ? name : getPkgName(name);\n  var installPath = getInstallPath(getTempName(pkgName), result.dir);\n  argv = result.argv;\n  fse.ensureDirSync(installPath);\n  fse.emptyDirSync(installPath);\n  var pkgJson = PACKAGE_JSON;\n  if (ver) {\n    pkgJson = pkgJson.replace(',', ',\"dependencies\":{\"' + name + '\":\"' + ver + '\"},');\n  }\n  fs.writeFileSync(path.join(installPath, 'package.json'), pkgJson);\n  fs.writeFileSync(path.join(installPath, 'LICENSE'), LICENSE);\n  fs.writeFileSync(path.join(installPath, 'README.md'), RESP_URL);\n  argv.unshift('install', name);\n  pluginsCache[pkgName] = 1;\n  var done;\n  var child = cp.spawn(cmd, argv, formatCmdOptions({\n    stdio: 'inherit',\n    cwd: installPath\n  }));\n  child.once('exit', function(code) {\n    !done && handleError && removeTempFiles(argv);\n    if (code) {\n      if (done) {\n        return;\n      }\n      done = true;\n      removeDirSync(installPath);\n      callback();\n      handleError && handleError('Install plugin \\'' + name + '\\' failed with code: ' + code);\n    } else {\n      done = true;\n      if (!isPkg) {\n        var deps = common.readJsonSync(path.join(installPath, 'package.json'));\n        deps = deps && deps.dependencies;\n        name = deps && getPluginNameFormDeps(deps);\n      }\n      if (!name) {\n        removeDirSync(installPath);\n        return callback();\n      }\n      var realPath = getInstallPath(name, result.dir);\n      removeDirSync(realPath);\n      try {\n        fs.renameSync(installPath, realPath);\n      } catch (e) {\n        if (handleError) {\n          try {\n            renameDirSync(installPath, realPath);\n          } catch (e) {\n            handleError(e);\n          }\n        } else {\n          renameDirSync(installPath, realPath);\n        }\n      }\n      var pkgPath = path.join(realPath, 'node_modules', name, 'package.json');\n      var stats = common.getStatSync(pkgPath);\n      if (stats && stats.mtime.getFullYear() < 2010) {\n        var now = new Date();\n        try {\n          fs.utimesSync(pkgPath, now, now);\n        } catch (e) {}\n      }\n      callback(pkgPath);\n    }\n  });\n  if (handleError) {\n    child.on('error', function(err) {\n      if (done) {\n        return;\n      }\n      done = true;\n      handleError(err);\n      removeTempFiles(argv);\n      removeDirSync(installPath);\n      callback();\n    });\n  }\n}\n\nfunction getRegistry(argv) {\n  for (var i = 0, len = argv.length; i < len; i++) {\n    var name = argv[i];\n    if (name === '--registry') {\n      return common.getRegistry(argv[i + 1]);\n    }\n    if (/^--registry=(.+)/.test(name)) {\n      return common.getRegistry(RegExp.$1);\n    }\n  }\n}\n\nfunction installPlugins(cmd, plugins, argv, pluginsCache, deep, handleError) {\n  deep = deep || 0;\n  var count = 0;\n  var peerPlugins = [];\n  var registry = getRegistry(argv);\n  var callback = function(pkgPath) {\n    if (pkgPath) {\n      var pkg = common.readJsonSync(pkgPath) || {};\n      var list = pkg.whistleConfig && (pkg.whistleConfig.peerPluginList || pkg.whistleConfig.peerPlugins);\n      if (Array.isArray(list) && list.length < 16) {\n        list.forEach(function(name) {\n          name = typeof name === 'string' ? name.trim() : null;\n          if (WHISTLE_PLUGIN_RE.test(name)) {\n            name = RegExp.$1;\n            if (peerPlugins.indexOf(name) === -1) {\n              peerPlugins.push(name);\n            }\n          }\n        });\n      }\n      if (registry) {\n        try {\n          fse.ensureDirSync(DEFAULT_PATH);\n        } catch (e) {}\n        var regList = common.readJsonSync(REGISTRY_LIST);\n        var result = [registry];\n        registry = null;\n        if (Array.isArray(regList)) {\n          regList.forEach(function(url) {\n            url = common.getRegistry(url);\n            if (url && result.indexOf(url) === -1) {\n              result.push(url);\n            }\n          });\n        }\n        try {\n          if (REGISTRY_LIST.length > MAX_REG_COUNT) {\n            REGISTRY_LIST = REGISTRY_LIST.slice(0, MAX_REG_COUNT);\n          }\n          fse.writeJsonSync(REGISTRY_LIST, result);\n        } catch (e) {\n          try {\n            fse.writeJsonSync(REGISTRY_LIST, result);\n          } catch (e) {}\n        }\n      }\n    }\n    if (--count <= 0 && deep < 16) {\n      peerPlugins = peerPlugins.filter(function(name) {\n        return !pluginsCache[name];\n      });\n      peerPlugins.length && installPlugins(cmd, peerPlugins, argv, pluginsCache, ++deep, handleError);\n    }\n  };\n  plugins.forEach(function(name) {\n    var ver;\n    if (WHISTLE_PLUGIN_RE.test(name)) {\n      name = RegExp.$1;\n      ver = RegExp.$2;\n    } else if (!common.isPluginAddr(name)) {\n      return;\n    }\n    ++count;\n    install(cmd, name, argv, ver, pluginsCache, callback, handleError);\n  });\n}\n\nexports.getWhistlePath = getWhistlePath;\n\nexports.install = function(cmd, argv, handleError) {\n  var restArgv = [];\n  var plugins = getPlugins(argv, true, restArgv);\n  if (!plugins.length) {\n    return;\n  }\n  cmd += CMD_SUFFIX;\n  restArgv.push('--no-package-lock');\n  installPlugins(cmd, plugins, restArgv, {}, 0, handleError);\n};\n\nexports.uninstall = function(plugins) {\n  var result = getInstallDir(plugins);\n  plugins = result.argv;\n  getPlugins(plugins).forEach(function(name) {\n    if (WHISTLE_PLUGIN_RE.test(name)) {\n      name = RegExp.$1;\n      removeDirSync(getInstallPath(name, result.dir));\n    }\n  });\n};\n\nexports.run = function(cmd, argv) {\n  var newPath = [];\n  fse.ensureDirSync(CUSTOM_PLUGIN_PATH);\n  fs.readdirSync(CUSTOM_PLUGIN_PATH).forEach(function(name) {\n    if (!name.indexOf('whistle.')) {\n      newPath.push(path.join(CUSTOM_PLUGIN_PATH, name, 'node_modules/.bin'));\n    } else if (name[0] === '@') {\n      try {\n        fs.readdirSync(path.join(CUSTOM_PLUGIN_PATH, name)).forEach(function(modName) {\n          newPath.push(path.join(CUSTOM_PLUGIN_PATH, name, modName, 'node_modules/.bin'));\n        });\n      } catch (e) {}\n    }\n  });\n  process.env.PATH && newPath.push(process.env.PATH);\n  newPath = newPath.join(CMD_SUFFIX ? ';' : ':');\n  process.env.PATH = newPath;\n  cp.spawn(cmd + CMD_SUFFIX, argv, formatCmdOptions({\n    stdio: 'inherit',\n    env: process.env\n  }));\n};\n"
  },
  {
    "path": "bin/proxy.js",
    "content": "var net = require('net');\nvar proxy = require('set-global-proxy');\nvar util = require('./util');\n\nvar OFF_RE = /^(?:o|0|-{0,2}off)$/i;\nvar BYPASS_RE = /^(?:-{0,2}bypass|-x|-b)$/i;\nvar NUM_RE = /^\\d+$/;\nvar HOST_SUFFIX_RE = /\\:(\\d+|auto)?$/;\nvar HOST_RE = /^[a-z\\d_-]+(?:\\.[a-z\\d_-]+)*$/i;\n\nfunction showInfo(msg) {\n  process.nextTick(function() {\n    util.info(msg);\n  });\n}\n\nfunction showError(msg) {\n  process.nextTick(function() {\n    util.error(msg);\n  });\n}\n\nfunction enableProxy(options) {\n  try {\n    var host = util.joinIpPort(options.host, options.port);\n    if (proxy.enableProxy(options)) {\n      showInfo('Successfully set system proxy (' + host + ')');\n    } else {\n      showError('Failed to set system proxy (' + host + ')');\n    }\n  } catch (e) {\n    showError(e.message);\n  }\n}\n\nfunction disableProxy(sudo) {\n  try {\n    if (proxy.disableProxy(sudo)) {\n      showInfo('Successfully disabled system proxy');\n    } else {\n      showError('Failed to disable system proxy');\n    }\n  } catch (e) {\n    showError(e.message);\n  }\n}\n\nmodule.exports = function(argv) {\n  var cmd = argv[0];\n  var sudo = argv.indexOf('--no-sudo') === -1;\n  if (OFF_RE.test(cmd)) {\n    return disableProxy(sudo);\n  }\n  var options = {};\n  var skip;\n  argv.forEach(function(arg) {\n    if (skip) {\n      options.bypass = arg;\n      skip = false;\n    } else if (BYPASS_RE.test(arg)) {\n      skip = true;\n    } else if (NUM_RE.test(arg)) {\n      options.port = parseInt(arg, 10) || options.port;\n    } else if (net.isIP(arg)) {\n      options.host = arg || options.host;\n    } else if (HOST_SUFFIX_RE.test(arg)) {\n      var port = RegExp.$1;\n      delete options.port;\n      if (port > 0) {\n        options.port = parseInt(port, 10) || options.port;\n      }\n      var host = arg.slice(0, - port.length - 1);\n      if (host[0] === '[') {\n        host = host.substring(1);\n      }\n      var lastIndex = host.length - 1;\n      if (host[lastIndex] === ']') {\n        host = host.substring(0, lastIndex);\n      }\n      if (host && (net.isIP(host) || HOST_RE.test(host))) {\n        options.host = host || options.host;\n      }\n    }\n  });\n  if (!options.port) {\n    options.port = util.getDefaultPort();\n  }\n  options.host = options.host || '127.0.0.1';\n  options.sudo = sudo;\n  enableProxy(options);\n};\n"
  },
  {
    "path": "bin/status.js",
    "content": "var util = require('./util');\nvar pkg = require('../package.json');\nvar colors = require('colors/safe');\nvar isRunning = util.isRunning;\nvar showUsage = util.showUsage;\nvar readConfig = util.readConfig;\nvar readConfigList = util.readConfigList;\nvar warn = util.warn;\nvar info = util.info;\n\nfunction showAll(byStop) {\n  var list = readConfigList().map(function(config) {\n    return new Promise(function(resolve) {\n      isRunning(config.pid, function(running) {\n        resolve(running && config);\n      });\n    });\n  });\n  Promise.all(list).then(function(confList) {\n    confList = confList.filter(function(conf) {\n      return conf;\n    });\n    var len = confList.length;\n    if (!len) {\n      warn('[!] No running Whistle instances');\n    } else {\n      var tips = ['[i] All running Whistle instances:'];\n      confList.forEach(function(conf, i) {\n        ++i;\n        var options = conf.options;\n        tips.push('  ' + i + '.' + (conf.pid ? ' PID: ' + conf.pid + ',' : '')\n          + ' Port: ' + (options.port || pkg.port)\n          + (options.host ? ', Host: ' + options.host : '')\n          + (options.storage ? ', Storage: ' + options.storage : '')\n          + (byStop ? colors.red(' (Stop cmd: ' + (options.storage ? 'w2 stop -S ' + options.storage : 'w2 stop') + ')') : ''));\n      });\n      byStop && warn('[!] This Whistle instance is not running');\n      info(tips.join('\\n'));\n    }\n  });\n\n  // All running whistle:\n  // 1. port: 8899\n  // 2. port: 888, storage: xxx\n}\n\nmodule.exports = function(all, storage) {\n  if (!all) {\n    var config = readConfig(storage) || '';\n    var pid = config.pid;\n    var options = pid && config.options;\n    if (pid) {\n      isRunning(pid, function(running) {\n        if (running) {\n          return showUsage(true, options);\n        }\n        showAll();\n      });\n      return;\n    }\n  }\n  showAll();\n};\n\nmodule.exports.showAll = showAll;\n"
  },
  {
    "path": "bin/use.js",
    "content": "var path = require('path');\nvar http = require('http');\nvar url = require('url');\nvar util = require('./util');\nvar importModule = require('./import');\nvar pkg = require('../package.json');\nvar common = require('../lib/util/common');\n\nvar isRunning = util.isRunning;\nvar error = util.error;\nvar warn = util.warn;\nvar info = util.info;\nvar readConfig = util.readConfig;\nvar MAX_RULES_LEN = 1024 * 256;\nvar DEFAULT_OPTIONS = util.DEFAULT_OPTIONS;\nvar options;\n\nfunction showStartWhistleTips(storage, isClient) {\n  if (isClient) {\n    error('No running Whistle client. Please install and start the latest Whistle client: https://github.com/avwo/whistle-client');\n  } else {\n    error('No running Whistle instances. Execute `w2 start' + (storage ? ' -S ' + storage : '') + '` to start Whistle on the cli');\n  }\n}\n\nfunction handleRules(filepath, callback, port) {\n  importModule(filepath, function(getRules) {\n    if (typeof getRules !== 'function') {\n      return callback(getRules);\n    }\n    var opts = {\n      port: port,\n      existsPlugin: existsPlugin\n    };\n    if (options && options.host) {\n      opts.host = options.host;\n    }\n    getRules(callback, opts);\n  });\n}\n\nfunction getString(str) {\n  return typeof str !== 'string' ? '' : str.trim();\n}\n\nfunction existsPlugin(name) {\n  if (!name || typeof name !== 'string') {\n    return false;\n  }\n  var pluginPaths = require('../lib/plugins/module-paths').getPaths();\n  for (var i = 0, len = pluginPaths.length; i < len; i++) {\n    var stats = common.getStatSync(path.join(pluginPaths[i], name));\n    if (stats && stats.isDirectory()) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvar reqOptions;\nfunction request(body, callback) {\n  if (!reqOptions) {\n    reqOptions = url.parse('http://' + util.joinIpPort(options.host || '127.0.0.1', options.port) + '/cgi-bin/rules/project');\n    reqOptions.headers = {\n      'content-type': 'application/x-www-form-urlencoded'\n    };\n    if (options.specialAuth) {\n      reqOptions.headers['x-whistle-special-auth'] = options.specialAuth;\n    }\n    reqOptions.method = 'POST';\n    if (options.username || options.password) {\n      var auth = [options.username || '', options.password || ''].join(':');\n      reqOptions.headers.authorization = 'Basic ' + new Buffer.from(auth).toString('base64');\n    }\n  }\n  var req = http.request(reqOptions, function(res) {\n    util.getBody(res, function(err, data) {\n      if (err) {\n        throw err;\n      }\n      callback(data);\n    });\n  });\n  // 不处理错误，直接抛出终止进程\n  req.end(body);\n}\n\nfunction checkDefault(running, storage, isClient, callback) {\n  if (running) {\n    return callback();\n  }\n  if (isClient) {\n    return callback(true);\n  }\n  var execCallback = function(err) {\n    callback && callback(err);\n    callback = null;\n  };\n  var req = http.get('http://' + DEFAULT_OPTIONS.host + ':' + DEFAULT_OPTIONS.port + '/cgi-bin/status', function(res) {\n    res.on('error', execCallback);\n    util.getBody(res, function(err, data) {\n      if (err || !data || data.name !== pkg.name || data.storage !== storage) {\n        return execCallback(true);\n      }\n      callback(null, DEFAULT_OPTIONS.port);\n    });\n  });\n  req.on('error', execCallback);\n  req.end();\n}\n\nfunction readClientConfig() {\n  var procPath = path.join(common.getHomedir(), '.whistle_client.pid');\n  var info = (common.readFileTextSync(procPath) || '').split(',');\n  if (info && info.length === 4) {\n    return {\n      pid: info[0],\n      options: {\n        host: info[1],\n        port: info[2],\n        specialAuth: info[3]\n      }\n    };\n  }\n}\n\nmodule.exports = function(filepath, storage, force, isClient) {\n  var config;\n  var dir = '';\n  if (isClient) {\n    storage = '';\n    config = readClientConfig() || '';\n  } else {\n    storage = storage || '';\n    dir = encodeURIComponent(storage);\n    config = readConfig(dir) || '';\n    if (config.options) {\n      delete config.options.specialAuth;\n    }\n  }\n  options = config.options || '';\n  isRunning(options && config.pid, function(running) {\n    checkDefault(running, dir, isClient, function(err, port) {\n      if (err) {\n        return showStartWhistleTips(storage, isClient);\n      }\n      filepath = path.resolve(filepath || '.whistle.js');\n      if (port) {\n        options = DEFAULT_OPTIONS;\n      } else {\n        port = options.port = options.port > 0 ? options.port : pkg.port;\n      }\n      handleRules(filepath, function(result) {\n        if (!result) {\n          error('The name and rules are required');\n          return;\n        }\n        var name = getString(result.name);\n        if (!name || name.length > 64) {\n          error('The name must be 1-64 characters');\n          return;\n        }\n        var rules = getString(result.rules);\n        if (rules.length > MAX_RULES_LEN) {\n          error('Maximum rules size: 256KB');\n          return;\n        }\n        var groupName = getString(result.groupName) || getString(result.group);\n        var setRules = function() {\n          var body = [\n            'name=' + encodeURIComponent(name),\n            'rules=' + encodeURIComponent(rules),\n            'groupName=' + encodeURIComponent(groupName.trim())\n          ].join('&');\n          request(body, function() {\n            info('Successfully configured rules for Whistle' + (isClient ? ' client' : '') + ' (' + util.joinIpPort(options.host || '127.0.0.1', port) + ')');\n          });\n        };\n        if (force) {\n          return setRules();\n        }\n        request('name=' + encodeURIComponent(name) + '&enable=1&top=1', function(data) {\n          if (data.rules) {\n            info('Successfully enabled');\n            warn('Warning: Rule already exists. Use \\'--force\\' to override');\n            return;\n          }\n          setRules();\n        });\n      }, port);\n    });\n  });\n};\n"
  },
  {
    "path": "bin/util.js",
    "content": "var cp = require('child_process');\nvar program = require('starting');\nvar util = require('util');\nvar os = require('os');\nvar fs = require('fs');\nvar config = require('../lib/config');\nvar common = require('../lib/util/common');\nvar colors = require('colors/safe');\nvar path = require('path');\nvar extend = require('extend');\nvar createHmac = require('crypto').createHmac;\n\nvar joinIpPort = common.joinIpPort;\nvar DEFAULT_OPTIONS = { host: '127.0.0.1', port: 8899 };\n\nexports.joinIpPort = joinIpPort;\nexports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;\n/*eslint no-console: \"off\"*/\nvar CHECK_RUNNING_CMD = process.platform === 'win32' ?\n  'tasklist /fi \"PID eq %s\" | findstr /i \"node.exe\"'\n  : 'ps -f -p %s | grep \"node\"';\nvar isWin = process.platform === 'win32';\n\nfunction isRunning(pid, callback) {\n  pid ? cp.exec(util.format(CHECK_RUNNING_CMD, pid),\n    function (err, stdout, stderr) {\n      callback(!err && !!stdout.toString().trim());\n    }) : callback();\n}\n\nexports.isRunning = isRunning;\n\n\nfunction getIpList() {\n  var ipList = [];\n  var ifaces = os.networkInterfaces();\n  Object.keys(ifaces).forEach(function(ifname) {\n    ifaces[ifname].forEach(function (iface) {\n      if (iface.family == 'IPv4' || iface.family === 4) {\n        ipList.push(iface.address);\n      }\n    });\n  });\n  var index = ipList.indexOf('127.0.0.1');\n  if (index !== -1) {\n    ipList.splice(index, 1);\n  }\n  ipList.unshift('127.0.0.1');\n  return ipList;\n}\n\nfunction error(msg) {\n  console.log(colors.red(msg));\n}\n\nfunction warn(msg) {\n  console.log(colors.yellow(msg));\n}\n\nfunction info(msg) {\n  console.log(colors.green(msg));\n}\nexports.error = error;\nexports.warn = warn;\nexports.info = info;\n\nfunction showKillError() {\n  error('[!] Cannot kill ' + config.appName + ' owned by root');\n  info('[i] Try to run command ' + (isWin ? 'as an administrator' : 'with sudo'));\n}\n\nexports.showKillError = showKillError;\n\nfunction showUsage(isRunning, options, restart) {\n  var rcConf;\n  if (isRunning) {\n    if (restart) {\n      showKillError();\n    } else {\n      warn('[!] ' + config.appName + '@' + config.version + ' is running');\n    }\n  } else {\n    rcConf = common.readWhistleRc(options);\n    if (rcConf) {\n      var rcStr = JSON.stringify(rcConf);\n      if (rcStr.length > 30) {\n        rcStr = rcStr.substring(0, 27) + '...';\n      }\n      info('[i] Load configuration from ~/.whistlerc: ' + rcStr);\n    }\n    info('[i] ' + config.appName + '@' + config.version + (restart ? ' restarted' : ' started'));\n  }\n  options = options ? extend({}, rcConf, options) : rcConf;\n  options = formatOptions(options);\n  var port = /^\\d+$/.test(options.port) && options.port > 0 ?  options.port : config.port;\n  var list = common.isString(options.host) ? [options.host] : getIpList();\n  var oneIp = list.length === 1;\n  var index = 0;\n  if (!oneIp) {\n    info('[i] ' + (++index) + '. Use your device to visit these URLs and note which one works:');\n    info(list.map(function(ip) {\n      return '       http://' + colors.bold(joinIpPort(ip, port != 80 && port)) + '/';\n    }).join('\\n'));\n    warn('       Note: If none are accessible, check your firewall settings');\n    warn('             For help, see ' + colors.bold('https://github.com/avwo/whistle'));\n  }\n  info('[i] ' + (++index) + '. Set your device\\'s HTTP PROXY to ' + colors.bold((oneIp ? 'IP(' + list[0] + ')' : 'the working IP') + ' & PORT(' + port + ')'));\n  info('[i] ' + (++index) + '. Open ' + colors.bold('Chrome') + ' and visit ' + colors.bold('http://' + (options.localUIHost || config.localUIHost) + '/') + ' to begin');\n\n  var bypass = program.init;\n  if (bypass == null) {\n    return;\n  }\n  return {\n    host: options.host || '127.0.0.1',\n    port: port,\n    bypass: typeof bypass === 'string' ? bypass : undefined\n  };\n}\n\nexports.showUsage = showUsage;\n\nfunction getDataDir() {\n  return path.resolve(common.getHomedir(), '.startingAppData');\n}\n\nfunction formatOptions(options) {\n  if (!options || (!/^(?:([\\w.-]+):)?([1-9]\\d{0,4})$/.test(options.port) &&\n    !/^\\[([\\w.:]+)\\]:([1-9]\\d{0,4})$/.test(options.port))) {\n    return options;\n  }\n  options.host = options.host || RegExp.$1;\n  options.port = parseInt(RegExp.$2, 10);\n  return options;\n}\n\nexports.formatOptions = formatOptions;\n\nfunction readConfig(storage) {\n  var dataDir = getDataDir();\n  var configFile = path.join(dataDir, encodeURIComponent('#' + (storage ? storage + '#' : '')));\n  var conf = common.readJsonSync(configFile);\n  conf && formatOptions(conf.options);\n  return conf;\n}\n\nfunction readConfigList() {\n  var dataDir = getDataDir();\n  var result = [];\n  try {\n    fs.readdirSync(dataDir).forEach(function(dir) {\n      try {\n        dir = decodeURIComponent(dir);\n        var lastIndex = dir.length - 1;\n        if (dir[0] === '#' && dir[lastIndex] === '#') {\n          dir = dir.substring(1, lastIndex || 1);\n          var config = readConfig(dir);\n          if (config && config.pid && config.options) {\n            result.push(config);\n          }\n        }\n      } catch(e) {}\n    });\n  } catch(e) {}\n  return result;\n}\n\nexports.readConfig = readConfig;\nexports.readConfigList = readConfigList;\nexports.getHash = function(str) {\n  var hmac = createHmac('sha256', '5b6af7b9884e1165');\n  return hmac.update(str).digest('hex');\n};\n\nexports.getDefaultPort = function () {\n  var conf = readConfig();\n  conf = conf && conf.options;\n  var port = conf && conf.port;\n  return port > 0 ? port : 8899;\n};\n\nfunction getBody(res, callback) {\n  var resBody;\n  res.on('data', function(data) {\n    resBody = resBody ? Buffer.concat([resBody, data]) : data;\n  });\n  res.on('end', function() {\n    if (res.statusCode != 200) {\n      callback(resBody || 'response ' + res.statusCode + ' error');\n    } else {\n      callback(null, JSON.parse(resBody + ''));\n    }\n  });\n}\n\nexports.getBody = getBody;\n"
  },
  {
    "path": "bin/whistle.js",
    "content": "#! /usr/bin/env node\n/*eslint no-console: \"off\"*/\nvar program = require('starting');\nvar path = require('path');\nvar http = require('http');\nvar config = require('../lib/config');\nvar useRules = require('./use');\nvar showStatus = require('./status');\nvar util = require('./util');\nvar plugin = require('./plugin');\nvar setProxy = require('./proxy');\nvar installCA = require('./ca/cli');\nvar colors = require('colors');\n\nvar error = util.error;\nvar info = util.info;\nvar OPTIONS = util.DEFAULT_OPTIONS;\n\nfunction getLatestVersion(options, cb, index) {\n  var done;\n  var handleCb = function(err, data) {\n    if (done) {\n      return;\n    }\n    index = index || 0;\n    if (index < 5 && (err || !data)) {\n      return setTimeout(function() {\n        getLatestVersion(options, cb, index + 1);\n      }, 300);\n    }\n    done = true;\n    cb(data && data.hasNewVersion ? data.latestVersion : '');\n  };\n  options = options || OPTIONS;\n  var req = http.get('http://' + util.joinIpPort(options.host || OPTIONS.host, options.port || OPTIONS.port) + '/cgi-bin/check-update', function(res) {\n    res.on('error', handleCb);\n    util.getBody(res, handleCb);\n  });\n  req.on('error', handleCb);\n  req.end();\n}\n\nfunction handleEnd(err, options, restart, run) {\n  var result = util.showUsage(err, options, restart);\n  getLatestVersion(options, function(latestVersion) {\n    if (latestVersion) {\n      error('[!] A new version is available (v' + latestVersion + '). Run \\'' + colors.bold('npm i -g ' + config.name) + '\\' to update');\n    }\n    run && console.log('Press [Ctrl+C] to stop ' + config.appName + '...');\n  });\n  if (!result) {\n    return;\n  }\n  var host = util.joinIpPort(result.host, result.port);\n  var argv = [host];\n  if (result.bypass) {\n    argv.push('-x', result.bypass);\n  }\n  setProxy(argv);\n  installCA([host]);\n}\n\nfunction showStartupInfo(err, options, debugMode, restart) {\n  if (!err || err === true) {\n    return handleEnd(err, options, restart);\n  }\n  if (/listen EADDRINUSE/.test(err)) {\n    options = util.formatOptions(options);\n    var port = options.port || config.port;\n    error('[!] Failed to bind proxy port ' + (options.host ? util.joinIpPort(options.host, port) : port) + ': Port already in use');\n    info('[i] ' + config.appName + ' may already be running. Try: ' + (debugMode ? 'w2 stop' : 'w2 restart') + ' to ' + (debugMode ? 'stop' : 'restart') + ' ' + config.appName);\n    info('    or use a different port with: ' + (debugMode ? '`w2 run -p newPort`\\n' : '`w2 start -p newPort`\\n'));\n  } else if (err.code == 'EACCES' || err.code == 'EPERM') {\n    error('[!] Permission denied: Cannot start ' + config.appName + ' as root');\n    info('[i] Try running with sudo\\n');\n  }\n\n  error(err.stack ? 'Date: ' + new Date().toLocaleString() + '\\n' + err.stack : err);\n}\n\nfunction getName() {\n  if (/[/\\\\](\\w+)$/.test(process.argv[1])) {\n    return RegExp.$1;\n  }\n}\n\nprogram.setConfig({\n  main: function(options) {\n    var cmd = process.argv[2];\n    if ((cmd === 'start' || cmd === 'restart') && (options.inspect || options.inspectBrk)) {\n      error('[!] Inspector mode only supported with `w2 run` command');\n      var argv = Array.prototype.slice.call(process.argv, 3);\n      info('[i] Usage: w2 run' + (argv.length ? ' ' + argv.join(' ') : ''));\n      return process.exit(1);\n    }\n    var hash = options && options.storage && encodeURIComponent(options.storage);\n    return path.join(__dirname, '../index.js') + (hash ? '#' + hash + '#' : '');\n  },\n  name: getName() || config.name,\n  version: config.version,\n  runCallback: function(err, options) {\n    if (err) {\n      showStartupInfo(err, options, true);\n      return;\n    }\n    handleEnd(false, options, false, true);\n  },\n  startCallback: showStartupInfo,\n  restartCallback: function(err, options) {\n    showStartupInfo(err, options, false, true);\n  },\n  stopCallback: function(err) {\n    if (err === true) {\n      info('[i] ' + config.appName + ' killed');\n    } else if (err) {\n      if (err.code === 'EPERM') {\n        util.showKillError();\n      } else {\n        error('[!] ' + err.message);\n      }\n    } else {\n      showStatus.showAll(true);\n    }\n  }\n});\n\nprogram\n  .command('status')\n  .description('Display running status');\nprogram\n  .command('add')\n  .description('Add rules from local JS file (.whistle.js by default)');\nprogram.command('proxy')\n  .description('Configure system proxy settings');\nprogram.command('ca')\n  .description('Manage Root CA certificates');\nprogram.command('install')\n  .description('Install Whistle plugin');\nprogram.command('uninstall')\n  .description('Uninstall Whistle plugin');\nprogram.command('exec')\n  .description('Execute plugin command');\n\nprogram\n  .option('-D, --baseDir [baseDir]', 'set storage root path', String, undefined)\n  .option('-z, --certDir [directory]', 'set custom certificate directory', String, undefined)\n  .option('-l, --localUIHost [hostname]', 'set web UI domain (' + config.localUIHost + ' by default)', String, undefined)\n  .option('-L, --pluginHost [hostname]', 'set plugin UI domains  (as: \"script=a.b.com&vase=x.y.com\")', String, undefined)\n  .option('-n, --username [username]', 'set web UI username', String, undefined)\n  .option('-w, --password [password]', 'set web UI password', String, undefined)\n  .option('-N, --guestName [username]', 'set web UI guest username (read-only)', String, undefined)\n  .option('-W, --guestPassword [password]', 'set web UI guest password (read-only)', String, undefined)\n  .option('-s, --sockets [number]', 'set max cached connections per domain (' + config.sockets + ' by default)', parseInt, undefined)\n  .option('-S, --storage [newStorageDir]', 'set configuration storage directory', String, undefined)\n  .option('-C, --copy [storageDir]', 'copy configuration from specified directory', String, undefined)\n  .option('-c, --dnsCache [time]', 'set DNS cache time (default: 60000ms)', String, undefined)\n  .option('-H, --host [boundHost]', 'set bound host (default: INADDR_ANY)', String, undefined)\n  .option('-p, --port [proxyPort]', 'set proxy port (default: ' + config.port + ' by default)', String, undefined)\n  .option('-P, --uiport [uiport]', 'set web UI port', String, undefined)\n  .option('-m, --middlewares [script path or module name]', 'set startup middlewares (format: xx,yy/zz.js)', String, undefined)\n  .option('-M, --mode [mode]', 'set startup mode (options: pureProxy|debug|multiEnv|capture|disableH2|network|rules|plugins|prod)', String, undefined)\n  .option('-t, --timeout [ms]', 'set request timeout (default: ' + config.timeout, parseInt, undefined)\n  .option('-e, --extra [extraData]', 'set plugin extra parameters', String, undefined)\n  .option('-f, --secureFilter [secureFilter]', 'set secure filter path', String, undefined)\n  .option('-r, --shadowRules [shadowRules]', 'set default shadow rules', String, undefined)\n  .option('-R, --reqCacheSize [reqCacheSize]', 'set request data cache size (default: 600)', String, undefined)\n  .option('-F, --frameCacheSize [frameCacheSize]', 'set WebSocket frame cache size (default: 512)', String, undefined)\n  .option('-A, --addon [pluginPaths]', 'add custom plugin paths', String, undefined)\n  .option('--init [bypass]', 'auto configure proxy and install Root CA')\n  .option('--cluster [workers]', 'start cluster with worker count (default: CPU cores)', String, undefined)\n  .option('--config [config]', 'load startup config from file', String, undefined)\n  .option('--rcPath [rcPath]', 'load configuration from file (default: ～/.whistlerc)', String, undefined)\n  .option('--dnsServer [dnsServer]', 'set custom DNS servers', String, undefined)\n  .option('--socksPort [socksPort]', 'set SOCKSv5 server port', String, undefined)\n  .option('--httpPort [httpPort]', 'set HTTP server port', String, undefined)\n  .option('--httpsPort [httpsPort]', 'set HTTPS server port', String, undefined)\n  .option('--allowOrigin [originList]', 'set allowed CORS origins (format: a.b.c,x.y.z or *)', String, undefined)\n  .option('--no-global-plugins', 'disable global plugins')\n  .option('--no-prev-options', 'ignore previous options on restart')\n  .option('--inspect [[host:]port]', 'enable inspector (default: 127.0.0.1:9229)')\n  .option('--inspectBrk [[host:]port]', 'enable inspector with breakpoint (default: 127.0.0.1:9229)');\n\nvar argv = process.argv;\nvar cmd = argv[2];\nvar storage;\nvar removeItem = function(list, name) {\n  var i = list.indexOf(name);\n  i !== -1 && list.splice(i, 1);\n};\nif (argv.indexOf('--init') !== -1) {\n  process.env.WHISTLE_MODE = (process.env.WHISTLE_MODE || '') + '|persistentCapture';\n}\nif (cmd === 'status') {\n  var all = argv[3] === '--all' || argv[3] === '-l';\n  if (argv[3] === '-S') {\n    storage = argv[4];\n  }\n  showStatus(all, storage);\n} else if (cmd === 'proxy') {\n  setProxy(Array.prototype.slice.call(argv, 3));\n} else if (cmd === 'ca') {\n  installCA(Array.prototype.slice.call(argv, 3));\n} else if (/^([a-z]{1,2})?uni(nstall)?$/.test(cmd)) {\n  plugin.uninstall(Array.prototype.slice.call(argv, 3));\n} else if (/^([a-z]{1,2})?i(nstall)?$/.test(cmd)) {\n  cmd = (RegExp.$1 || '') + 'npm';\n  argv = Array.prototype.slice.call(argv, 3);\n  removeItem(argv, '-g');\n  removeItem(argv, '--global');\n  plugin.install(cmd, argv);\n} else if (cmd === 'use' || cmd === 'enable' || cmd === 'add') {\n  var index = argv.indexOf('--force');\n  var force = index !== -1;\n  if (force) {\n    argv.splice(index, 1);\n  }\n  index = argv.indexOf('-c');\n  var isClient = index !== -1;\n  if (isClient) {\n    argv.splice(index, 1);\n  }\n  index = argv.indexOf('--client');\n  if (index !== -1) {\n    isClient = true;\n    argv.splice(index, 1);\n  }\n  var filepath = argv[3];\n  if (filepath === '-S') {\n    filepath = null;\n    storage = argv[4];\n  } else if (argv[4] === '-S') {\n    storage = argv[5];\n  }\n  if (filepath && /^-/.test(filepath)) {\n    filepath = null;\n  }\n  useRules(filepath, storage, force, isClient);\n} else if ((cmd === 'run' || cmd === 'exec') && argv[3] && /^[^-]/.test(argv[3])) {\n  cmd = argv[3];\n  argv = Array.prototype.slice.call(argv, 4);\n  plugin.run(cmd, argv);\n} else {\n  var pluginIndex = argv.indexOf('--pluginPaths');\n  if (pluginIndex !== -1) {\n    argv[pluginIndex] = '--addon';\n  }\n  program.parse(argv);\n}\n"
  },
  {
    "path": "biz/index.js",
    "content": "var net = require('net');\nvar rules = require('../lib/rules');\nvar util = require('../lib/util');\nvar handleUIReq = require('./webui/lib').handleRequest;\nvar handleWeinreReq = require('./weinre');\nvar config = require('../lib/config');\nvar common = require('../lib/util/common');\n\nvar localIpCache = util.localIpCache;\nvar WEBUI_PATH = config.WEBUI_PATH;\nvar CUSTOM_WEBUI_PATH = /\\/[\\w.-]*\\.whistle-path\\.5b6af7b9884e1165[\\w.-]*\\/+/;\nvar CUSTOM_WEBUI_PATH_RE = /^\\/[\\w.-]*\\.whistle-path\\.5b6af7b9884e1165[\\w.-]*\\/+/;\nvar PREVIEW_PATH_RE = config.PREVIEW_PATH_RE;\nvar WEBUI_PATH_RE = util.escapeRegExp(WEBUI_PATH, true);\nvar REAL_WEBUI_HOST = new RegExp('^' + WEBUI_PATH_RE + '(__([a-z\\\\d.-]+)(?:__(\\\\d{1,5}))?__/)');\nvar INTERNAL_APP = new RegExp('^' + WEBUI_PATH_RE + '(log|weinre|cgi)(?:\\\\.(\\\\d{1,5}))?/');\nvar PLUGIN_RE = new RegExp('^' + WEBUI_PATH_RE + 'whistle\\\\.([a-z\\\\d_-]+)/');\nvar CUSTOM_REAL_WEBUI_HOST = new RegExp('^/[\\\\w.-]*\\\\.whistle-path\\\\.5b6af7b9884e1165[\\\\w.-]*/+(__([a-z\\\\d.-]+)(?:__(\\\\d{1,5}))?__/)');\nvar CUSTOM_INTERNAL_APP = new RegExp('^/[\\\\w.-]*\\\\.whistle-path\\\\.5b6af7b9884e1165[\\\\w.-]*/+(log|weinre|cgi)(?:\\\\.(\\\\d{1,5}))?/');\nvar CUSTOM_PLUGIN_RE = new RegExp('^/[\\\\w.-]*\\\\.whistle-path\\\\.5b6af7b9884e1165[\\\\w.-]*/+whistle\\\\.([a-z\\\\d_-]+)/');\nvar REAL_WEBUI_HOST_PARAM = /_whistleInternalHost_=(__([a-z\\d.-]+)(?:__(\\d{1,5}))?__)/;\nvar OUTER_PLUGIN_RE = /^(?:\\/whistle)?\\/((?:whistle|plugin)\\.[a-z\\\\d_-]+)::(\\d{1,5})\\//;\n\nfunction transformUI(req, res) {\n  if (config.customUIHost && !config.keepProxyUI) {\n    return res.status(404).end();\n  }\n  return handleUIReq(req, res);\n}\n\nmodule.exports = function(req, res, next) {\n  var config = this.config;\n  var pluginMgr = this.pluginMgr;\n  var fullUrl = req.fullUrl = util.getFullUrl(req); // format request\n  var headers = req.headers;\n  var host = util.parseHost(headers.host);\n  var port = host[1] || (req.isHttps ? 443 : 80);\n  var bypass;\n  host = host[0];\n  req._w2hostname = host;\n  var transformPort, isProxyReq, isWeinre, isOthers;\n  var webUI = WEBUI_PATH;\n  var realHostRe = REAL_WEBUI_HOST;\n  var internalAppRe = INTERNAL_APP;\n  var pluginRe = PLUGIN_RE;\n  var isWebUI = req.path.indexOf(WEBUI_PATH) === 0;\n  var isOld;\n  if (!isWebUI && CUSTOM_WEBUI_PATH_RE.test(req.path)) {\n    isWebUI = true;\n    isOld = true;\n    webUI = CUSTOM_WEBUI_PATH;\n    realHostRe = CUSTOM_REAL_WEBUI_HOST;\n    internalAppRe = CUSTOM_INTERNAL_APP;\n    pluginRe = CUSTOM_PLUGIN_RE;\n  }\n  if (isWebUI) {\n    isWebUI = !config.pureProxy;\n    var realHost;\n    if (isWebUI) {\n      if (realHostRe.test(req.path) || REAL_WEBUI_HOST_PARAM.test(req.url)) {\n        var realPath = RegExp.$1;\n        var realPort = RegExp.$3;\n        realHost = RegExp.$2 + (realPort ? ':' + realPort : '');\n        headers[util.REAL_HOST_HEADER] = realHost;\n        req.url = req.url.replace(realPath, '');\n      } else {\n        req.curUrl = fullUrl;\n        if (realHost = rules.resolveInternalHost(req)) {\n          headers[util.REAL_HOST_HEADER] = realHost;\n        }\n      }\n      if (internalAppRe.test(req.path)) {\n        transformPort = RegExp.$2;\n        isWeinre = RegExp.$1 === 'weinre';\n        if (transformPort) {\n          isOthers = isProxyReq = transformPort != config.port;\n        } else {\n          isProxyReq = false;\n          transformPort = config.port;\n        }\n        isProxyReq = isProxyReq || isOld;\n      } else if (pluginRe.test(req.path)) {\n        isProxyReq = !pluginMgr.getPlugin(RegExp.$1 + ':');\n      } else if (!headers[config.WEBUI_HEAD]) {\n        isWebUI = false;\n      }\n      if (!config.proxyServer && isProxyReq && !config.isLocalUIUrl(host)) {\n        isWebUI = false;\n        req.isPluginReq = true;\n        req._isProxyReq = true;\n      }\n      if (isWebUI) {\n        req.fromInternalPath = true;\n        var hostname = (req._fwdHost && util.parseHost(req._fwdHost)[0]) || host;\n        headers[common.ORIGIN_HOST_HEADER] = hostname || '*';\n      }\n    }\n  } else {\n    isWebUI = headers[config.WEBUI_HEAD];\n    if (!isWebUI) {\n      if (!(isWebUI = localIpCache.get(host))) {\n        isWebUI = config.isLocalUIUrl(host);\n        if (isWebUI ? net.isIP(host) : util.isLocalHost(host)) {\n          isWebUI = util.isProxyPort(port);\n        }\n      }\n    } else if (util.isProxyPort(port) && net.isIP(host)) {\n      localIpCache.set(host, 1);\n    }\n    if (PREVIEW_PATH_RE.test(req.url)) {\n      headers[util.INTERNAL_ID_HEADER] = util.INTERNAL_ID;\n      req.url = '/preview.html?charset=' + RegExp.$1;\n      isWebUI = true;\n    } else if (isWebUI) {\n      if (req.path.indexOf('/_/') === 0) {\n        bypass = '/_/';\n      } else if (req.path.indexOf('/-/') === 0) {\n        bypass = '/-/';\n      }\n      if (bypass) {\n        req.url = req.url.replace(bypass, '/');\n      }\n      delete headers[util.INTERNAL_ID_HEADER];\n    }\n  }\n  // 后续有用到\n  fullUrl = req.fullUrl = util.getFullUrl(req);\n  if (bypass) {\n    return next();\n  }\n  var localRule;\n  req.curUrl = fullUrl;\n  if (isWebUI) {\n    if (isOthers) {\n      util.transformReq(req, res, transformPort);\n    } else {\n      req.url = req.url.replace(transformPort ? internalAppRe : webUI, '/');\n      if (OUTER_PLUGIN_RE.test(req.path)) {\n        var outerPort = RegExp.$2;\n        req.url = req.url.replace(RegExp['$&'], '/' + RegExp.$1 + '/');\n        if (outerPort > 0 && outerPort < 65536 && outerPort != config.port) {\n          headers.host = '127.0.0.1:' + outerPort;\n          return util.transformReq(req, res, outerPort);\n        }\n      }\n      req._hasRespond = true;\n      if (isWeinre) {\n        handleWeinreReq(req, res);\n      } else {\n        transformUI(req, res);\n      }\n    }\n  } else if (localRule = rules.resolveLocalRule(req)) {\n    req.url = localRule.url;\n    if (localRule.realPort) {\n      headers.host = '127.0.0.1:' + localRule.realPort;\n      util.transformReq(req, res, localRule.realPort);\n    } else {\n      transformUI(req, res);\n    }\n  } else {\n    next();\n  }\n};\n\n"
  },
  {
    "path": "biz/init.js",
    "content": "var http = require('http');\nvar ui = require('./webui/lib');\nvar util = require('../lib/util');\n\nmodule.exports = function init(proxy, callback) {\n  var config = proxy.config;\n  ui.init(proxy);\n  if (config.customUIPort) {\n    var server = http.createServer();\n    ui.setupServer(server);\n    util.getBoundIp(config.uihost, function(host) {\n      if (host) {\n        config.customUIHost = host;\n        server.listen(config.uiport, host, callback);\n      } else {\n        server.listen(config.uiport, callback);\n      }\n    });\n  } else {\n    callback();\n  }\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/abort.js",
    "content": "var proxy = require('../lib/proxy');\nvar socketMgr = proxy.socketMgr;\n\nfunction abort(reqId) {\n  proxy.abortRequest(reqId);\n  socketMgr.abort(reqId);\n}\n\nmodule.exports = function(req, res) {\n  var list = req.body.list;\n  if (list && typeof list === 'string') {\n    list.split(',').forEach(abort);\n  }\n  res.json({ ec: 0 });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/add-rules-values.js",
    "content": "var rules = require('../../../lib/rules/util').rules;\nvar values = require('../../../lib/rules/util').values;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var clientId = body.clientId;\n  var rulesData = body.rules;\n  var valuesData = body.values;\n  if (rulesData) {\n    if (rulesData.name === 'Default') {\n      rules.setDefault(rulesData.value, clientId);\n      rules.enableDefault();\n    } else {\n      rules.add(rulesData.name, rulesData.value, clientId);\n      rules.select(rulesData.name);\n    }\n  }\n  if (valuesData) {\n    values.add(valuesData.name, valuesData.value, clientId);\n  }\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/certs/active.js",
    "content": "var ca = require('../../../../lib/https/ca');\n\nmodule.exports = function(req, res) {\n  ca.setActiveCert(req.body);\n  res.json(ca.getCustomCertsFiles());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/certs/all.js",
    "content": "var ca = require('../../../../lib/https/ca');\n\nmodule.exports = function(req, res) {\n  res.json({\n    certs: ca.getCustomCertsFiles(),\n    dir: ca.CUSTOM_CERTS_DIR\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/certs/remove.js",
    "content": "var ca = require('../../../../lib/https/ca');\n\nmodule.exports = function(req, res) {\n  ca.removeCert(req.body);\n  var isparsed = req.query.dataType === 'parsed';\n  res.json(isparsed ? ca.getCustomCertsInfo() : ca.getCustomCertsFiles());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/certs/upload.js",
    "content": "var ca = require('../../../../lib/https/ca');\n\nmodule.exports = function(req, res) {\n  ca.uploadCerts(req.body);\n  var isparsed = req.query.dataType === 'parsed';\n  res.json(isparsed ? ca.getCustomCertsInfo() : ca.getCustomCertsFiles());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/check-update.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\nvar config = require('../../../lib/config');\nvar common = require('../../../lib/util/common');\n\nmodule.exports = function(req, res) {\n  var version = config.version;\n  var doNotShowAgainVersion = properties.get('doNotShowAgainVersion');\n  var latestVersion = properties.getLatestVersion('latestVersion');\n  var latestClientVersion = properties.getLatestVersion('latestClientVersion');\n  var hasNewVersion = common.compareVersion(latestVersion, version);\n\n  res.json({\n    ec: 0,\n    em: 'success',\n    showUpdate: !config.disableUpdateTips && hasNewVersion > 1 && common.compareVersion(latestVersion, doNotShowAgainVersion) > 1,\n    hasNewVersion: hasNewVersion > 0,\n    hasUpdater: config.hasUpdater,\n    version: config.version,\n    latestVersion: latestVersion,\n    latestClientVersion: latestClientVersion\n  });\n};\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/cookies.js",
    "content": "var sendGzip = require('./util').sendGzip;\nvar proxy = require('../lib/proxy');\n\nmodule.exports = function(req, res) {\n  sendGzip(req, res, {\n    ec: 0,\n    cookies: proxy.getCookiesByDomain(req.query.domain)\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/create-cert.js",
    "content": "var Zip = require('adm-zip');\nvar qs = require('querystring');\nvar ca = require('../../../lib/https/ca');\nvar sendError = require('./util').sendError;\n\nvar URL_RE = /^(?:([\\w.-]+:)?\\/\\/)?([\\w.=&!~*'()%-]+)/i;\nvar ILLEGAL_CHARS_RE = /[=&!~*'()%]/;\n\nfunction parseDomain(domain) {\n  domain = domain && typeof domain === 'string' && domain.trim();\n  if (!domain || domain.length > 256 || !URL_RE.test(domain)) {\n    return;\n  }\n  domain =  RegExp.$2.toLowerCase();\n  if (RegExp.$1 === 'root:') {\n    return qs.parse(domain);\n  }\n  return ILLEGAL_CHARS_RE.test(domain) ? null : domain;\n}\n\nmodule.exports = function(req, res) {\n  var domain = parseDomain(req.query.domain);\n  if (!domain) {\n    return res.status(400).end('Bad Request');\n  }\n  var isStr = typeof domain == 'string';\n  var cert = isStr ? ca.createCertificate(domain) : ca.createRootCA(domain);\n  var zip = new Zip();\n  domain = isStr ? domain : 'root';\n  var dir = domain + '/' + domain;\n  zip.addFile(dir + '.crt', Buffer.from(cert.cert));\n  zip.addFile(dir + '.key', Buffer(cert.key));\n  zip.toBuffer(function(body) {\n    res.attachment(domain + '.zip').send(body);\n  }, function(err) {\n    sendError(res, err);\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/custom-frames.js",
    "content": "var proxy = require('../lib/proxy');\nvar socketMgr = proxy.socketMgr;\n\nmodule.exports = function(req, res) {\n  var result = {};\n  req.body.idList.forEach(function(reqId) {\n    result[reqId] = socketMgr.getData(reqId);\n  });\n  req.body.frames.forEach(function(frame) {\n    proxy.emit('frame', frame);\n  });\n  res.json(result);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/custom-handler.js",
    "content": "var config = require('../../../lib/config');\n\nmodule.exports = function(req, res) {\n  if (!config.customHandler) {\n    return res.sendStatus(404);\n  }\n  config.customHandler(req, res);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/do-not-show-again.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\n\nmodule.exports = function(req, res) {\n  properties.set('doNotShowAgainVersion', properties.getLatestVersion('latestVersion'));\n  res.json({ec: 0, em: 'success'});\n};\n\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/download.js",
    "content": "var util = require('./util');\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var filename = body.filename;\n  var headers = body.headers;\n  var content = body.content;\n  var type = body.type;\n  var suffix = '.txt';\n  if (type === 'log') {\n    suffix = '.log';\n  } else if (type === 'mock') {\n    suffix = '.json';\n  } else if (type === 'rawBase64') {\n    type = 'base64';\n    suffix = '';\n  }\n\n  if (!filename || typeof filename !== 'string') {\n    filename = (type === 'mock' ? 'mock_' : 'text_') + util.formatDate() + suffix;\n  } else if (!/\\.\\w+$/.test(filename)) {\n    filename += suffix;\n  }\n\n  if (type === 'base64') {\n    try {\n      content = Buffer.from(content, 'base64');\n    } catch (e) {}\n  }\n  if (headers) {\n    headers += '\\r\\n\\r\\n';\n    try {\n      headers = Buffer.from(headers);\n      if (Buffer.isBuffer(content)) {\n        content = Buffer.concat([headers, content]);\n        headers = null;\n      }\n    } catch (e) {}\n    if (headers) {\n      content = headers + (content || '');\n    }\n  }\n  res.attachment(filename).send(content);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/enable-http2.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\n\nmodule.exports = function(req, res) {\n  properties.setEnableHttp2(req.body.enableHttp2 == 1);\n  res.json({ec: 0, em: 'success'});\n};\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-cert.js",
    "content": "var ca = require('../../../lib/https/ca');\n\nvar URL_RE = /^(?:(?:[\\w.-]+:)?\\/\\/)?([\\w.-]+)/i;\n\nfunction parseDomain(domain) {\n  domain = domain && typeof domain === 'string' && domain.trim();\n  if (!domain || domain.length > 64 || !URL_RE.test(domain)) {\n    return;\n  }\n  return RegExp.$1;\n}\n\nmodule.exports = function(req, res) {\n  var domain = parseDomain(req.query.domain);\n  if (!domain) {\n    return res.status(400).end('Bad Request');\n  }\n  if (domain === 'rootCA') {\n    return res.json(ca.getRootCA());\n  }\n  res.json(ca.createCertificate(domain.toLowerCase()));\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-custom-certs-files.js",
    "content": "var getCustomCertsFiles = require('../../../lib/https/ca').getCustomCertsFiles;\n\nmodule.exports = function(req, res) {\n  res.json(getCustomCertsFiles());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-custom-certs-info.js",
    "content": "var getCustomCertsInfo = require('../../../lib/https/ca').getCustomCertsInfo;\n// 给第三方用的，不能删除\nmodule.exports = function(req, res) {\n  res.json(getCustomCertsInfo());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-data.js",
    "content": "var proxy = require('../lib/proxy');\nvar util = require('./util');\nvar config = require('../../../lib/config');\nvar rulesUtil = require('../../../lib/rules/util');\nvar ca = require('../../../lib/https/ca');\n\nvar properties = rulesUtil.properties;\nvar rules = rulesUtil.rules;\nvar pluginMgr = proxy.pluginMgr;\nvar logger = proxy.logger;\n\nmodule.exports = function(req, res) {\n  var data = req.query;\n  if (data.ids && typeof data.ids == 'string') {\n    data.ids = data.ids.split(',');\n    if (data.status && typeof data.status == 'string') {\n      data.status = data.status.split(',');\n    } else {\n      data.status = null;\n    }\n  } else {\n    data.ids = null;\n  }\n  var clientIp = util.getClientIp(req);\n  var stopRecordConsole = data.startLogTime == -3;\n  var stopRecordSvrLog = data.startSvrLogTime == -3;\n  var h = req.headers;\n  var curLogId = proxy.getLatestId();\n  var curSvrLogId = logger.getLatestId();\n  util.sendGzip(req, res, {\n    ec: 0,\n    wName: config.whistleName,\n    disableInstaller: config.disableInstaller,\n    account: config.account,\n    version: config.version,\n    installErrors: config.getInstallPluginErrors && config.getInstallPluginErrors(data.clientId),\n    custom1: properties.get('Custom1'),\n    custom2: properties.get('Custom2'),\n    custom1Key: properties.get('Custom1Key'),\n    custom2Key: properties.get('Custom2Key'),\n    supportH2: config.enableH2,\n    hasInvalidCerts: ca.hasInvalidCerts,\n    clientIp: clientIp,\n    mrulesClientId: config.mrulesClientId,\n    mrulesTime: config.mrulesTime,\n    mvaluesClientId: config.mvaluesClientId,\n    mvaluesTime: config.mvaluesTime,\n    server: util.getServerInfo(req),\n    curLogId: stopRecordConsole ? undefined : curLogId,\n    curSvrLogId: stopRecordSvrLog ? undefined : curSvrLogId,\n    lastLogId: stopRecordConsole ? curLogId : undefined,\n    lastSvrLogId: stopRecordSvrLog ? curSvrLogId : undefined,\n    log: stopRecordConsole ? [] : proxy.getLogs(data.startLogTime, data.count, data.logId),\n    svrLog: stopRecordSvrLog ? [] : logger.getLogs(data.startSvrLogTime, data.count),\n    plugins: pluginMgr.getPlugins(),\n    disabledPlugins: !config.notAllowedDisablePlugins && properties.get('disabledPlugins') || {},\n    allowMultipleChoice: properties.get('allowMultipleChoice'),\n    backRulesFirst: properties.get('backRulesFirst'),\n    enabledCount: rules.getEnabledRules().length,\n    disabledAllPlugins: !config.notAllowedDisablePlugins && properties.get('disabledAllPlugins'),\n    disabledAllRules: !config.notAllowedDisableRules && properties.get('disabledAllRules'),\n    interceptHttpsConnects: properties.isEnableCapture(),\n    enableHttp2: properties.isEnableHttp2(),\n    defaultRulesIsDisabled: rules.defaultRulesIsDisabled(),\n    list: rules.getSelectedList(),\n    data: proxy.getData(data, clientIp, h['x-whistle-filter-key'], h['x-whistle-filter-value'], h['x-whistle-filter-client-id'], h[config.CLIENT_ID_HEADER])\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-frames.js",
    "content": "var proxy = require('../lib/proxy');\nvar socketMgr = proxy.socketMgr;\n\nmodule.exports = function(req, res) {\n  var frames = proxy.getFrames(req.query);\n  if (frames && !frames.length &&\n      !socketMgr.exists(req.query.curReqId)) {\n    frames = undefined;\n  }\n  res.json({\n    ec: 0,\n    frames: frames\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/get-session.js",
    "content": "var proxy = require('../lib/proxy');\n\nvar emptyArr = [];\nvar parseArray = function(str) {\n  try {\n    str = JSON.parse(str);\n    return Array.isArray(str) ? str : emptyArr;\n  } catch(e) {}\n  return emptyArr;\n};\n\nmodule.exports = function(req, res) {\n  var reqList = parseArray(req.query.reqList);\n  var resList = parseArray(req.query.resList);\n  var result = {};\n  reqList.concat(resList).forEach(function(id) {\n    if (result[id] != null) {\n      return;\n    }\n    var item = proxy.getItem(id);\n    if (!item) {\n      result[id] = 0;\n      return;\n    }\n    if ((item.requestTime && reqList.indexOf(id) !== -1) || item.endTime) {\n      result[id] = item;\n    }\n  });\n  res.json(result);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/hide-https-connects.js",
    "content": "module.exports = function(req, res) {\n  res.json({ec: 0, em: 'success'});\n};\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/https-status.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\n\nmodule.exports = function(req, res) {\n  res.json({\n    ec: 0,\n    enableCapture: properties.isEnableCapture(),\n    enableHttp2: properties.isEnableHttp2()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/import-remote.js",
    "content": "var util = require('../../../lib/util');\nvar loadService = require('../lib/proxy').loadService;\n\nvar MAX_LEN = 1024 * 1024 * 6;\nvar HTTP_RE = /https?:\\/\\/\\S/i;\n\nmodule.exports = function(req, res) {\n  var url = req.query.url;\n  if (HTTP_RE.test(url)) {\n    util.request({\n      url: url,\n      maxLength: MAX_LEN\n    }, function(err, body, r) {\n      if (err) {\n        var msg = err.code === 'EEXCEED'  ? 'The size of response body exceeds 6MB' : err.message;\n        return res.json({ec: 2, em: msg});\n      }\n      var status = r.statusCode;\n      if (status !== 200) {\n        var em = status > 200 && status < 400 ? 'No data' : 'Request failed';\n        return res.json({ec: 2, em: em + ' (statusCode: ' + status + ')'});\n      }\n      return res.json({ec: 0, body: body});\n    });\n  } else if (util.isString(url)) {\n    loadService(function(err, options) {\n      if (err) {\n        util.sendRes(res, 500, err.stack || err);\n      } else {\n        req.url = '/cgi-bin/temp/get?filename=' + encodeURIComponent(url);\n        util.transformReq(req, res, options.port);\n      }\n    });\n  } else {\n    res.json({ec: 400, em: 'Bad url'});\n  }\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/init.js",
    "content": "var getRules = require('./rules');\nvar getValues = require('./values');\nvar util = require('./util');\nvar config = require('../../../lib/config');\nvar rulesUtil = require('../../../lib/rules/util');\nvar ca = require('../../../lib/https/ca');\nvar proxy = require('../lib/proxy');\n\nvar properties = rulesUtil.properties;\nvar logger = proxy.logger;\nvar pluginMgr = proxy.pluginMgr;\n\nmodule.exports = function(req, res) {\n  var lastLog = proxy.getLogs(0, 1)[0];\n  var lastSvrLog = logger.getLogs(0, 1)[0];\n\n  util.sendGzip(req, res, {\n    wName: config.whistleName,\n    disableInstaller: config.disableInstaller,\n    account: config.account,\n    version: config.version,\n    custom1: properties.get('Custom1'),\n    custom2: properties.get('Custom2'),\n    hasInvalidCerts: ca.hasInvalidCerts,\n    supportH2: config.enableH2,\n    lastLogId: lastLog && lastLog.id,\n    lastSvrLogId: lastSvrLog && lastSvrLog.id,\n    lastDataId: proxy.getLastDataId(),\n    clientId: util.getClientId(),\n    clientIp: util.getClientIp(req),\n    mrulesClientId: config.mrulesClientId,\n    mrulesTime: config.mrulesTime,\n    mvaluesClientId: config.mvaluesClientId,\n    mvaluesTime: config.mvaluesTime,\n    latestVersion: properties.getLatestVersion('latestVersion'),\n    latestClientVersion: properties.getLatestVersion('latestClientVersion'),\n    server: util.getServerInfo(req),\n    rules: getRules(),\n    values: getValues(),\n    interceptHttpsConnects: properties.isEnableCapture(),\n    enableHttp2: properties.isEnableHttp2(),\n    plugins: pluginMgr.getPlugins(),\n    disabledPlugins: !config.notAllowedDisablePlugins && properties.get('disabledPlugins') || {},\n    disabledAllPlugins: !config.notAllowedDisablePlugins && properties.get('disabledAllPlugins'),\n    disabledAllRules: !config.notAllowedDisableRules && properties.get('disabledAllRules'),\n    localUIHost: config.localUIHost\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/intercept-https-connects.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\n\nmodule.exports = function(req, res) {\n  properties.setEnableCapture(req.body.interceptHttpsConnects == 1);\n  res.json({ec: 0, em: 'success'});\n};\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/log/set.js",
    "content": "var proxy = require('../../lib/proxy');\n\nmodule.exports = function(req, res) {\n  proxy.addLog(req.query);\n  res.setHeader('content-type', 'image/png');\n  res.end();\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/add-registry.js",
    "content": "var pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  pluginMgr.addRegistry(req.body.registry);\n  res.json({ec: 0});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/disable-all-plugins.js",
    "content": "var pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  pluginMgr.disableAllPlugins(req.body.disabledAllPlugins == 1);\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/disable-plugin.js",
    "content": "var properties = require('../../../../lib/rules/util').properties;\nvar pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  var disabledPlugins = properties.get('disabledPlugins') || {};\n  if (req.body.disabled == 1) {\n    disabledPlugins[req.body.name] = 1;\n  } else {\n    delete disabledPlugins[req.body.name];\n  }\n  properties.set('disabledPlugins', disabledPlugins);\n  pluginMgr.updateRules();\n  res.json({ec: 0, data: disabledPlugins});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/get-plugins.js",
    "content": "var properties = require('../../../../lib/rules/util').properties;\nvar pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  res.json({\n    ec: 0,\n    plugins: pluginMgr.getPlugins(),\n    disabledPlugins: properties.get('disabledPlugins') || {},\n    disabledAllPlugins: properties.get('disabledAllPlugins')\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/is-enable.js",
    "content": "var pluginMgr = require('../../lib/proxy').pluginMgr;\nvar config = require('../../../../lib/config');\n\nmodule.exports = function(req, res) {\n  var name = req.headers[config.PROXY_ID_HEADER];\n  res.json({\n    ec: 0,\n    enable: !!name && !!pluginMgr.getPlugin(name + ':')\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/registry-list.js",
    "content": "var pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  res.json({ ec: 0, list: pluginMgr.getRegistryList() });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/uninstall.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  var plugin = pluginMgr.getModifiablePlugin(req.body.name);\n  if (!plugin) {\n    return res.json({ ec: 0 });\n  }\n  var pkgPath = path.join(plugin.path, 'package.json');\n  var newPkgPath = pkgPath + '.' + Date.now();\n  var retry;\n  var handleCb = function(err) {\n    if (err && err.code !== 'ENOENT') {\n      if (!retry) {\n        retry = true;\n        return fs.unlink(pkgPath, handleCb);\n      }\n      return res.json({ ec: 2, em: err.message || 'Error' });\n    }\n    pluginMgr.refreshPlugins();\n    res.json({ ec: 0 });\n  };\n  fs.rename(pkgPath, newPkgPath, handleCb);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/plugins/update-rules.js",
    "content": "var config = require('../../../../lib/config');\nvar pluginMgr = require('../../lib/proxy').pluginMgr;\n\nmodule.exports = function(req, res) {\n  var name = req.headers[config.PROXY_ID_HEADER];\n  pluginMgr.updatePluginRules(name);\n  res.json({ec: 0});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/reset-local-address.js",
    "content": "var util = require('../../../lib/util');\n\nmodule.exports = function(req, res) {\n  util.localIpCache.reset();\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rootca.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\nvar getRootCAFile = require('../../../lib/https/ca').getRootCAFile;\n\nmodule.exports = function(req, res) {\n  var type = req.query.type;\n  if (type !== 'crt' && type !== 'pem') {\n    type = 'cer';\n  }\n  if (req.query.enableHttps) {\n    properties.setEnableCapture(true);\n  }\n  res.download(getRootCAFile(), 'rootCA.' + type);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/account.js",
    "content": "var getAccountRules = require('../../../../lib/rules/util').getAccountRules;\n\nmodule.exports = function(_, res) {\n  res.json({ec: 0, rules: getAccountRules() });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/add.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\nvar recycleBin = require('../../../../lib/rules/util').rules.recycleBin;\nvar isGroup = require('../../../../lib/util/common').isGroup;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var list;\n  if (body.name === 'Default') {\n    rules.setDefault(body.value, body.clientId);\n    body.selected && rules.enableDefault();\n  } else {\n    var exists = rules.exists(body.name);\n    if (rules.add(body.name, body.value, body.clientId) != null) {\n      if (isGroup(body.name)) {\n        if (body.focusName) {\n          rules.moveTo(body.name, body.focusName, body.clientId);\n        }\n      } else if (body.groupName) {\n        rules.moveToGroup(body.name, body.groupName, body.addToTop);\n      } else if (body.addToTop) {\n        rules.moveToTop(body.name, body.clientId);\n      } else if (!exists) {\n        var group = rules.getFirstGroup();\n        group && rules.moveTo(body.name, group.name, body.clientId, null, true);\n      }\n    }\n    body.selected && rules.select(body.name);\n  }\n  if (req.body.recycleFilename) {\n    recycleBin.remove(req.body.recycleFilename);\n    list = recycleBin.list();\n  }\n  res.json({\n    ec: 0,\n    list: list\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/allow-multiple-choice.js",
    "content": "var properties = require('../../../../lib/rules/util').properties;\nvar proxy = require('../../lib/proxy');\n\nmodule.exports = function(req, res) {\n  var enable = req.body.allowMultipleChoice == 1;\n  properties.set('allowMultipleChoice', enable);\n  proxy.emit('rulesDataChange', 'allowMultipleChoice', enable);\n  res.json({ec: 0, em: 'success'});\n};\n\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/disable-all-rules.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.disableAllRules(req.body.disabledAllRules == 1);\n  res.json({ec: 0, em: 'success'});\n};\n\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/disable-default.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.disableDefault();\n  res.json({ec: 0, em: 'success', defaultRulesIsDisabled: rules.defaultRulesIsDisabled(), list: rules.getSelectedList()});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/enable-back-rules-first.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.enableBackRulesFirst(req.body.backRulesFirst === '1');\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/enable-default.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.enableDefault();\n  rules.setDefault(req.body.value, req.body.clientId);\n  res.json({ec: 0, em: 'success', defaultRulesIsDisabled: rules.defaultRulesIsDisabled(), list: rules.getSelectedList()});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/enabled.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  util.sendGzip(req, res, {\n    ec: 0,\n    mflag: rules.getMFlag(),\n    list: rules.getEnabledRules()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/export.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  var exportRules = req.query.rules;\n  try {\n    exportRules = exportRules && JSON.parse(exportRules);\n  } catch(e) {\n    exportRules = null;\n  }\n  var result = {};\n  var list = ['Default'];\n  if (!exportRules || exportRules.Default) {\n    var defaultRules = rules.getDefault() || '';\n    result.Default = defaultRules;\n  }\n  rules.list().forEach(function(file) {\n    if (!exportRules || exportRules[file.name]) {\n      result[file.name] = file.data;\n      list.push(file.name);\n    }\n  });\n  result[''] = list;\n  var filename = req.query.filename;\n  if (filename && typeof filename === 'string') {\n    if (!/\\.(txt|json)/i.test(filename)) {\n      filename += '.txt';\n    }\n  } else {\n    filename = 'rules_' + util.formatDate() + '.txt';\n  }\n  res.attachment(filename);\n  util.sendGzipText(req, res, null, JSON.stringify(result, null, '  '));\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/import.js",
    "content": "var get = require('./index');\nvar addRules = require('../../../../lib/rules/util').addRules;\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  util.getReqData(req, function(err, result) {\n    if (err) {\n      res.status(200).json({ ec: 2, em: err.message });\n    } else {\n      addRules(result.data, result.replace, req.query.clientId);\n      util.sendGzip(req, res, get());\n    }\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/index.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\nvar properties = require('../../../../lib/rules/util').properties;\n\nmodule.exports = function get() {\n  return {\n    ec: 0,\n    enabledCount: rules.getEnabledRules().length,\n    defaultRulesIsDisabled: rules.defaultRulesIsDisabled(),\n    defaultRules: rules.getDefault(),\n    allowMultipleChoice: properties.get('allowMultipleChoice'),\n    backRulesFirst: properties.get('backRulesFirst'),\n    list: rules.list()\n  };\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/list.js",
    "content": "var get = require('./index');\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  util.sendGzip(req, res, get());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/list2.js",
    "content": "var get = require('./index');\n\nmodule.exports = function(req, res) {\n  var rules = get();\n  var data;\n  if (req.query.order) {\n    data = [{\n      name: 'Default',\n      value: rules.defaultRules\n    }];\n    rules.list.forEach(function(item) {\n      data.push({\n        name: item.name,\n        value: item.data\n      });\n    });\n  } else {\n    var list = ['Default'];\n    data = {\n      Default: rules.defaultRules\n    };\n    rules.list.forEach(function(item) {\n      data[item.name] = item.data;\n      list.push(item.name);\n    });\n    data[''] = list;\n  }\n  res.json(data);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/move-to.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var result = rules.moveTo(body.from, body.to, body.clientId, body.group === 'true', body.toTop === 'true');\n  res.json({ec: result ? 0 : 2, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/project.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nvar DEFAULT_GROUP = '\\rothers';\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var name = typeof body.name === 'string' ? body.name.trim() : null;\n  if (!name) {\n    return res.json({ ec: 0 });\n  }\n  var rulesText = body.rules || body.value;\n  if (!rulesText || !name || typeof rulesText !== 'string') {\n    if (body.enable == 1) {\n      rules.select(name);\n    }\n    return res.json({ ec: 0, rules: !!rules.get(name) });\n  }\n  if (rules.add(name, rulesText) != null) {\n    var groupName = typeof body.groupName === 'string' ? body.groupName.trim() : '';\n    rules.select(name);\n    if (groupName) {\n      groupName = '\\r' + groupName;\n      var group = rules.getFirstGroup();\n      if (rules.add(groupName) != null) {\n        if (!group && groupName !== DEFAULT_GROUP) {\n          rules.add(DEFAULT_GROUP);\n          rules.moveToTop(DEFAULT_GROUP);\n        }\n        rules.moveToGroup(name, groupName, true);\n        rules.moveGroupToTop(groupName);\n      }\n    } else {\n      rules.moveToTop(name);\n    }\n  }\n  res.json({ ec: 0 });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/recycle/list.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').rules.recycleBin;\nvar util = require('../../util');\n\nmodule.exports = function(req, res) {\n  util.sendGzip(req, res, {\n    ec: 0,\n    list: recycleBin.list()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/recycle/remove.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').rules.recycleBin;\n\nmodule.exports = function(req, res) {\n  recycleBin.remove(req.body.name);\n  res.json({\n    ec: 0,\n    list: recycleBin.list()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/recycle/view.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').rules.recycleBin;\n\nmodule.exports = function(req, res) {\n  var item = recycleBin.getFile(req.query.name);\n  res.json({\n    ec: item ? 0 : 3,\n    data: item && item.data\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/remove.js",
    "content": "var util = require('../../../../lib/rules/util');\n\nmodule.exports = function(req, res) {\n  util.removeBatch(util.rules, req.body);\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/rename.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  rules.rename(body.name, body.newName, body.clientId);\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/select.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var exists = rules.exists(body.name);\n  var changed;\n  if (rules.add(body.name, body.value, body.clientId) && !exists) {\n    var group = rules.getFirstGroup();\n    if (group) {\n      rules.moveTo(body.name, group.name, body.clientId, null, true);\n      changed = true;\n    }\n  }\n  rules.select(req.body.name);\n  res.json({ec: 0, em: 'success', defaultRulesIsDisabled: rules.defaultRulesIsDisabled(), list: rules.getSelectedList(), changed: changed});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/set-sys-hosts.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.setSysHosts(req.body.hosts, function(err) {\n    res.json({ec: err ? 2 : 0, em: err ? err.stack : 'success'});\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/rules/unselect.js",
    "content": "var rules = require('../../../../lib/rules/util').rules;\n\nmodule.exports = function(req, res) {\n  rules.add(req.body.name, req.body.value);\n  rules.unselect(req.body.name);\n  res.json({ec: 0, em: 'success', defaultRulesIsDisabled: rules.defaultRulesIsDisabled(), list: rules.getSelectedList()});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/server-info.js",
    "content": "var util = require('./util');\n\nmodule.exports = function(req, res) {\n  res.json({ec: 0, server: util.getServerInfo(req)});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/set-custom-column.js",
    "content": "var util = require('../../../lib/util');\nvar properties = require('../../../lib/rules/util').properties;\n\n\nfunction updateName(name, value, key) {\n  properties.set(name, util.isString(value) ? value.trim().toString(0, 16) : name);\n  properties.set(name + 'Key', util.isString(key) ? key.trim().substring(0, 72) : '');\n}\n\nmodule.exports = function(req, res) {\n  var name = req.body.name;\n  var value = req.body.value;\n  if (name === 'Custom1' || name === 'Custom2') {\n    updateName(name, value, req.body.key);\n  }\n  res.json({ ec: 0 });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/set-dns-order.js",
    "content": "var properties = require('../../../lib/rules/util').properties;\nvar config = require('../../../lib/config');\n\nmodule.exports = function(req, res) {\n  var order = +req.body.order;\n  properties.setDnsOrder(order);\n  properties.setIPv6Only(order === 4);\n  res.json({ec: 0, order: config.dnsOrder});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/socket/abort.js",
    "content": "var proxy = require('../../lib/proxy');\n\nvar socketMgr = proxy.socketMgr;\n\nmodule.exports = function(req, res) {\n  var reqId = req.body.reqId;\n  proxy.abortRequest(reqId);\n  socketMgr.abort(req.body.reqId);\n  res.json({ec: 0});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/socket/change-status.js",
    "content": "var socketMgr = require('../../lib/proxy').socketMgr;\n\nmodule.exports = function(req, res) {\n  socketMgr.changeStatus(req.body);\n  res.json({ec: 0});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/socket/data.js",
    "content": "var socketMgr = require('../../lib/proxy').socketMgr;\n\nmodule.exports = function(req, res) {\n  var result = socketMgr.sendData(req.body);\n  res.json({ec: result === false ? 3 : 0});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/status.js",
    "content": "var config = require('../../../lib/config');\n\nmodule.exports = function(_, res) {\n  res.json({\n    storage: config.storage || '',\n    client: config.client,\n    whistleName: config.whistleName,\n    name: config.name,\n    version: config.version\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/top.js",
    "content": "var proc = require('../../../lib/util/process');\n\nmodule.exports = function(req, res) {\n  res.json(proc);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/util.js",
    "content": "var util = require('../../../lib/util');\nvar config = require('../../../lib/config');\nvar proc = require('../../../lib/util/process');\nvar rulesUtil = require('../../../lib/rules/util');\nvar padLeft = require('../../../lib/util/common').padLeft;\n\nvar properties = rulesUtil.properties;\nvar rules = rulesUtil.rules;\nvar PID = process.pid;\nvar MAX_OBJECT_SIZE = 1024 * 1024 * 6;\nvar index = 0;\nvar dnsOverHttps = config.dnsOverHttps;\nvar doh = !!dnsOverHttps;\n\nexports.getClientId = function() {\n  if (index > 9999) {\n    index = 0;\n  }\n  return Date.now() + '-' + index++;\n};\n\nexports.getServerInfo = function(req) {\n  var baseDir;\n  if (!config.networkMode && !config.pluginsMode) {\n    baseDir = config.baseDirHash;\n  }\n  var info = {\n    whistleId: config.whistleId,\n    hasUpdater: config.hasUpdater,\n    hasWhistleToken: config.hasWhistleToken,\n    pid: PID,\n    pInfo: proc,\n    verbatim: config.verbatim,\n    dnsOrder: config.dnsOrder,\n    ipv6Only: config.ipv6Only,\n    dcc: config.disableCustomCerts,\n    dns: dnsOverHttps || config.dnsServer,\n    rulesMFlag: rules.getMFlag(),\n    doh: doh,\n    bip: config.host,\n    df: config.dnsOptional,\n    r6: config.resolve6,\n    version: config.version,\n    cmdName: config.cmdName,\n    hideLeftMenu: config.hideLeftMenu,\n    networkMode: config.networkMode,\n    rulesOnlyMode: config.rulesOnlyMode,\n    pluginsMode: config.pluginsMode,\n    ndr: config.notAllowedDisableRules,\n    ndp: config.notAllowedDisablePlugins,\n    drb: config.disabledBackOption,\n    drm: config.disabledMultipleOption,\n    rulesMode: config.rulesMode,\n    strictMode: config.strict,\n    multiEnv: config.multiEnv,\n    pureProxy: config.pureProxy,\n    notHTTPS: config.notAllowedEnableHTTPS,\n    baseDir: baseDir,\n    username: config.whistleName && config.username ? config.username + ' (' + config.whistleName + ')' : (config.username || config.whistleName),\n    nodeVersion: process.version,\n    latestVersion: properties.getLatestVersion('latestVersion'),\n    latestClientVersion: properties.getLatestVersion('latestClientVersion'),\n    host: util.hostname(),\n    isWin: util.isWin,\n    port: config.port,\n    realPort: config.realPort,\n    realHost: config.realHost,\n    socksPort: config.socksPort,\n    httpPort: config.httpPort,\n    httpsPort: config.httpsPort,\n    ipv4: [],\n    ipv6: [],\n    mac: req.ip + (config.storage ? '\\n' + config.storage : '')\n  };\n  var ifaces = util.networkInterfaces();\n  Object.keys(ifaces).forEach(function(ifname) {\n    ifaces[ifname].forEach(function (iface) {\n      if (iface.internal) {\n        return;\n      }\n      info[iface.family == 'IPv4' || iface.family === 4 ? 'ipv4' : 'ipv6'].push(iface.address);\n    });\n  });\n\n  return info;\n};\n\nvar DATA_RE = /[\\r\\n]\\s*(\\{[\\s\\S]*\\})[\\r\\n]/;\nvar REPLACE_RE = /[\\r\\n]1[\\r\\n]/;\nexports.getReqData = function(req, callback) {\n  var result = '';\n  req.on('data', function(chunk) {\n    result = result ? Buffer.concat([result, chunk]) : chunk;\n    if (result.length > MAX_OBJECT_SIZE) {\n      req.removeAllListeners('data');\n      callback(new Error('The file size can not exceed 6MB'));\n    }\n  });\n  req.on('error', callback);\n  req.on('end', function() {\n    result += '';\n    var data;\n    result = result.replace(DATA_RE, function(all, match) {\n      data = match;\n      return '';\n    });\n    if (!data) {\n      return callback(new Error('The file content is not a JSON object'));\n    }\n    try {\n      data = JSON.parse(data);\n    } catch(err) {\n      return callback(err);\n    }\n    callback(null, {\n      data: data,\n      replace: REPLACE_RE.test(result)\n    });\n  });\n};\n\nfunction formatDate() {\n  var date = new Date();\n  var result = [];\n  result.push(date.getFullYear());\n  result.push(padLeft(date.getMonth() + 1));\n  result.push(padLeft(date.getDate()));\n  result.push(padLeft(date.getHours()));\n  result.push(padLeft(date.getMinutes()));\n  result.push(padLeft(date.getSeconds()));\n  result.push(padLeft(date.getMilliseconds(), 3));\n  return result.join('');\n}\n\nexports.formatDate = formatDate;\n\nexports.getClientIp = util.getClientIp;\n\nfunction sendError(res, err) {\n  util.sendRes(res, 500, config.debugMode ?\n    '<pre>' + util.encodeHtml(util.getErrorStack(err)) + '</pre>' : 'Internal Server Error');\n}\n\nexports.sendError = sendError;\n\nexports.sendGzip = util.sendGzip;\n\nexports.sendGzipText = util.sendGzipText;\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/add.js",
    "content": "var values = require('../../../../lib/rules/util').values;\nvar recycleBin = require('../../../../lib/rules/util').values.recycleBin;\nvar isGroup = require('../../../../lib/util/common').isGroup;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var list;\n  var exists = values.exists(body.name);\n  if (values.add(body.name, body.value, body.clientId) != null) {\n    if (isGroup(body.name)) {\n      if (body.focusName) {\n        values.moveTo(body.name, body.focusName, body.clientId);\n      }\n    } else if (body.groupName) {\n      values.moveToGroup(body.name, body.groupName);\n    } else if (!exists) {\n      var group = values.getFirstGroup();\n      group && values.moveTo(body.name, group.name, body.clientId, null, true);\n    }\n  }\n  if (req.body.recycleFilename) {\n    recycleBin.remove(req.body.recycleFilename);\n    list = recycleBin.list();\n  }\n  res.json({\n    ec: 0,\n    list: list\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/export.js",
    "content": "var values = require('../../../../lib/rules/util').values;\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  var exportValues = req.query.values;\n  try {\n    exportValues = exportValues && JSON.parse(exportValues);\n  } catch(e) {\n    exportValues = null;\n  }\n  var result = {};\n  var list = [];\n  values.list().forEach(function(file) {\n    if (!exportValues || exportValues[file.name]) {\n      result[file.name] = file.data;\n      list.push(file.name);\n    }\n  });\n  result[''] = list;\n  var filename = req.query.filename;\n  if (filename && typeof filename === 'string') {\n    if (!/\\.(txt|json)/i.test(filename)) {\n      filename += '.txt';\n    }\n  } else {\n    filename = 'values_' + util.formatDate() + '.txt';\n  }\n  res.attachment(filename).send(JSON.stringify(result, null, '  '));\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/get.js",
    "content": "var values = require('../../../../lib/rules/util').values;\nvar properties = require('../../../../lib/rules/util').properties;\n\nmodule.exports = function(req, res) {\n  res.json({\n    fontSize: properties.get('valuesFontSize'),\n    theme: properties.get('valuesTheme'),\n    showLineNumbers: properties.get('valuesShowLineNumbers'),\n    values: values.list()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/import.js",
    "content": "var get = require('./index');\nvar util = require('../util');\nvar addValues = require('../../../../lib/rules/util').addValues;\n\nmodule.exports = function(req, res) {\n  util.getReqData(req, function(err, result) {\n    if (err) {\n      res.status(200).json({ ec: 2, em: err.message });\n    } else {\n      addValues(result.data, result.replace, req.query.clientId);\n      util.sendGzip(req, res, get());\n    }\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/index.js",
    "content": "var rulesUtil = require('../../../../lib/rules/util');\nvar values = rulesUtil.values;\n\nmodule.exports = function get() {\n  return {\n    ec: 0,\n    list: values.list()\n  };\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/list.js",
    "content": "var get = require('./index');\nvar util = require('../util');\n\nmodule.exports = function(req, res) {\n  util.sendGzip(req, res, get());\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/list2.js",
    "content": "var get = require('./index');\n\nmodule.exports = function(req, res) {\n  var data;\n  if (req.query.order) {\n    data = [];\n    get().list.forEach(function(item) {\n      data.push({\n        name: item.name,\n        value: item.data\n      });\n    });\n  } else {\n    var list = [];\n    data = {};\n    get().list.forEach(function(item) {\n      data[item.name] = item.data;\n      list.push(item.name);\n    });\n    data[''] = list;\n  }\n  res.json(data);\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/move-to.js",
    "content": "var values = require('../../../../lib/rules/util').values;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  var result = values.moveTo(body.from, body.to, body.clientId, body.group === 'true');\n  res.json({ec: result ? 0 : 2, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/recycle/list.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').values.recycleBin;\nvar util = require('../../util');\n\nmodule.exports = function(req, res) {\n  util.sendGzip(req, res, {\n    ec: 0,\n    list: recycleBin.list()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/recycle/remove.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').values.recycleBin;\n\nmodule.exports = function(req, res) {\n  recycleBin.remove(req.body.name);\n  res.json({\n    ec: 0,\n    list: recycleBin.list()\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/recycle/view.js",
    "content": "var recycleBin = require('../../../../../lib/rules/util').values.recycleBin;\n\nmodule.exports = function(req, res) {\n  var item = recycleBin.getFile(req.query.name);\n  res.json({\n    ec: item ? 0 : 3,\n    data: item && item.data\n  });\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/remove.js",
    "content": "var util = require('../../../../lib/rules/util');\n\nmodule.exports = function(req, res) {\n  util.removeBatch(util.values, req.body);\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/rename.js",
    "content": "var values = require('../../../../lib/rules/util').values;\n\nmodule.exports = function(req, res) {\n  var body = req.body;\n  values.rename(body.name, body.newName, body.clientId);\n  res.json({ec: 0, em: 'success'});\n};\n"
  },
  {
    "path": "biz/webui/cgi-bin/values/value.js",
    "content": "var values = require('../../../../lib/rules/util').values;\n\nmodule.exports = function(req, res) {\n  res.json({ value: values.get(req.query.key) });\n};\n"
  },
  {
    "path": "biz/webui/htdocs/editor.html",
    "content": "<!DOCTYPE html>\n<html data-theme=\"light\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"google\" value=\"notranslate\">\n<link rel=\"shortcut icon\" href=\"img/favicon.ico?v=2016\" />\n<title>Whistle Editor</title>\n<style>\n[data-theme=\"dark\"]:root { color-scheme: dark; }\nhtml, body, .main {padding: 0; margin: 0; height: 100%; overflow: hidden;}\n::-webkit-scrollbar{ width:10px; height:10px; }\n::-webkit-scrollbar-button{ width:10px;height:1px; }\n::-webkit-scrollbar-thumb{ background-clip:padding-box; background-color:rgba(0,0,0,.5); border-radius:8px; min-height: 30px;}\n::-webkit-scrollbar-thumb:hover{ background-clip:padding-box; background-color:rgba(0,0,0,.7); border-radius:8px;}\n::-webkit-scrollbar-track,::-webkit-scrollbar-thumb { border-left:2px solid transparent; border-right:2px solid transparent;}\n::-webkit-scrollbar-track:hover{ background-clip:padding-box; background-color:rgba(0,0,0,.15);}\ntextarea {resize: none; display: block; padding: 6px; font-size: 12px; width: 100%; height: 100%; border: none; box-sizing: border-box; font-family: consolas, monospace;}\n</style>\n</head>\n<body style=\"overscroll-behavior-x: none;\">\n<div class=\"main\">\n\t<textarea id=\"editor\"></textarea>\n</div>\n<script>\n\tvar editor = document.getElementById('editor');\n  var theme = 'light';\n\tvar getInitCallback = function() {\n\t\ttry {\n      var cb = window.parent._initWhistleTextEditor_;\n      if (typeof cb === 'function') {\n        return cb;\n      }\n\t\t} catch (e) {}\n\t};\n\tvar init = getInitCallback();\n\tif (!init && window.getValue) {\n    var value = getValue();\n    if (value) {\n      editor.value = window.name = value;\n    } else {\n      editor.value = window.name;\n    }\n\t} else {\n    editor.value = window.name;\n    window.setValue = function(value) {\n      editor.value = value;\n    };\n    init && init(window);\n\t}\n\twindow.getEditorValue = function() {\n\t\treturn editor.value;\n\t};\n\n  var setTheme = function() {\n    if (init) {\n      return;\n    }\n    var curTheme;\n    if (typeof window.getWhistleTheme === 'function') {\n      curTheme = window.getWhistleTheme();\n      setTimeout(setTheme, 1600);\n    } else {\n      curTheme = location.hash.slice(1);\n    }\n    if (curTheme === theme || (curTheme !== 'dark' && curTheme !== 'light')) {\n      return;\n    }\n    theme = curTheme;\n    document.documentElement.setAttribute('data-theme', theme);\n    if (location.hash.slice(1) || theme === 'dark') {\n      location.hash = theme;\n    }\n  };\n  setTheme();\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "biz/webui/htdocs/index.html",
    "content": "<!DOCTYPE html>\n<html data-theme=\"light\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"google\" value=\"notranslate\">\n<link rel=\"shortcut icon\" href=\"img/favicon.ico\" />\n<title>Whistle Web Debugging Proxy</title>\n</head>\n<body style=\"overscroll-behavior-x: none;\">\n<div id=\"container\" class=\"main\"></div>\n<script src=\"js/index.js?v=2.10.1\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "biz/webui/htdocs/js/decode.js",
    "content": "!function(r){function t(e){if(n[e])return n[e].exports;var o=n[e]={exports:{},id:e,loaded:!1};return r[e].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=r,t.c=n,t.p=\"\",t(0)}([function(r,t,n){\"use strict\";function e(r){try{return u(r)}catch(t){}}var o,u=n(1).toByteArray,a=n(2).Base64.decode,c=n(3);if(self.TextDecoder)try{o=new self.TextDecoder(\"GB18030\")}catch(i){}self.getText=function(r){var t=r&&e(r);if(!t)return\"\";if(!c(t))try{if(o)return o.decode(t)}catch(n){}try{return a(r)}catch(n){}return\"\"}},function(r,t){\"use strict\";function n(r){var t=r.length;if(t%4>0)throw new Error(\"Invalid string. Length must be a multiple of 4\");var n=r.indexOf(\"=\");-1===n&&(n=t);var e=n===t?0:4-n%4;return[n,e]}function e(r){var t=n(r),e=t[0],o=t[1];return 3*(e+o)/4-o}function o(r,t,n){return 3*(t+n)/4-n}function u(r){var t,e,u=n(r),a=u[0],c=u[1],i=new d(o(r,a,c)),f=0,s=c>0?a-4:a;for(e=0;s>e;e+=4)t=h[r.charCodeAt(e)]<<18|h[r.charCodeAt(e+1)]<<12|h[r.charCodeAt(e+2)]<<6|h[r.charCodeAt(e+3)],i[f++]=t>>16&255,i[f++]=t>>8&255,i[f++]=255&t;return 2===c&&(t=h[r.charCodeAt(e)]<<2|h[r.charCodeAt(e+1)]>>4,i[f++]=255&t),1===c&&(t=h[r.charCodeAt(e)]<<10|h[r.charCodeAt(e+1)]<<4|h[r.charCodeAt(e+2)]>>2,i[f++]=t>>8&255,i[f++]=255&t),i}function a(r){return f[r>>18&63]+f[r>>12&63]+f[r>>6&63]+f[63&r]}function c(r,t,n){for(var e,o=[],u=t;n>u;u+=3)e=(r[u]<<16&16711680)+(r[u+1]<<8&65280)+(255&r[u+2]),o.push(a(e));return o.join(\"\")}function i(r){for(var t,n=r.length,e=n%3,o=[],u=16383,a=0,i=n-e;i>a;a+=u)o.push(c(r,a,a+u>i?i:a+u));return 1===e?(t=r[n-1],o.push(f[t>>2]+f[t<<4&63]+\"==\")):2===e&&(t=(r[n-2]<<8)+r[n-1],o.push(f[t>>10]+f[t>>4&63]+f[t<<2&63]+\"=\")),o.join(\"\")}t.byteLength=e,t.toByteArray=u,t.fromByteArray=i;for(var f=[],h=[],d=\"undefined\"!=typeof Uint8Array?Uint8Array:Array,s=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",A=0,p=s.length;p>A;++A)f[A]=s[A],h[s.charCodeAt(A)]=A;h[\"-\".charCodeAt(0)]=62,h[\"_\".charCodeAt(0)]=63},function(r,t,n){var e,o;(function(n){!function(t,n){r.exports=n(t)}(\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:\"undefined\"!=typeof n?n:this,function(n){\"use strict\";n=n||{};var u,a=n.Base64,c=\"2.6.4\",i=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",f=function(r){for(var t={},n=0,e=r.length;e>n;n++)t[r.charAt(n)]=n;return t}(i),h=String.fromCharCode,d=function(r){if(r.length<2){var t=r.charCodeAt(0);return 128>t?r:2048>t?h(192|t>>>6)+h(128|63&t):h(224|t>>>12&15)+h(128|t>>>6&63)+h(128|63&t)}var t=65536+1024*(r.charCodeAt(0)-55296)+(r.charCodeAt(1)-56320);return h(240|t>>>18&7)+h(128|t>>>12&63)+h(128|t>>>6&63)+h(128|63&t)},s=/[\\uD800-\\uDBFF][\\uDC00-\\uDFFFF]|[^\\x00-\\x7F]/g,A=function(r){return r.replace(s,d)},p=function(r){var t=[0,2,1][r.length%3],n=r.charCodeAt(0)<<16|(r.length>1?r.charCodeAt(1):0)<<8|(r.length>2?r.charCodeAt(2):0),e=[i.charAt(n>>>18),i.charAt(n>>>12&63),t>=2?\"=\":i.charAt(n>>>6&63),t>=1?\"=\":i.charAt(63&n)];return e.join(\"\")},l=n.btoa&&\"function\"==typeof n.btoa?function(r){return n.btoa(r)}:function(r){if(r.match(/[^\\x00-\\xFF]/))throw new RangeError(\"The string contains invalid characters.\");return r.replace(/[\\s\\S]{1,3}/g,p)},g=function(r){return l(A(String(r)))},y=function(r){return r.replace(/[+\\/]/g,function(r){return\"+\"==r?\"-\":\"_\"}).replace(/=/g,\"\")},v=function(r,t){return t?y(g(r)):g(r)},C=function(r){return v(r,!0)};n.Uint8Array&&(u=function(r,t){for(var n=\"\",e=0,o=r.length;o>e;e+=3){var u=r[e],a=r[e+1],c=r[e+2],f=u<<16|a<<8|c;n+=i.charAt(f>>>18)+i.charAt(f>>>12&63)+(\"undefined\"!=typeof a?i.charAt(f>>>6&63):\"=\")+(\"undefined\"!=typeof c?i.charAt(63&f):\"=\")}return t?y(n):n});var x,B=/[\\xC0-\\xDF][\\x80-\\xBF]|[\\xE0-\\xEF][\\x80-\\xBF]{2}|[\\xF0-\\xF7][\\x80-\\xBF]{3}/g,b=function(r){switch(r.length){case 4:var t=(7&r.charCodeAt(0))<<18|(63&r.charCodeAt(1))<<12|(63&r.charCodeAt(2))<<6|63&r.charCodeAt(3),n=t-65536;return h((n>>>10)+55296)+h((1023&n)+56320);case 3:return h((15&r.charCodeAt(0))<<12|(63&r.charCodeAt(1))<<6|63&r.charCodeAt(2));default:return h((31&r.charCodeAt(0))<<6|63&r.charCodeAt(1))}},F=function(r){return r.replace(B,b)},m=function(r){var t=r.length,n=t%4,e=(t>0?f[r.charAt(0)]<<18:0)|(t>1?f[r.charAt(1)]<<12:0)|(t>2?f[r.charAt(2)]<<6:0)|(t>3?f[r.charAt(3)]:0),o=[h(e>>>16),h(e>>>8&255),h(255&e)];return o.length-=[0,0,2,1][n],o.join(\"\")},w=n.atob&&\"function\"==typeof n.atob?function(r){return n.atob(r)}:function(r){return r.replace(/\\S{1,4}/g,m)},S=function(r){return w(String(r).replace(/[^A-Za-z0-9\\+\\/]/g,\"\"))},U=function(r){return F(w(r))},j=function(r){return String(r).replace(/[-_]/g,function(r){return\"-\"==r?\"+\":\"/\"}).replace(/[^A-Za-z0-9\\+\\/]/g,\"\")},D=function(r){return U(j(r))};n.Uint8Array&&(x=function(r){return Uint8Array.from(S(j(r)),function(r){return r.charCodeAt(0)})});var O=function(){var r=n.Base64;return n.Base64=a,r};if(n.Base64={VERSION:c,atob:S,btoa:l,fromBase64:D,toBase64:v,utob:A,encode:v,encodeURI:C,btou:F,decode:D,noConflict:O,fromUint8Array:u,toUint8Array:x},\"function\"==typeof Object.defineProperty){var E=function(r){return{value:r,enumerable:!1,writable:!0,configurable:!0}};n.Base64.extendString=function(){Object.defineProperty(String.prototype,\"fromBase64\",E(function(){return D(this)})),Object.defineProperty(String.prototype,\"toBase64\",E(function(r){return v(this,r)})),Object.defineProperty(String.prototype,\"toBase64URI\",E(function(){return v(this,!0)}))}}return n.Meteor&&(Base64=n.Base64),\"undefined\"!=typeof r&&r.exports?r.exports.Base64=n.Base64:(e=[],o=function(){return n.Base64}.apply(t,e),!(void 0!==o&&(r.exports=o))),{Base64:n.Base64}})}).call(t,function(){return this}())},function(r,t){\"use strict\";function n(r,t){t=t||0;for(var n=Math.min(r.length,e);n>t;t++){var o=r[t];if(!(9==o||10==o||13==o||o>=32&&127>=o)){++t;var u=r[t];if(o>=194&&223>=o){if(u>=128&&191>=u)continue;return!u}++t;var a=r[t];if(224==o){if(u>=160&&191>=u&&a>=128&&191>=a)continue;return!a}if(o>=225&&236>=o||238==o||239==o){if(u>=128&&191>=u&&a>=128&&191>=a)continue;return!a}if(237==o){if(u>=128&&159>=u&&a>=128&&191>=a)continue;return!a}++t;var c=r[t];if(240==o){if(u>=144&&191>=u&&a>=128&&191>=a&&c>=128&&191>=c)continue;return!c}if(o>=241&&243>=o){if(u>=128&&191>=u&&a>=128&&191>=a&&c>=128&&191>=c)continue;return!c}if(244==o){if(u>=128&&143>=u&&a>=128&&191>=a&&c>=128&&191>=c)continue;return!c}return!1}}return!0}var e=32768;r.exports=function(r){return n(r)?!0:0===r[0]&&n(r,5)}}]);"
  },
  {
    "path": "biz/webui/htdocs/js/index.js",
    "content": "!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p=\"\",t(0)}([function(e,t,n){\"use strict\";function r(e){return e?be:null}function o(e){return\"string\"==typeof e?e.trim():\"\"}function i(e){if(!De.test(e))return!1;var t=z.isWin?/^(?:[\\w-]+:\\/\\/)?[a-z]:[\\\\/]/i:/^(?:[\\w-]+:\\/\\/)?\\//i;return t.test(e)}function a(e,t){var n=new FormData,r=new File([JSON.stringify(e)],\"data.json\",{type:\"application/json\"});return n.append(t||\"rules\",r),n}function s(e,t){return Q.isString(e)?e.length>ge?(ee.alert(\"File exceeds maximum size limit\"),t()):t(u(e)):e&&/\\.(txt|json|har)$/i.test(e.name)?e.size>ge?(ee.alert(\"File exceeds maximum size limit\"),t()):void Q.readFileAsText(e,function(e){t(u(e))}):(ee.alert(\"Supported file formats: .txt, .json, .har\"),t())}function l(e,t,n){s(e,function(e){return!e||Q.handleImportData(e,n)?t():void t(e)})}function c(e){var t=location.hash.substring(1);return t=t?t.replace(/[?#].*$/,\"\"):location.href.replace(/[?#].*$/,\"\").replace(/.*\\//,\"\"),e.networkMode?\"network\":e.rulesMode&&e.pluginsMode?\"plugins\":e.rulesOnlyMode?\"values\"===t?\"values\":\"rules\":e.rulesMode?\"network\"===t?\"rules\":t:e.pluginsMode?\"plugins\"!==t?\"network\":t:Ce&&!t?(t=F.get(\"pageName\"),-1!==Ee.indexOf(t)?t:\"network\"):t}function u(e){try{var t=JSON.parse(e);return t&&\"object\"===(\"undefined\"==typeof t?\"undefined\":M(t))?t:null}catch(n){G.error(n.message)}}function d(e,t){var n,r=e.length;for(n=0;r>n;n++)if(-1===y.inArray(e[n],t))return!1;var o=t.length;if(r!==o)for(n=0;o>n;n++)if(-1===y.inArray(t[n],e))return!1;return!0}function p(e){if(0==e.indexOf(\"{\")){var t=e.lastIndexOf(\"}\");return t>1&&e.substring(1,t)}return!1}function h(e){if(0==e.indexOf(\"(\")){var t=e.lastIndexOf(\")\");return-1!=t&&e.substring(1,t)||\"\"}return!1}function g(e,t){if(t.length){for(var n=0,r=e.length;r>n;n++)if(Q.isGroup(e[n]))return t.unshift(n,0),void e.splice.apply(e,t);e.push.apply(e,t)}}function f(e,t,n){var r=n.getChangedList();if(r.length){var o,i,a=[];return r.forEach(function(e){var n=e.name,r=t[n];r?r.value!=e.value&&(o=!0,t[n]=e):(t[n]=e,a.push(n),e.active&&(i=e))}),g(e,a),i&&(e.forEach(function(e){t[e].active=!1}),i.active=!0),o}}function m(e){return\"crt\"===e||\"pem\"===e?e:\"cer\"}var A=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},M=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};n(4),n(20),n(22);var w,v,b,y=n(18),x=n(24),T=n(57),C=n(196),N=n(197),I=n(215),E=n(513),D=n(514),S=n(601),L=n(604),k=n(569),j=n(568),U=n(609),B=n(612),R=n(615),z=n(200),Q=n(202),O=n(288),H=n(199),F=n(210),V=n(198),P=n(621),Y=n(627),G=n(206),W=n(628),X=n(293),_=n(629),q=n(630),J=n(636),K=n(639),Z=n(641),$=n(642),ee=n(209),te=n(645),ne=n(626),re=n(646),oe=n(650),ie=n(651),ae=n(656),se=n(213),le=n(214),ce=/^(?:[\\w-]+:\\/\\/)?temp(?:\\/([\\da-z]{64}|blank))?(?:\\.[\\w-]+)?$/,ue=/^(?:[\\w-]+:\\/\\/)?((?:[a-z]:[\\\\/]|\\/).+)$/i,de=\"Default\",pe=7,he=134217728,ge=37748736,fe=2097152,me=100,Ae=\".cm-js-type, .cm-js-http-url, .cm-string, .cm-js-at\",Me=/^\"(https?:)?(\\/\\/[^/]\\S+)\"$/i,we=/^@(https?:)?(\\/\\/[^/]\\S+)$/i,ve=[\"removeSelected\",\"exportWhistleFile\"],be={display:\"none\"},ye=/^(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)(?:-[\\w-]+)?$/,xe=window.location.search,Te=Q.getQuery(),Ce=\"client\"===Te.mode,Ne=Ce&&Q.isElectron&&ye.test(Te.v)?Te.v:\"\",Ie=!(!Te.hideMenus&&!Te.hideMenu),Ee=[\"Network\",\"Rules\",\"Values\",\"Plugins\"],De=/[\\w-]\\.(?:txt|csv|tsv|json|xml|yaml|yml|ini|conf|log|html|htm|css|js|py|java|c|cpp|h|sh|php|sql|md|markdown|rtf|tex|bib|vcf)$/i,Se=T.findDOMNode;window.setWhistleDataUrl=function(e){return e=o(e),e?(z.handleDataUrl?z.handleDataUrl(e):b=e,!0):!1},window.showWhistleMessage=function(e){G[e.level||e.type||\"info\"](e.text||e.msg||e.message)},window.showWhistleWebUI=function(e){-1!==Ee.indexOf(e)&&H.trigger(\"show\"+e)},/[&#?]showTreeView=(0|false|1|true)(?:&|$|#)/.test(xe)&&(v=\"1\"===RegExp.$1||\"true\"===RegExp.$1),/[&#?]hideLeft(?:Bar|Menu)=(0|false|1|true)(?:&|$|#)/.test(xe)?w=\"1\"===RegExp.$1||\"true\"===RegExp.$1:/[&#?]showLeft(?:Bar|Menu)=(0|false|1|true)(?:&|$|#)/.test(xe)&&(w=\"0\"===RegExp.$1||\"false\"===RegExp.$1);var Le=[{name:\"Scroll To Top\",action:\"top\"},{name:\"Scroll To Selected\",action:\"selected\"},{name:\"Scroll To Bottom\",action:\"bottom\"}],ke=[{name:\"Clear\",icon:\"remove\"},{name:\"Save\",icon:\"save-file\"},{name:\"Tree View\",multiple:!0},{name:\"Rules\",multiple:!0},{name:\"Plugins\",multiple:!0}],je=[{name:\"Import\",icon:\"import\",id:\"importRules\",title:\"Ctrl[Command] + I\"},{name:\"Export\",icon:\"export\",id:\"exportRules\"}],Ue=[{name:\"Import\",icon:\"import\",id:\"importValues\",title:\"Ctrl[Command] + I\"},{name:\"Export\",icon:\"export\",id:\"exportValues\"}],Be=[{name:\"Abort\",icon:\"ban-circle\",id:\"abort\"}],Re=x.createClass({displayName:\"Index\",getInitialState:function(){var e=this,t=e.props.modal,n=t.rules,r=t.values,o=t.server,i=[],a=Q.getCAHash(o,i),s={filename:\"\",replayCount:1,tabs:[],caType:m(F.get(\"caType\")),caHash:a,caUrlList:i,allowMultipleChoice:t.rules.allowMultipleChoice,backRulesFirst:t.rules.backRulesFirst,networkMode:!!o.networkMode,rulesMode:!!o.rulesMode,pluginsMode:!!o.pluginsMode,rulesOnlyMode:!!o.rulesOnlyMode,ndr:o.ndr,ndp:o.ndp,drb:o.drb,drm:o.drm,port:o.port,whistleId:o.whistleId,version:t.version};w!==!1&&(w=w||o.hideLeftMenu);var l=c(s);l&&-1==l.indexOf(\"rules\")?-1!=l.indexOf(\"values\")?(s.hasValues=!0,s.name=\"values\"):-1!=l.indexOf(\"plugins\")?(s.hasPlugins=!0,s.name=\"plugins\"):(s.hasNetwork=!0,s.name=\"network\"):(s.hasRules=!0,s.name=\"rules\");var u,d=[],p=[],h={},g=[],f=[],A={},M=F.get(\"rulesTheme\"),b=F.get(\"valuesTheme\"),y=F.get(\"rulesFontSize\"),x=F.get(\"valuesFontSize\"),T=F.get(\"showRulesLineNumbers\"),C=F.get(\"showValuesLineNumbers\"),N=F.get(\"autoRulesLineWrapping\"),I=F.get(\"autoValuesLineWrapping\");if(n){u=F.get(\"activeRules\")||n.current;var D=!n.defaultRulesIsDisabled;M||(M=n.theme),y||(y=n.fontSize),T||(T=n.showLineNumbers?\"true\":\"false\"),d.push(de),h.Default={name:de,fixed:!0,value:n.defaultRules,selected:D,isDefault:!0,active:u===de},p.push(h.Default),n.list.forEach(function(e){d.push(e.name),e=h[e.name]={name:e.name,value:e.data,selected:e.selected,active:u===e.name},p.push(e)})}r&&(u=F.get(\"activeValues\")||r.current,b||(b=r.theme),x||(x=r.fontSize),C||(C=r.showLineNumbers?\"true\":\"false\"),r.list.forEach(function(e){g.push(e.name),A[e.name]={name:e.name,value:e.data,active:u===e.name},f.push({name:e.name,icon:\"edit\"})}));var S=new E(d,h),L=new E(g,A),k=z.networkModal;z.setValuesModal(L),z.rulesModal=S,z.exportSessions=function(t,n,r){var o;\"string\"==typeof n?o=n:n&&(o=n.type,r=n.name||r),(\"saz\"===o||\"fiddler\"===o)&&(o=\"Fiddler\"),\"string\"!=typeof r&&(r=\"\"),e.exportSessions(o,r,t)},s.rulesTheme=M,s.valuesTheme=b,s.rulesFontSize=y,s.valuesFontSize=x,s.showRulesLineNumbers=\"true\"===T,s.showValuesLineNumbers=\"true\"===C,s.autoRulesLineWrapping=!!N,s.foldGutter=\"\"!==F.get(\"foldGutter\"),s.autoValuesLineWrapping=!!I,s.plugins=t.plugins,s.disabledPlugins=t.disabledPlugins,s.disabledAllRules=t.disabledAllRules,s.disabledAllPlugins=t.disabledAllPlugins,s.interceptHttpsConnects=t.interceptHttpsConnects,s.enableHttp2=t.enableHttp2,s.rules=S,s.network=k,s.rulesOptions=p,s.pluginsOptions=e.createPluginsOptions(t.plugins),z.valuesModal=s.values=L,s.valuesOptions=f,z.syncData=e.syncData,z.syncRules=e.syncRules,z.syncValues=e.syncValues,e.initPluginTabs(s,t.plugins),S.exists(z.activeRulesName)&&e.setRulesActive(z.activeRulesName,S),L.exists(z.activeValuesName)&&e.setValuesActive(z.activeValuesName,L),s.networkOptions=[{name:\"Remove All\",icon:\"remove\",id:\"removeAll\",disabled:!0,title:\"Ctrl[Command] + X\"},{name:\"Remove Selected\",id:\"removeSelected\",disabled:!0,title:\"Ctrl[Command] + D\"},{name:\"Remove Unselected\",id:\"removeUnselected\",disabled:!0,title:\"Ctrl[Command] + Shift + D\"},{name:\"Import\",icon:\"import\",id:\"importSessions\",title:\"Ctrl[Command] + I\"},{name:\"Export\",icon:\"export\",id:\"exportWhistleFile\",disabled:!0,title:\"Ctrl[Command] + S\"},{name:\"Show Tree View\",icon:\"tree-conifer\",id:\"toggleView\"}],s.helpOptions=[{name:\"Website\",href:Q.getDocUrl(),icon:\"link\"},{name:\"GitHub\",href:\"https://github.com/avwo/whistle\",icon:\"github\"},{name:\"Update\",href:Q.UPDATE_URL,icon:\"refresh\"},{name:\"Issue\",href:\"https://github.com/avwo/whistle/issues/new\",icon:\"record\"}],O.setPlugins(s),s.exportFileType=F.get(\"exportFileType\");var j=F.get(\"showLeftMenu\");return s.showLeftMenu=null==j?!0:j,Q.triggerPageChange(s.name),(v||v===!1)&&k.setTreeView(v,!0),H.on(\"importSessionsFromUrl\",function(t,n){e.importSessionsFromUrl(n)}),e.updateMenuView(s)},initPluginTabs:function(e,t){t=t||{};var n,r,o=e.tabs;try{n=JSON.parse(F.get(\"activePluginTabList\")),r=F.get(\"activePluginTabName\")}catch(i){}if(Array.isArray(n)){var a={};Object.keys(t).forEach(function(o){var i=t[o];o=o.slice(0,-1),-1!==n.indexOf(o)&&(r===o&&(e.active=o),a[o]={name:o,url:i.pluginHomepage||\"plugin.\"+o+\"/\"})}),n.forEach(function(e){e=e&&a[e],e&&o.push(e)})}},getListByName:function(e,t){var n=this.state[e].list,r=this.state[e].data;return{type:t,url:location.href,list:n.map(function(e){var t=r[e];return{name:e,value:t&&t.value||\"\"}})}},triggerRulesChange:function(e){Q.triggerListChange(\"rules\",this.getListByName(\"rules\",e))},triggerValuesChange:function(e){Q.triggerListChange(\"values\",this.getListByName(\"values\",e))},syncData:function(e,t){var n=this.state;this.refs.syncDialog.show(e,n.rules,n.values,t)},syncRules:function(e){var t=this;t.syncData(e,function(){t.refs.syncDialog.syncRules(e)})},syncValues:function(e){var t=this;t.syncData(e,function(){t.refs.syncDialog.syncValues(e)})},showKVDialog:function(e,t){e&&this.refs.syncDialog.showKVDialog(e,this.state.rules,this.state.values,t)},createPluginsOptions:function(e){e=e||{};var t=[{name:\"Home\"}];return Object.keys(e).sort(function(t,n){var r=e[t],o=e[n];return Q.compare(r.priority,o.priority)||Q.compare(o.mtime,r.mtime)||(t>n?1:-1)}).forEach(function(n){var r=e[n];t.push({name:n.slice(0,-1),icon:\"checkbox\",mtime:r.mtime,homepage:r.homepage,latest:r.latest,hideLongProtocol:r.hideLongProtocol,hideShortProtocol:r.hideShortProtocol,path:r.path,pluginVars:r.pluginVars})}),t},reloadRules:function(e,t){var n=this,r=F.get(\"activeRules\",!0)||e.current,o=[],i={};o.push(de),i.Default={name:de,fixed:!0,value:e.defaultRules,selected:!e.defaultRulesIsDisabled,isDefault:!0,active:r===de},e.list.forEach(function(e){o.push(e.name),e=i[e.name]={name:e.name,value:e.data,selected:e.selected,active:r===e.name}});var a=t&&f(o,i,n.state.rules);return n.state.rules.reset(o,i),n.setState({}),a},reloadValues:function(e,t){var n=this,r=F.get(\"activeValues\",!0)||e.current,o=[],i={};e.list.forEach(function(e){o.push(e.name),i[e.name]={name:e.name,value:e.data,active:r===e.name}});var a=t&&f(o,i,n.state.values);return n.state.values.reset(o,i),n.setState({}),a},reloadDataQuite:function(){this.reloadData(!0)},reloadData:function(e){var t=this,n=y(\".w-reload-data-tips\").closest(\".w-confirm-reload-dialog\"),r=n.find(\".w-reload-data-tips\").attr(\"data-name\"),o=\"rules\"===r;e=e===!0;var i=function(n,r){return n?void(o?(t.reloadRules(n,e)&&H.trigger(\"rulesChanged\",!0),t.triggerRulesChange(\"reload\")):(t.reloadValues(n,e)&&H.trigger(\"valuesChanged\",!0),t.triggerValuesChange(\"reload\"))):(!e&&Q.showSystemError(r,!0),setTimeout(function(){H.trigger(o?\"rulesChanged\":\"valuesChanged\",!0)},2e3))};o?(z.rules.list(i),H.trigger(\"reloadRulesRecycleBin\")):(z.values.list(i),H.trigger(\"reloadValuesRecycleBin\"))},showReloadRules:function(e){if(this.rulesChanged&&\"rules\"===this.state.name){this.rulesChanged=!1;var t=this.state.rules.hasChanged();this.showReloadDialog(\"Rules changed. Reload now?\",t,e)}},showReloadValues:function(e){if(this.valuesChanged&&\"values\"===this.state.name){this.valuesChanged=!1;var t=this.state.values.hasChanged();this.showReloadDialog(\"Values changed. Reload now?\",t,e)}},componentDidUpdate:function(){this.showReloadRules(),this.showReloadValues()},showReloadDialog:function(e,t,n){var r=this.refs.confirmReload;clearTimeout(this.reloadTimer);var o=y(\".w-reload-data-tips\");return o.attr(\"data-name\",this.state.name),n||r.isVisible()?(r.show(),t&&(e+='<p class=\"w-confim-reload-note\">Warning: Unsaved changes will be lost</p>'),void o.html(e)):void(this.reloadTimer=setTimeout(this.reloadDataQuite,1e3))},showTab:function(){var e=c(this.state);e&&-1==e.indexOf(\"rules\")?-1!=e.indexOf(\"values\")?this.showValues():-1!=e.indexOf(\"plugins\")?this.showPlugins():this.showNetwork():this.showRules(),F.set(\"pageName\",e||\"\")},switchTab:function(e){var t=this,n=t.state.name,r=[];t.hideNetwork||r.push(\"network\"),t.hideRules||r.push(\"rules\"),t.hideValues||r.push(\"values\"),t.hidePlugins||r.push(\"plugins\");var o=r.indexOf(n),i=r.length;switch(e?(o-=1,0>o&&(o=i-1)):(o+=1,o>=i&&(o=0)),r[o]){case\"network\":t.showNetwork();break;case\"rules\":t.showRules();break;case\"values\":t.showValues();break;case\"plugins\":t.showPlugins()}},componentDidMount:function(){function e(e){if(e=e&&e.trim()){var t=e.indexOf(\"://\")+3;if(e=-1!=t?e.substring(t):e,0===e.indexOf(\"{\"))return t=e.lastIndexOf(\"}\"),t>1?e.substring(1,t):null}}var t=this,n=new C(\".w-copy-text\");n.on(\"error\",function(e){ee.alert(\"Copy failed\")}),n=new C(\".w-copy-text-with-tips\"),n.on(\"error\",function(e){G.error(\"Copy failed\")}),n.on(\"success\",function(e){G.success(\"Copied clipboard\")});var r=function(e){e.preventDefault()};if(H.on(\"showRulesDialog\",function(e,n){n&&!t.isHideRules()&&t.refs.rulesDialog.show(n.rules,n.values)}),H.on(\"changeRecordState\",function(e,n){t.setState({record:n},t.updateList)}),H.on(\"showHttpsSettingsDialog\",t.showHttpsSettingsDialog),Ce){var s=function(e,t){return H.editorMatchedCount=0,H.trigger(t?\"findEditorPrev\":\"findEditorNext\",e),H.editorMatchedCount};window.__findWhistleCodeMirrorEditor_=s}var c,u;H.one(\"networkDidMount\",function(){u&&H.trigger(\"showComposerTab\")}),H.one(\"composerDidMount\",function(){c=!0,u&&(H.trigger(\"_setComposerData\",u),u=null)}),H.on(\"showPluginOptionTab\",function(e,n){n&&t.showPluginTab(Q.getSimplePluginName(n))}),H.on(\"disablePlugin\",function(e,n,r){t.setPluginState(Q.getSimplePluginName(n),r)}),H.on(\"setComposerData\",function(e,n){n&&!t.state.rulesMode&&ee.confirm(\"Do you confirm the changes to the composer's data?\",function(e){e&&(c?H.trigger(\"_setComposerData\",n):u=n)})}),H.on(\"showPluginOption\",function(e,n){if(n){var r=Q.getSimplePluginName(n),o=n.pluginHomepage||\"plugin.\"+r+\"/\";if((n.pluginHomepage||n.openExternal)&&!n.openInPlugins&&!n.openInModal)return window.open(o);var i=n.openInModal||\"\";i&&!n.pluginHomepage&&(o+=\"?openInModal=5b6af7b9884e1165\"),t.refs.iframeDialog.show({favicon:Q.getPluginIcon(n),name:r,url:o,homepage:n.homepage,disabled:Q.pluginIsDisabled(t.state,r),width:i.width,height:i.height})}}),H.on(\"hidePluginOption\",function(){t.refs.iframeDialog.hide()}),H.on(\"download\",function(e,n){t.download(n)}),H.on(\"showMockDialog\",function(e,n){n&&t.refs.mockDialog.show(n.item,n.type)}),H.on(\"enableRecord\",function(){t.enableRecord()}),H.on(\"showJsonViewDialog\",function(e,n,r){t.refs.jsonDialog.show(n,r)}),H.on(\"rulesChanged\",function(e,n){t.rulesChanged=!0,t.showReloadRules(n===!0)}),H.on(\"switchTreeView\",function(){t.toggleTreeView()}),H.on(\"updateGlobal\",function(){t.setState({})}),H.on(\"valuesChanged\",function(e,n){t.valuesChanged=!0,t.showReloadValues(n===!0)}),H.on(\"showNetwork\",function(){t.showNetwork()}),H.on(\"showRules\",function(e,n){t.showRules(),n&&t.state.rules.exists(n)&&(H.trigger(\"expandRulesGroup\",n),t.setRulesActive(n))}),H.on(\"showValues\",function(){t.showValues()}),H.on(\"showPlugins\",function(e,n){n&&\"string\"==typeof n&&(t.setState({active:\"Home\"}),setTimeout(function(){H.trigger(\"highlightPlugin\",n)},600)),t.showPlugins()}),H.on(\"disableAllPlugins\",t.disableAllPlugins),H.on(\"disableAllRules\",t.disableAllRules),H.on(\"activeRules\",function(){var e=z.rulesModal;e.exists(z.activeRulesName)&&(t.setRulesActive(z.activeRulesName,e),t.setState({}))}),H.on(\"activeValues\",function(){var e=z.valuesModal;e.exists(z.activeValuesName)&&(t.setValuesActive(z.activeValuesName,e),t.setState({}))});var p;H.on(\"openEditor\",function(e,n){if(\"1\"===F.get(\"viewAllInNewWindow\"))return Q.openInNewWin(n||\"\");try{if(p&&\"function\"==typeof p.setValue)return window.getTextFromWhistle_=null,t.refs.editorWin.show(),p.setValue(n);window._initWhistleTextEditor_=function(e){p=e,p.setValue(n)},t.refs.editorWin.show(\"editor.html\")}catch(r){}}),H.on(\"openInNewWin\",function(){try{Q.openInNewWin(p.getEditorValue()||\"\"),t.refs.editorWin.hide()}catch(e){}});var h;H.on(\"updateUIThrottle\",function(){h||(h=setTimeout(function(){h=null,t.setState({})},200))}),H.on(\"addNewRulesFile\",function(e,n){var r=n.filename,o=t.state.rules,i=o.add(r,n.data);o.setChanged(r,!1),t.setRulesActive(r),t.setState({activeRules:i}),n.update||t.triggerRulesChange(\"create\")}),H.on(\"addNewValuesFile\",function(e,n){var r=n.filename,o=t.state.values,i=o.add(r,n.data);o.setChanged(r,!1),n.update?t.setState({}):(t.setValuesActive(r),t.setState({activeValues:i}),t.triggerValuesChange(\"create\"))}),H.on(\"recoverRules\",function(e,n){var r=t.state.rules,o=n.filename,i=function(e){e&&z.rules.add({name:o,value:n.data,recycleFilename:n.name},function(e,i){if(e&&0===e.ec){var a=r.add(o,n.data);t.setRulesActive(o),t.setState({activeRules:a}),t.triggerRulesChange(\"create\"),H.trigger(\"rulesRecycleList\",e),H.trigger(\"focusRulesList\")}else Q.showSystemError(i)})};return r.exists(o)?void ee.confirm(\"The name '\"+o+\"' is already in use. Overwrite?\",i):i(!0)}),H.on(\"recoverValues\",function(e,n){var r=t.state.values,o=n.filename,i=function(e){e&&z.values.add({name:o,value:n.data,recycleFilename:n.name},function(e,i){if(e&&0===e.ec){var a=r.add(o,n.data);t.setValuesActive(o),t.setState({activeValues:a}),t.triggerValuesChange(\"create\"),H.trigger(\"valuesRecycleList\",e)}else Q.showSystemError(i)})};return r.exists(o)?void ee.confirm(\"The name '\"+o+\"' is already in use. Overwrite?\",i):i(!0)}),H.on(\"networkImportFile\",function(e,n){t.uploadSessionsForm(n)}),H.on(\"networkImportData\",function(e,n){t.importAnySessions(n)}),H.on(\"rulesImportFile\",function(e,n){l(n,t.handleImportRules)}),H.on(\"rulesImportData\",function(e,n){t.handleImportRules(n)}),H.on(\"valuesImportFile\",function(e,n){l(n,t.handleImportValues)}),H.on(\"valuesImportData\",function(e,n){t.handleImportValues(n)}),H.on(\"networkSettingsImportFile composerImportFile rulesSettingsImportFile valuesSettingsImportFile\",function(e,t){l(t,Q.noop,e.type)}),H.on(\"networkSettingsImportData composerImportData rulesSettingsImportFile valuesSettingsImportFile\",function(e,t){Q.handleImportData(t)}),H.on(\"setRulesSettings\",function(e,n){n&&ee.confirm(\"Do you confirm the changes to the rules settings?\",function(e){e&&(t.setState({rulesTheme:n.theme,rulesFontSize:n.fontSize,showRulesLineNumbers:n.lineNumbers,autoRulesLineWrapping:n.autoLineWrapping}),F.set(\"rulesTheme\",o(n.theme).substring(0,30)),F.set(\"rulesFontSize\",o(n.fontSize).substring(0,30)),F.set(\"showRulesLineNumbers\",!!n.lineNumbers),F.set(\"autoRulesLineWrapping\",n.autoLineWrapping?\"1\":\"\"),t.setMultipleCohice(n.allowMultipleChoice),t.setBackRulesFirst(n.backRulesFirst))})}),H.on(\"setValuesSettings\",function(e,n){n&&ee.confirm(\"Do you confirm the changes to the values settings?\",function(e){e&&(t.setState({valuesTheme:n.theme,valuesFontSize:n.fontSize,showValuesLineNumbers:n.lineNumbers,autoValuesLineWrapping:n.autoLineWrapping,foldGutter:n.foldGutter}),F.set(\"valuesTheme\",o(n.theme).substring(0,30)),F.set(\"valuesFontSize\",o(n.fontSize).substring(0,10)),F.set(\"showValuesLineNumbers\",!!n.lineNumbers),F.set(\"autoValuesLineWrapping\",n.autoLineWrapping?\"1\":\"\"),F.set(\"foldGutter\",n.foldGutter?\"1\":\"\"))})}),y(document).on(\"dragleave\",r).on(\"dragenter\",r).on(\"dragover\",r).on(\"drop\",function(e){e.preventDefault();var n=e.originalEvent.dataTransfer.files,r=n&&n[0];if(r){var o=e.target;\"TEXTAREA\"===o.nodeName&&(e.preventDefault(),o.readOnly=!0,setTimeout(function(){o.readOnly=!1},0)),o=y(o);var i=o.closest(\".w-fix-drag\").find(\"iframe\")[0];if(i)try{var a=i.contentWindow;if(a&&\"function\"==typeof a.onWhistleFileDrop)return a.onWhistleFileDrop(r)}catch(e){console.error(e)}if(y(\".w-show-upload-temp-file.in\").length)return H.trigger(\"uploadTempFile\",r);if(y(\".w-import-dialog.in\").length)return H.trigger(\"importFile\",r);var s=t.state.name,c=r.name;if(\"network\"===s){if(o.closest(\".w-frames-com\").length)return;return/\\.log$/i.test(c)?r.size>fe?a.alert(\"Maximum file size: 2MB\"):void Q.readFileAsText(r,function(e){e=Q.parseLogs(e),e&&(null!==z.uploadLogs&&(z.uploadLogs=e),H.trigger(\"showLog\"),H.trigger(\"uploadLogs\",{logs:e}))}):t.uploadSessionsForm(r)}l(r,function(e){e&&(\"rules\"===s?t.handleImportRules(e):\"values\"===s&&t.handleImportValues(e))})}}).on(\"keyup\",function(e){if((e.metaKey||e.ctrlKey)&&82===e.keyCode)!Ce&&e.preventDefault();else if(191===e.keyCode){var n=t.state.name,r=document.activeElement&&document.activeElement.nodeName;if(\"INPUT\"!==r&&\"TEXTAREA\"!==r&&!y(\".modal.in\").length)if(\"network\"===n){if(!Q.hasShortcut(\"focusNetworkSearchBox\"))return;H.trigger(\"focusNetworkFilterInput\")}else if(\"rules\"===n){if(!Q.hasShortcut(\"focusRulesSearchBox\"))return;H.trigger(\"focusRulesFilterInput\")}else if(\"values\"===n){if(!Q.hasShortcut(\"focusValuesSearchBox\"))return;H.trigger(\"focusValuesFilterInput\")}}}).on(\"contextmenu\",\".w-textarea-bar\",function(e){e.preventDefault()});var g=function(e){var n=e.target;\"A\"==n.nodeName&&y(n).parent().hasClass(\"w-list-data\")&&(\"rules\"==t.state.name?t.removeRules():t.removeValues()),e.preventDefault()};y(window).on(\"hashchange\",t.showTab).on(\"keyup\",function(e){if(27==e.keyCode){t.setMenuOptionsState();var n=y(\".modal\");\"function\"==typeof n.modal&&n.modal(\"hide\")}}).on(\"keydown\",function(e){var n=t.state.name,r=e.keyCode;if(46==r&&g(e),!e.ctrlKey&&!e.metaKey)return void(112===r?(e.preventDefault(),window.open(Q.getDocUrl(\"gui/\"+n+\".html\"))):116===r&&e.preventDefault());var o=37===r;if(o||39===r){if(!Q.hasShortcut(o?\"switchTabReverse\":\"switchTab\"))return;return t.switchTab(o),e.preventDefault()}if(79===r){if(\"network\"===n){if(!Q.hasShortcut(\"toggleNetworkState\"))return;H.trigger(\"toggleNetworkState\")}else if(\"rules\"===n){if(!Q.hasShortcut(\"toggleRules\"))return;t.confirmDisableAllRules()}else if(\"plugins\"===n){if(!Q.hasShortcut(\"togglePlugins\"))return;t.confirmDisableAllPlugins()}e.preventDefault()}else if(76===r){if(\"network\"===n){if(!Q.hasShortcut(\"toggleNetworkPanelLayout\"))return;H.trigger(\"toggleNetworkDock\")}else if(\"rules\"===n){if(!Q.hasShortcut(\"toggleRulesNum\"))return;H.trigger(\"toggleRulesLineNumbers\")}else if(\"values\"===n){if(!Q.hasShortcut(\"toggleValuesNum\"))return;H.trigger(\"toggleValuesLineNumbers\")}e.preventDefault()}else if(82===r)!Ce&&e.preventDefault();else if(77===r)t.toggleLeftMenu(),e.preventDefault();else if(66===r){if(!Q.hasShortcut(\"switchNetworkView\"))return;return t.toggleTreeView(),e.preventDefault(),void H.trigger(\"toggleTreeViewByAccessKey\")}var i=\"network\"===n;if(i&&88==r)return void(Q.isFocusEditor()||y(e.target).closest(\".w-frames-list\").length||!Q.hasShortcut(\"clearNetworkSessions\")||t.clear());if(68==r){if(!Q.hasShortcut(i?\"removeNetworkSessions\":\"rules\"===n?\"removeRules\":\"removeValues\"))return;return g(e)}var a=t.state.network;if(i&&(83===r||69===r)){if(83===r){if(!Q.hasShortcut(\"saveNetwork\"))return;return e.preventDefault(),void(Q.noModal()&&H.trigger(\"saveSessions\"))}if(e.preventDefault(),!Q.noModal())return void(y(Se(t.refs.chooseFileType)).is(\":visible\")&&t.exportBySave());var s=e.target.nodeName;if(\"INPUT\"===s||\"TEXTAREA\"===s)return;var l=a.hasSelected();return void(l&&(y(Se(t.refs.chooseFileType)).modal(\"show\"),setTimeout(function(){var e=Se(t.refs.sessionsName);e.focus(),e.select()},500)))}if(69===r){if(!Q.hasShortcut(i?\"exportNetwork\":\"rules\"===n?\"exportRules\":\"exportValues\"))return;return e.preventDefault(),Q.noModal()&&t.exportData()}if(190===r){if(!Q.hasShortcut(i?\"openNetworkSettings\":\"rules\"===n?\"openRulesSettings\":\"openValuesSettings\"))return;return t.showSettings(),e.preventDefault()}var c=74===r;if(c||73===r){if(Q.noModal())if(c){if(!z.whistleId||!Q.hasShortcut(\"openService\"))return;t.showService()}else if(i||\"rules\"===n||\"values\"===n){if(!Q.hasShortcut(i?\"importNetwork\":\"rules\"===n?\"importRules\":\"importValues\"))return;t.importData()}else if(\"plugins\"===n){if(!Q.hasShortcut(\"openInstallPlugins\"))return;H.trigger(\"installPlugins\")}e.preventDefault()}});var f=function(){var e=t.state.name;return\"rules\"===e||\"values\"===e};y(document.body).on(\"mouseenter\",Ae,function(t){if(f()&&(t.ctrlKey||t.metaKey)){var n,r=y(this);(r.hasClass(\"cm-js-http-url\")||r.hasClass(\"cm-string\")||r.hasClass(\"cm-js-at\")||ce.test(n=r.text())||i(n)||e(n))&&r.addClass(\"w-is-link\")}}).on(\"mouseleave\",Ae,function(e){y(this).removeClass(\"w-is-link\")}).on(\"mousedown\",Ae,function(n){if(f()&&(n.ctrlKey||n.metaKey)){var r=y(this),o=r.text();if(r.hasClass(\"cm-js-at\"))return void(we.test(o)&&window.open((RegExp.$1||\"http:\")+RegExp.$2));if(r.hasClass(\"cm-string\"))return void(Me.test(o)&&window.open((RegExp.$1||\"http:\")+RegExp.$2));if(r.hasClass(\"cm-js-http-url\"))return/^https?:\\/\\//i.test(o)||(o=\"http:\"+(\"/\"===o[0]?\"\":\"//\")+o),void window.open(o);if(ce.test(o)||i(o)&&ue.test(o)){var a=RegExp.$1;return H.trigger(\"showEditorDialog\",[{ruleName:t.getActiveRuleName(),tempFile:a},r])}var s=e(o);if(s){var l=t.state.rules.getActive(),c=l&&l.value,u={};return Q.resolveInlineValues(c,u),null==u[s]?H.trigger(\"showEditorDialog\",{name:s}):ee.confirm(\"The value of '\"+s+\"' is stored in this rule file and cannot be synced if edited via the Values's editor. Continue?\",function(e){e&&H.trigger(\"showEditorDialog\",{name:s})})}}}),\"network\"==t.state.name&&t.startLoadData(!0),z.on(\"settings\",function(e){var n=t.state,r=e.server,o=n.whistleId!==r.whistleId;o&&(n.whistleId=r.whistleId);var i=[],a=Q.getCAHash(r,i);if(a!==n.caHash&&(n.caHash=a,n.caUrlList=i,o=!0),n.interceptHttpsConnects!==e.interceptHttpsConnects||n.enableHttp2!==e.enableHttp2||n.disabledAllRules!==e.disabledAllRules||n.allowMultipleChoice!==e.allowMultipleChoice||n.disabledAllPlugins!==e.disabledAllPlugins||n.backRulesFirst!==e.backRulesFirst||n.ndp!=r.ndp||n.ndr!=r.ndr||n.drb!=r.drb||n.drm!=r.drm||n.port!=r.port){n.interceptHttpsConnects=e.interceptHttpsConnects,n.enableHttp2=e.enableHttp2,n.disabledAllRules=e.disabledAllRules,n.allowMultipleChoice=e.allowMultipleChoice,n.backRulesFirst=e.backRulesFirst,n.disabledAllPlugins=e.disabledAllPlugins,n.ndp=r.ndp,n.ndr=r.ndr,n.drb=r.drb,n.drm=r.drm,n.port=r.port,O.setPlugins(n);var s=ke;return s[3].checked=!n.disabledAllRules,s[4].checked=!n.disabledAllPlugins,t.refs.contextMenu.update(),t.setState({})}o&&t.setState({})}),z.on(\"rules\",function(e){var n=t.state.rules,r=e.list;e.defaultRulesIsDisabled||-1!==r.indexOf(\"Default\")||r.unshift(\"Default\");var o=n.getSelectedNames();d(o,r)||(t.reselectRules(e,!0),t.setState({}))}),z.on(\"serverInfo\",function(e){t.serverInfo=e}),H.on(\"autoRefreshNetwork\",function(){!t.state.network.isTreeView&&t.autoRefresh&&t.autoRefresh()});var m=function(e){if(Array.isArray(e))return e;if(e&&!e.selected)return[e]};H.on(\"updateUI\",function(){t.setState({})}),H.on(\"replaySessions\",function(e,n,r){var o=t.state.network,i=m(n)||o.getSelectedList(),a=i&&i.length;return r&&1===a?(t.replayList=i,t.refs.setReplayCount.show(),void setTimeout(function(){var e=Se(t.refs.replayCount);e.select(),e.focus()},300)):void t.replay(e,i)}),H.on(\"filterSessions\",t.showSettings),H.on(\"exportSessions\",function(e,n,r){t.exportData(e,m(n),r)}),H.on(\"abortRequest\",function(e,n){t.abort(m(n))}),H.on(\"removeIt\",function(e,n){var r=t.state.network;n&&r&&(r.remove(n),t.setState({}))}),H.on(\"removeOthers\",function(e,n){var r=t.state.network;n&&r&&(n.selected?r.removeUnselectedItems():r.removeOthers(n),t.setState({}))}),H.on(\"clearAll\",t.clear),H.on(\"removeSelected\",function(){var e=t.state.network;e&&(e.removeSelectedItems(),t.setState({}))}),H.on(\"removeUnselected\",function(){var e=t.state.network;e&&(e.removeUnselectedItems(),t.setState({}))}),H.on(\"removeUnmarked\",function(){var e=t.state.network;e&&(e.removeUnmarkedItems(),t.setState({}))}),H.on(\"saveRules\",function(e,n){if(n.changed||!n.selected){var r=t.state.rules.getChangedGroupList(n);r.forEach(t.selectRules)}else t.unselectRules(n)}),H.on(\"saveValues\",function(e,n){var r=t.state.values.getChangedGroupList(n);r.forEach(t.saveValues)}),H.on(\"renameRules\",function(e,n){t.showEditRules(n)}),H.on(\"renameValues\",function(e,n){t.showEditValues(n)}),H.on(\"deleteRules\",function(e,n){setTimeout(function(){t.removeRules(n)},0)}),H.on(\"deleteValues\",function(e,n){setTimeout(function(){t.removeValues(n)},0)}),H.on(\"createRules\",t.showCreateRules),H.on(\"createValues\",t.showCreateValues),H.on(\"showImportDialog\",function(e,n){t.refs.importDialog.show(n||t.state.name)}),H.on(\"showExportDialog\",function(e,n,r){t.refs.exportDialog.show(n||t.state.name,r)}),H.on(\"exportData\",t.exportData),H.on(\"handleImportRules\",function(e,n){t.handleImportRules(n)}),H.on(\"handleImportValues\",function(e,n){t.handleImportValues(n)}),H.on(\"uploadRules\",function(e,n){var r=a(n);r.append(\"replaceAll\",\"1\"),z.upload.importRules(r,function(e,n){e?0===e.ec?(t.reloadRules(e),G.success(\"Rules imported successfully\")):ee.alert(e.em):Q.showSystemError(n)})}),H.on(\"uploadValues\",function(e,n){var r=a(n,\"values\");r.append(\"replaceAll\",\"1\"),z.upload.importValues(r,function(e,n){e||Q.showSystemError(n),0===e.ec?(t.reloadValues(e),G.success(\"Values imported successfully\")):ee.alert(e.em)})});var A,M,w=document.hidden;y(document).on(\"visibilitychange\",function(){clearTimeout(A);var e=\"network\"===t.state.name;return document.hidden||!e?void(e&&w!==document.hidden&&(w=!0,M=t.scrollerAtBottom&&t.scrollerAtBottom())):(w=!1,void(A=setTimeout(function(){var e=M||t.scrollerAtBottom&&t.scrollerAtBottom();M=!1,t.setState({},function(){e&&t.autoRefresh()})},100)))}),setTimeout(function(){z.checkUpdate(function(e){e&&e.showUpdate&&t.setState({version:e.version,latestVersion:e.latestVersion},function(){y(Se(t.refs.showUpdateTipsDialog)).modal(\"show\")})})},1e4),z.getLogIdList=this.getLogIdListFromRules,z.importAnySessions=t.importAnySessions,z.on(\"plugins\",function(e){var n=t.createPluginsOptions(e.plugins),r=t.state.pluginsOptions,o=t.state.disabledPlugins,i=t.state.disabledAllPlugins,a=e.disabledPlugins;if(i==e.disabledAllPlugins&&n.length==r.length){for(var s,l=0,c=n.length;c>l;l++){var u=n[l],d=r[l];if(u.name!=d.name||u.latest!==d.latest||u.mtime!=d.mtime||o[u.name]!=a[u.name]||u.hideLongProtocol!=d.hideLongProtocol||u.hideShortProtocol!=d.hideShortProtocol||u.path!=d.path){s=!0;break}}if(!s)return}var p=t.state.plugins;p&&e.plugins&&Object.keys(e.plugins).forEach(function(t){var n=p[t];if(n){var r=e.plugins[t];r.selectedRulesHistory=n.selectedRulesHistory,r.selectedValuesHistory=n.selectedValuesHistory}});var h={plugins:e.plugins,disabledPlugins:e.disabledPlugins,pluginsOptions:n,disabledAllPlugins:e.disabledAllPlugins};O.setPlugins(h),t.setState(h)});try{var v=window.parent.onWhistleReady;if(\"function\"==typeof v){var x=function(e){var n=e&&t.state.network,r=n&&n.getList().indexOf(e);r>=0&&H.trigger(\"selectedIndex\",r)},T=function(e){H.trigger(\"selectedIndex\",e)};v({url:location.href,pageId:z.getPageId(),compose:z.compose,createComposeInterrupt:z.createComposeInterrupt,importSessions:t.importAnySessions,importHarSessions:t.importHarSessions,clearSessions:t.clear,selectIndex:T,selectItem:x,setActive:function(e){e>=0?T(e):x(e)}})}}catch(N){}t.handleDataUrl(b||Q.getDataUrl()),z.handleDataUrl=t.handleDataUrl,b=null;var I,E,D,S,L=6e3,k=function j(){if(!z.whistleId)return I=E=D=null,setTimeout(j,L);S!==z.whistleId&&(I=E=D=null,S=z.whistleId);var e,n=JSON.stringify(t.refs.networkSettings.getSettings()),r=JSON.stringify(t.getRulesSettings()),o=JSON.stringify(t.getValuesSettings());return I!==n&&(e={networkSettings:n}),E!==r&&(e=e||{},e.rulesSettings=r),\nD!==o&&(e=e||{},e.valuesSettings=o),e?(e.type=\"settings\",void z.saveToService(e,function(e){setTimeout(j,L),e&&0===e.ec&&(D=o,E=r,I=n)})):setTimeout(j,L)};setTimeout(k,L)},shouldComponentUpdate:function(e,t){var n=this.state.name;return\"network\"===n&&t.name!==n&&(this._isAtBottom=this.scrollerAtBottom&&this.scrollerAtBottom()),!0},handleDataUrl:function(e){if(e=o(e)){var t=this;z.getRemoteData(e,function(e,n){e||t.importAnySessions(n)})}},importAnySessions:function(e){if(e&&!Q.handleImportData(e)){var t=Array.isArray(e);t||Array.isArray(e.log&&e.log.entries)||(t=!0,e=[e]),Array.isArray(e)?z.addNetworkList(e):this.importHarSessions(e)}},donotShowAgain:function(){z.donotShowAgain()},hideUpdateTipsDialog:function(){y(Se(this.refs.showUpdateTipsDialog)).modal(\"hide\")},getAllRulesText:function(){var e=\" \"+this.getAllRulesValue();return e.replace(/#[^\\r\\n]*[\\r\\n]/g,\"\\n\")},getLogIdListFromRules:function(){var e=this.getAllRulesText();if(e=e.match(/\\slog:\\/\\/(?:\\{[^\\s]{1,36}\\}|[^/\\\\{}()<>\\s]{1,36})\\s/g)){var t={};e=e.map(function(e){return e=Q.removeProtocol(e.trim()),\"{\"===e[0]&&(e=e.slice(1,-1)),e}).filter(function(e){return e?t[e]?!1:(t[e]=1,!0):!1})}return e},getWeinreFromRules:function(){var e=this.state.values,t=this.getAllRulesText();if(t=t.match(/(?:^|\\s)weinre:\\/\\/[^\\s#]+(?:$|\\s)/gm)){var n={};t=t.map(function(t){t=Q.removeProtocol(t.trim());var n=h(t);if(n!==!1)return n;var r=p(t);return r!==!1?(r=e.get(r),r&&r.value):t}).filter(function(e){return e?n[e]?!1:(n[e]=1,!0):!1})}return t},getValuesFromRules:function(){var e=\" \"+this.getAllRulesValue();return(e=e.match(/\\s(?:[\\w-]+:\\/\\/)?\\{[^\\s#]+\\}/g))&&(e=e.map(function(e){return p(Q.removeProtocol(e.trim()))}).filter(function(e){return!!e})),e},getAllRulesValue:function(){var e=[],t=[],n=[],r=this.state.rules;return r.list.forEach(function(o){var i=r.get(o),a=i.value||\"\";i.active?t.push(a):i.selected?n.push(a):e.push(a)}),r=this.state.values,r.list.forEach(function(t){/\\.rules$/.test(t)&&e.push(r.get(t).value)}),t.concat(n).concat(e).join(\"\\r\\n\")},preventBlur:function(e){\"INPUT\"!=e.target.nodeName&&e.preventDefault()},startLoadData:function(e){function t(e,i){e=e||o.state.network,clearTimeout(a),a=null,\"network\"==o.state.name&&(i=i||r(),e.update(i)&&i&&(a=setTimeout(t,3e3)),document.hidden||o.setState({},function(){i&&n()}))}function n(e){(e||!o.state.network.isTreeView)&&(l.scrollTop=1e7)}function r(e){var t=s.find(\".ReactVirtualized__Grid__innerScrollContainer\")[0];if(!t)return e&&H.trigger(\"toggleBackToBottomBtn\",!1),!0;var n=l.offsetHeight+5,r=t.offsetHeight,o=l.scrollTop+n>r;return H.trigger(\"toggleBackToBottomBtn\",!o&&r>=n),o}var o=this;if(o._updateNetwork)return void(e?o._updateNetwork():setTimeout(o._updateNetwork,30));var i,a,s=y(\".w-req-data-list .ReactVirtualized__Grid:first\").scroll(function(){var e=o.state.network;i&&clearTimeout(i),i=null,r()&&(i=setTimeout(function(){t(e,!0)},1e3))}),l=s[0];this.container=s,z.on(\"data\",t),y(document).on(\"dblclick\",\".w-network-menu-list\",function(e){y(e.target).hasClass(\"w-network-menu-list\")&&(l.scrollTop<1?n(!0):l.scrollTop=0)}),o._updateNetwork=t,o.autoRefresh=n,o.scrollerAtBottom=r,H.on(\"checkAtBottom\",r)},showPlugins:function(e){\"plugins\"!=this.state.name?(this.setMenuOptionsState(),this.hidePluginsOptions()):e&&!this.state.showLeftMenu&&this.showPluginsOptions(),this.setState({hasPlugins:!0,name:\"plugins\"}),Q.changePageName(\"plugins\")},handleAction:function(e){if(\"top\"===e)return void(this.container[0].scrollTop=0);if(\"bottom\"===e)return this.autoRefresh(!0);if(\"pause\"===e)return H.trigger(\"changeRecordState\",e),z.pauseNetworkRecord();var t=\"refresh\"===e;return t?H.trigger(\"changeRecordState\"):H.trigger(\"changeRecordState\",\"stop\"),z.stopNetworkRecord(!t),t?this.autoRefresh():void 0},showNetwork:function(e){var t=this;return\"network\"==t.state.name?void(e&&!t.state.showLeftMenu&&t.showNetworkOptions()):(t.setMenuOptionsState(),t.setState({hasNetwork:!0,name:\"network\"},function(){t.startLoadData(),t._isAtBottom&&(t._isAtBottom=!1,t.autoRefresh&&t.autoRefresh())}),void Q.changePageName(\"network\"))},handleNetwork:function(e,t){var n=this.state.network;\"removeAll\"==e.id?this.clear():\"removeSelected\"==e.id?n.removeSelectedItems():\"removeUnselected\"==e.id?n.removeUnselectedItems():\"exportWhistleFile\"==e.id?this.exportData():\"toggleView\"===e.id?this.toggleTreeView():\"importSessions\"===e.id&&this.importData(),this.hideNetworkOptions()},importData:function(){this.refs.importDialog.show(this.state.name)},getRulesSettings:function(){var e=this.state;return{type:\"setRulesSettings\",theme:e.rulesTheme||\"cobalt\",fontSize:e.rulesFontSize||\"14px\",lineNumbers:!!e.showRulesLineNumbers,autoLineWrapping:!!e.autoRulesLineWrapping,allowMultipleChoice:!!e.allowMultipleChoice,backRulesFirst:!!e.backRulesFirst}},getValuesSettings:function(){var e=this.state;return{type:\"setValuesSettings\",theme:e.valuesTheme||\"cobalt\",fontSize:e.rulesFontSize||\"14px\",lineNumbers:!!e.showValuesLineNumbers,autoLineWrapping:!!e.autoValuesLineWrapping,foldGutter:!!e.foldGutter}},importRulesSettings:function(){this.refs.importDialog.show(\"rulesSettings\")},exportRulesSettings:function(){this.refs.exportDialog.show(\"rulesSettings\",this.getRulesSettings())},importValuesSettings:function(){this.refs.importDialog.show(\"valuesSettings\")},exportValuesSettings:function(){this.refs.exportDialog.show(\"valuesSettings\",this.getValuesSettings())},getInputValue:function(){return Q.formatFilename(Se(this.refs.sessionsName).value.trim())},filterFilename:function(e){this.setState({filename:Q.formatFilename(e.target.value)})},exportData:function(e,t,n){switch(this.state.name){case\"network\":var r=this.state.network,o=Array.isArray(t)||r.hasSelected();if(this.currentFoucsItem=t,o){y(Se(this.refs.chooseFileType)).modal(\"show\");var i=Se(this.refs.sessionsName);n&&\"string\"==typeof n&&(i.value=n),setTimeout(function(){i.focus(),i.select()},500)}else G.info(\"Please select one or more sessions first\");break;case\"rules\":this.showAndActiveRules({id:\"exportRules\"});break;case\"values\":this.showAndActiveValues({id:\"exportValues\"})}},showService:function(){Q.showService(this.state.name)},importSessionsFromUrl:function(e){var t=this;e&&z.getRemoteData(e,function(e,n){e||t.importAnySessions(n)})},handleImportRules:function(e){e&&!Q.handleImportData(e)&&this.showKVDialog(e)},handleImportValues:function(e){e&&!Q.handleImportData(e)&&this.showKVDialog(e,!0)},showAndActiveRules:function(e,t){if(\"rules\"===this.state.name)switch(e.id){case\"exportRules\":this.refs.selectRulesDialog.show();break;case\"importRules\":this.importData()}else this.setRulesActive(e.name),this.showRules();this.hideRulesOptions()},showRules:function(e){\"rules\"!=this.state.name?(this.setMenuOptionsState(),this.hideRulesOptions()):e&&!this.state.showLeftMenu&&this.showRulesOptions(e),this.setState({hasRules:!0,name:\"rules\"}),Q.changePageName(\"rules\")},showAndActiveValues:function(e,t){var n=this;if(\"values\"===n.state.name&&e.id)switch(e.id){case\"exportValues\":n.refs.selectValuesDialog.show();break;case\"importValues\":this.importData()}else{var r=n.state.values,o=e.name;r.exists(o)?n.setValuesActive(o):z.values.add({name:o},function(e,t){if(e&&0===e.ec){var i=r.add(o);n.setValuesActive(o),n.setState({activeValues:i}),H.trigger(\"focusValuesList\")}else Q.showSystemError(t)}),this.showValues()}n.hideValuesOptions()},addValue:function(){},showValues:function(e){\"values\"!=this.state.name?(this.setMenuOptionsState(),this.hideValuesOptions()):e&&!this.state.showLeftMenu&&this.showValuesOptions(e),this.setState({hasValues:!0,name:\"values\"}),Q.changePageName(\"values\")},showNetworkOptions:function(){\"network\"==this.state.name&&this.setState({showNetworkOptions:!0})},hideNetworkOptions:function(){this.setState({showAbortOptions:!1,showNetworkOptions:!1})},showAbortOptions:function(){var e=this.state.network,t=e.getSelectedList();Be[0].disabled=!t||!t.filter(Q.canAbort).length,this.setState({showAbortOptions:!0})},showCreateOptions:function(){this.setState({showCreateOptions:!0})},hideCreateOptions:function(){this.setState({showCreateOptions:!1})},hideAbortOptions:function(){this.setState({showAbortOptions:!1})},showHelpOptions:function(){this.setState({showHelpOptions:!0})},hideHelpOptions:function(){this.setState({showHelpOptions:!1})},showHasNewVersion:function(e){this.setState({hasNewVersion:e})},showRulesOptions:function(e){var t,n=this,r=n.state.rules,o=r.data,i=r.list;if(\"rules\"===n.state.name){var a=i.length;je[0].disabled=2>a,je[1].disabled=1>a,t=je}else t=[],i.forEach(function(e){t.push(o[e])});n.setState({rulesOptions:t,showRulesOptions:!0})},hideRulesOptions:function(){this.setState({showRulesOptions:!1})},showValuesOptions:function(e){var t,n=this,r=this.state.values.list;if(\"values\"===n.state.name){var o=r.length;Ue[0].disabled=2>o,Ue[1].disabled=1>o,t=Ue}else{t=[];var i=n.getValuesFromRules()||[];i=Q.unique(r.concat(i));var a=[];i.forEach(function(e){var n=-1!=r.indexOf(e),o={name:e,icon:n?\"edit\":\"plus\"};n?t.push(o):a.push(o)}),t=a.concat(t)}n.setState({valuesOptions:t,showValuesOptions:!0})},hideValuesOptions:function(){this.setState({showValuesOptions:!1})},showAndActivePlugins:function(e){this.hidePluginsOptions(),this.showPlugins(),this.showPluginTab(e.name)},showPluginTab:function(e){var t=\"Home\",n=this.state.tabs||[];if(e&&e!=t)for(var r=0,o=n.length;o>r;r++)if(n[r].name==e){t=e,e=null;break}var i=e&&this.state.plugins[e+\":\"];if(i){if(n.length>=pe)return ee.alert(\"Maximum \"+pe+\" tabs allowed\"),this.showPlugins();if(t=e,i.pluginHomepage&&!i.openInPlugins)return window.open(i.pluginHomepage);n.push({name:e,url:i.pluginHomepage||\"plugin.\"+e+\"/\"})}this.setState({active:t,tabs:n}),this.updatePluginTabInfo(n,t)},updatePluginTabInfo:function(e,t){e=e.map(function(e){return e.name}),F.set(\"activePluginTabList\",JSON.stringify(e)),t&&F.set(\"activePluginTabName\",t)},activePluginTab:function(e){this.showPluginTab(y(e.target).attr(\"data-name\"))},closePluginTab:function(e){for(var t=y(e.target).attr(\"data-name\"),n=this.state.tabs||[],r=0,o=n.length;o>r;r++)if(n[r].name==t){n.splice(r,1);var i=this.state.active;if(i==t){var a=n[r]||n[r-1];this.state.active=a?a.name:null}return this.setState({tabs:n}),void this.updatePluginTabInfo(n)}},showPluginsOptions:function(e){this.setState({showPluginsOptions:!0})},hidePluginsOptions:function(){this.setState({showPluginsOptions:!1})},showWeinreOptionsQuick:function(e){var t=this.getWeinreFromRules();return t&&t.length?(y(e.target).closest(\"div\").addClass(\"w-menu-wrapper-show\"),void Q.shakeElem(y(Se(this.refs.weinreMenuItem)))):void this.showAnonymousWeinre()},showWeinreOptions:function(e){var t=this,n=t.state.weinreOptions=t.getWeinreFromRules()||[];t.state.weinreOptions=Q.unique(n).map(function(e){return{name:e,icon:\"console\"}}),t.setState({showWeinreOptions:!0})},hideWeinreOptions:function(){this.setState({showWeinreOptions:!1})},setMenuOptionsState:function(e,t){var n={showCreateRules:!1,showCreateValues:!1,showEditRules:!1,showEditValues:!1,showCreateOptions:!1};e&&(n[e]=!0),this.setState(n,t)},hideRulesInput:function(){this.setState({showCreateRules:!1})},hideValuesInput:function(){this.setState({showCreateValues:!1})},hideRenameRuleInput:function(){this.setState({showEditRules:!1})},hideRenameValueInput:function(){this.setState({showEditValues:!1})},showCreateRules:function(e,t,n){var r=Se(this.refs.createRulesInput);this._curFocusRulesGroup=t,this._curFocusRulesItem=n,this.setState({showCreateRules:!0},function(){r.focus()})},showCreateValues:function(e,t,n){var r=Se(this.refs.createValuesInput);this._curFocusValuesGroup=t,this._curFocusValuesItem=n,this.setState({showCreateValues:!0},function(){r.focus()})},showHttpsSettingsDialog:function(){this.refs.httpsSettings.show()},interceptHttpsConnects:function(e){var t=this,n=e.target.checked;z.interceptHttpsConnects({interceptHttpsConnects:n?1:0},function(e,r){e&&0===e.ec?(t.state.interceptHttpsConnects=n,z.isCapture=n?1:0,H.trigger(\"reqTabsChange\"),H.trigger(\"resTabsChange\")):Q.showSystemError(r),t.setState({})})},enableHttp2:function(e){var t=this;if(!z.supportH2)return void ee.confirm(\"HTTP/2 requires Node.js LTS version v16+. Please upgrade\",function(e){e&&window.open(\"https://nodejs.org/\"),t.setState({})});var n=e.target.checked;z.enableHttp2({enableHttp2:n?1:0},function(e,r){e&&0===e.ec?t.state.enableHttp2=n:Q.showSystemError(r),t.setState({})})},createRules:function(e){if(13==e.keyCode||\"click\"==e.type){var t=this,n=Se(t.refs.createRulesInput),r=n.value.trim();if(!r)return void G.error(\"The name is required\");var o,i=t.state.rules,a=e&&e.target.getAttribute(\"data-type\");if(\"group\"===a&&(o=!0,r=\"\\r\"+r),i.exists(r))return void G.error(\"The name '\"+r+\"' is already in use\");var s=\"top\"===a?1:\"\",l=t._curFocusRulesGroup,c=t._curFocusRulesItem,u={name:r,addToTop:s};if(o){var d=c&&c.name;d&&(\"Default\"===d&&(d=t.state.rules.list[1]),u.focusName=d)}else l&&(u.groupName=l.name);z.rules.add(u,function(e,a){if(e&&0===e.ec){var l=i[s?\"unshift\":\"add\"](r);n.value=\"\",n.blur();var c=u.focusName;c?i.moveTo(r,c):i.moveToGroup(r,u.groupName,s),o?l&&(l._isNewGroup=!0):t.setRulesActive(r),u.groupName&&H.trigger(\"expandRulesGroup\",u.groupName),t.setState(o?{}:{activeRules:l}),t.triggerRulesChange(\"create\")}else Q.showSystemError(a)})}},createValues:function(e){if(13==e.keyCode||\"click\"==e.type){var t=this,n=Se(t.refs.createValuesInput),r=n.value.trim();if(!r)return void G.error(\"The name is required\");if(/\\s/.test(r))return void G.error(\"Spaces are not allowed in the name\");if(/#/.test(r))return void G.error(\"Special character '#' is not allowed in the name\");var o,i=t.state.values,a=e&&e.target.getAttribute(\"data-type\");if(\"group\"===a&&(o=!0,r=\"\\r\"+r),i.exists(r))return void G.error(\"The name '\"+r+\"' is already in use\");var s=t._curFocusValuesGroup,l=t._curFocusValuesItem,c={name:r};o?l&&(c.focusName=l.name):s&&(c.groupName=s.name),z.values.add(c,function(e,a){if(e&&0===e.ec){var s=i.add(r);n.value=\"\",n.blur();var l=c.focusName;l?i.moveTo(r,l):i.moveToGroup(r,c.groupName),o?s&&(s._isNewGroup=!0):t.setValuesActive(r),c.groupName&&H.trigger(\"expandValuesGroup\",c.groupName),t.setState(o?{}:{activeValues:s}),t.triggerValuesChange(\"create\")}else Q.showSystemError(a)})}},showEditRules:function(e){this.currentFocusRules=e;var t=this.state.rules,n=e||t.getActive();if(n&&!n.isDefault){var r=Se(this.refs.editRulesInput);r.value=n.name,this.setState({showEditRules:!0,selectedRule:n},function(){r.select(),r.focus()})}},showEditValuesByDBClick:function(e){!e.changed&&this.showEditValues()},showEditValues:function(e){this.currentFocusValues=e;var t=this.state.values,n=e||t.getActive();if(n&&!n.isDefault){var r=Se(this.refs.editValuesInput);r.value=n.name,this.setState({showEditValues:!0,selectedValue:n},function(){r.select(),r.focus()})}},editRules:function(e){if(13==e.keyCode||\"click\"==e.type){var t=this,n=t.state.rules,r=this.currentFocusRules||n.getActive();if(r){var o=Se(t.refs.editRulesInput),i=Q.isGroup(r.name),a=(i?\"\\r\":\"\")+o.value.trim();if(!a)return void G.error(\"The name is required\");if(n.exists(a))return void G.error(\"The name '\"+a+\"' is already in use\");var s=r.name;z.rules.rename({name:s,newName:a},function(e,r){e&&0===e.ec?(n.rename(s,a),o.value=\"\",o.blur(),!i&&t.setRulesActive(a),H.trigger(\"rulesNameChanged\",[s,a]),t.setState({activeRules:n.getActive()}),t.triggerRulesChange(\"rename\")):Q.showSystemError(r)})}}},editValues:function(e){if(13==e.keyCode||\"click\"==e.type){var t=this,n=t.state.values,r=this.currentFocusValues||n.getActive();if(r){var o=Se(t.refs.editValuesInput),i=Q.isGroup(r.name),a=(i?\"\\r\":\"\")+o.value.trim();if(!a)return void G.error(\"The name is required\");if(n.exists(a))return void G.error(\"The name '\"+a+\"' is already in use\");var s=r.name;z.values.rename({name:s,newName:a},function(e,r){e&&0===e.ec?(n.rename(s,a),o.value=\"\",o.blur(),!i&&t.setValuesActive(a),H.trigger(\"valuesNameChanged\",[s,a]),t.setState({activeValues:n.getActive()}),t.triggerValuesChange(\"rename\")):Q.showSystemError(r)})}}},getActiveRuleName:function(){var e=this.state.rules,t=e.getActive();return t?t.name:\"\"},showAnonymousWeinre:function(){this.openWeinre()},showWeinre:function(e){this.openWeinre(e.name)},openWeinre:function(e){window.open(\"weinre/client/#\"+(e||\"anonymous\")),this.setState({showWeinreOptions:!1})},onClickRulesOption:function(e){e.selected?this.unselectRules(e):this.selectRules(e)},selectRules:function(e){if(!Q.isGroup(e.name)){var t=this;return z.rules[e.isDefault?\"enableDefault\":\"select\"](e,function(n,r){n&&0===n.ec?(t.reselectRules(n),t.state.rules.setChanged(e.name,!1),t.setState({}),t.triggerRulesChange(\"save\"),n.changed&&H.trigger(\"rulesChanged\"),t.state.disabledAllRules&&ee.confirm(\"Rules are currently disabled. Enable them now?\",function(e){e&&z.rules.disableAllRules({disabledAllRules:0},function(e,n){e&&0===e.ec?(t.state.disabledAllRules=!1,t.setState({})):Q.showSystemError(n)})})):Q.showSystemError(r)}),!1}},selectRulesByOptions:function(e){var t=this.state.rules.data[y(e.target).attr(\"data-name\")];this[e.target.checked?\"selectRules\":\"unselectRules\"](t)},unselectRules:function(e){var t=this;return z.rules[e.isDefault?\"disableDefault\":\"unselect\"](e,function(e,n){e&&0===e.ec?(t.reselectRules(e),t.triggerRulesChange(\"unselect\"),t.setState({})):Q.showSystemError(n)}),!1},reselectRules:function(e,t){var n=this;n.state.rules.clearAllSelected(),n.setSelected(n.state.rules,\"Default\",!e.defaultRulesIsDisabled,t),e.list.forEach(function(e){n.setSelected(n.state.rules,e,!0,t)})},saveValues:function(e){if(e.changed&&!Q.isGroup(e.name)){var t=this;return z.values.add(e,function(n,r){n&&0===n.ec?(t.setSelected(t.state.values,e.name),t.triggerValuesChange(\"save\")):Q.showSystemError(r)}),!1}},setSelected:function(e,t,n,r){e.setSelected(t,n)&&(r||e.setChanged(t,!1),this.setState({curSelectedName:t}))},replayCountChange:function(e){var t=e.target.value.replace(/^\\s*0*|[^\\d]+/,\"\"),n=t.slice(0,3);n>me&&(n=me),this.setState({replayCount:n})},clickReplay:function(e){e.shiftKey?H.trigger(\"replaySessions\",[null,e.shiftKey]):this.replay(e)},replay:function(e,t,n){var r=this.state.network;if(t=Array.isArray(t)?t:r.getSelectedList(),t&&t.length){this.enableRecord();var o,i=function(e,t){var n=e.req;z.compose({repeatCount:t,useH2:e.useH2?1:\"\",url:e.url,headers:Q.getOriginalReqHeaders(e),method:n.method,base64:n.base64})};if(n>1?i(t[0],Math.min(n,me)):(o={},t.slice(0,me).forEach(function(e){o[e.id]=1,i(e)})),r.isTreeView){var a=z.lastSelectedDataId;if(!a)return;if(!o)return H.trigger(\"replayTreeView\",[a,n]);var s=a&&r.getTreeNode(a);if(s=s&&s.parent,!s)return;n=0,s.children.forEach(function(e){e=e.data,e&&o[e.id]&&++n}),H.trigger(\"replayTreeView\",[a,n])}else this.autoRefresh&&this.autoRefresh()}},enableRecord:function(){this.refs.recordBtn.enable(),H.trigger(\"changeRecordState\")},composer:function(){H.trigger(\"composer\")},clear:function(){var e=this.state.network;this.setState({network:e.clear()})},removeRulesBatch:function(e){var t=this;z.rules.remove({list:e},function(n,r){if(n&&0===n.ec){var o,i=t.state.rules;e.forEach(function(e){var n=i.data[e]||\"\";n.active&&(o=i.getSibling(e),o&&t.setRulesActive(o.name)),i.remove(e)}),o&&H.trigger(\"expandRulesGroup\",o.name),t.setState(o?{activeRules:o}:{}),t.triggerRulesChange(\"remove\"),H.trigger(\"focusRulesList\")}else Q.showSystemError(r)}),this.refs.deleteRulesDialog.hide()},removeValuesBatch:function(e){var t=this;z.values.remove({list:e},function(n,r){if(n&&0===n.ec){var o,i=t.state.values;e.forEach(function(e){var n=i.data[e]||\"\";n.active&&(o=i.getSibling(e),o&&t.setValuesActive(o.name)),i.remove(e)}),o&&H.trigger(\"expandValuesGroup\",o.name),t.setState(o?{activeValues:o}:{}),t.triggerValuesChange(\"remove\"),H.trigger(\"focusValuesList\")}else Q.showSystemError(r)}),this.refs.deleteValuesDialog.hide()},removeRules:function(e){var t=this.state.rules,n=e||t.getActive();n&&!n.isDefault&&this.refs.deleteRulesDialog.show(n.name)},removeValues:function(e){var t=this.state.values,n=e||t.getActive();n&&!n.isDefault&&this.refs.deleteValuesDialog.show(n.name)},setRulesActive:function(e,t){t=t||this.state.rules,F.set(\"activeRules\",e),t.setActive(e)},setValuesActive:function(e,t){t=t||this.state.values,F.set(\"activeValues\",e),t.setActive(e)},showRulesSettings:function(){y(Se(this.refs.rulesSettingsDialog)).modal(\"show\")},showValuesSettings:function(){y(Se(this.refs.valuesSettingsDialog)).modal(\"show\")},toggleLeftMenu:function(){var e=!this.state.showLeftMenu;this.setState({showLeftMenu:e}),F.set(\"showLeftMenu\",e?1:\"\"),H.trigger(\"editorResize\")},handleCreate:function(){\"rules\"==this.state.name?this.showCreateRules():this.showCreateValues()},saveRulesOrValues:function(){var e,t=this,n=t.state,r=\"rules\"==n.name;if(r){e=n.rules.getChangedList();var o=n.rules.getActive();o&&!o.selected&&-1===e.indexOf(o)&&e.push(o),e.length&&(e.forEach(function(e){t.selectRules(e)}),t.setState({}))}else e=n.values.getChangedList(),e.length&&(e.forEach(function(e){t.saveValues(e)}),t.setState({}))},onClickMenu:function(e){var t=y(e.target).closest(\"a\"),n=this,r=n.state,o=\"rules\"==r.name;t.hasClass(\"w-edit-menu\")?o?n.showEditRules():n.showEditValues():t.hasClass(\"w-delete-menu\")?o?n.removeRules():n.removeValues():t.hasClass(\"w-save-menu\")&&n.saveRulesOrValues()},showSettings:function(){var e=this.state.name;return\"rules\"===e?void this.showRulesSettings():\"values\"===e?void this.showValuesSettings():void(\"network\"===e&&this.refs.networkSettings.showDialog())},activeRules:function(e){F.set(\"activeRules\",e.name),this.setState({activeRules:e})},activeValues:function(e){F.set(\"activeValues\",e.name),this.setState({activeValues:e})},onRulesThemeChange:function(e){var t=e.target.value;F.set(\"rulesTheme\",t),this.setState({rulesTheme:t})},onValuesThemeChange:function(e){var t=e.target.value;F.set(\"valuesTheme\",t),this.setState({valuesTheme:t})},onRulesFontSizeChange:function(e){var t=e.target.value;F.set(\"rulesFontSize\",t),this.setState({rulesFontSize:t})},onValuesFontSizeChange:function(e){var t=e.target.value;F.set(\"valuesFontSize\",t),this.setState({valuesFontSize:t})},onRulesLineNumberChange:function(e){var t=e.target.checked;F.set(\"showRulesLineNumbers\",t),this.setState({showRulesLineNumbers:t})},onValuesLineNumberChange:function(e){var t=e.target.checked;F.set(\"showValuesLineNumbers\",t),this.setState({showValuesLineNumbers:t})},showFoldGutter:function(e){var t=e.target.checked;F.set(\"foldGutter\",t?\"1\":\"\"),this.setState({foldGutter:t})},onRulesLineWrappingChange:function(e){var t=e.target.checked;F.set(\"autoRulesLineWrapping\",t?1:\"\"),this.setState({autoRulesLineWrapping:t})},onValuesLineWrappingChange:function(e){var t=e.target.checked;F.set(\"autoValuesLineWrapping\",t?1:\"\"),this.setState({autoValuesLineWrapping:t})},confirmDisableAllRules:function(e){var t,n=this,r=n.state;r.disabledAllRules||!e&&(t=y(\".w-win-dialog[data-confirm-flag=rules]\")).is(\":visible\")?(n.disableAllRules(),t&&t.modal(\"hide\")):ee.confirm(\"Do you confirm disabling all rules?\",function(e){e&&n.disableAllRules()},!1,\"rules\"),e&&e.preventDefault()},confirmDisableAllPlugins:function(e){var t,n=this,r=n.state;r.disabledAllPlugins||!e&&(t=y(\".w-win-dialog[data-confirm-flag=plugins]\")).is(\":visible\")?(n.disableAllPlugins(),t&&t.modal(\"hide\")):ee.confirm(\"Do you confirm disabling all plugins?\",function(e){e&&n.disableAllPlugins()},!1,\"plugins\"),e&&e.preventDefault()},disableAllRules:function(e,t){var n=this,r=n.state,o=!r.disabledAllRules;z.rules.disableAllRules({disabledAllRules:o?1:0},function(e,i){e&&0===e.ec?(r.disabledAllRules=o,n.setState({}),\"function\"==typeof t&&t(o)):Q.showSystemError(i)}),e&&e.preventDefault()},disableAllPlugins:function(e,t){var n=this,r=n.state,o=!r.disabledAllPlugins;z.plugins.disableAllPlugins({disabledAllPlugins:o?1:0},function(e,i){e&&0===e.ec?(r.disabledAllPlugins=o,O.setPlugins(r),n.setState({}),\"function\"==typeof t&&t(o)):Q.showSystemError(i)}),e&&e.preventDefault()},setPluginState:function(e,t){var n=this;return n.state.ndp?G.info(\"Plugin disabling is restricted\"):void z.plugins.disablePlugin({name:e,disabled:t?1:0},function(e,t){e&&0===e.ec?(n.state.disabledPlugins=e.data,z.setDisabledPlugins(e.data),O.setPlugins(n.state),n.setState({})):Q.showSystemError(t)})},disablePlugin:function(e){var t=e.target;this.setPluginState(y(t).attr(\"data-name\"),!t.checked)},abort:function(e){if(!Array.isArray(e)){var t=this.state.network;e=t.getSelectedList()}e&&(e=e.map(function(e){return Q.canAbort(e)?e.id:void 0}),e.length&&z.abort({list:e.join()})),this.hideAbortOptions()},allowMultipleChoice:function(e){this.setMultipleCohice(e.target.checked)},setMultipleCohice:function(e){var t=this;z.rules.allowMultipleChoice({allowMultipleChoice:e?1:0},function(n,r){n&&0===n.ec?t.setState({allowMultipleChoice:e}):Q.showSystemError(r)})},enableBackRulesFirst:function(e){this.setBackRulesFirst(e.target.checked)},setBackRulesFirst:function(e){var t=this;z.rules.enableBackRulesFirst({backRulesFirst:e?1:0},function(n,r){n&&0===n.ec?(t.setState({backRulesFirst:e}),z.backRulesFirst=e):Q.showSystemError(r)})},installPlugins:function(){H.trigger(\"installPlugins\")},chooseFileType:function(e){var t=e.target.value;F.set(\"exportFileType\",t),this.setState({exportFileType:t})},importHarSessions:function(e){if(e&&\"object\"===(\"undefined\"==typeof e?\"undefined\":M(e))){var t=e.log.entries,n=[];t.forEach(function(e){e=Q.harToSession(e),e&&n.push(e)}),z.addNetworkList(n)}},uploadSessionsForm:function(e){if(!(e instanceof FormData)){var t=new FormData;t.append(\"importSessions\",e),e=t}var n=e.get(\"importSessions\");if(!n||!/\\.(txt|json|saz|har)$/i.test(n.name))return ee.alert(\"Supported file formats: .txt, .json, .saz, .har\");if(n.size>he)return ee.alert(\"Maximum file size: 64MB\");var r=/\\.(?:txt|json)$/i.test(n.name);if(r||/\\.har$/i.test(n.name)){var o=this;return void Q.readFileAsText(n,function(e){try{e=JSON.parse(e),r?z.importAnySessions(e):o.importHarSessions(e)}catch(t){ee.alert(\"Invalid JSON format\")}})}z.upload.importSessions(e,z.addNetworkList)},getExportSessions:function(){var e=this.state.network,t=this.currentFoucsItem;return this.currentFoucsItem=null,t&&y(Se(this.refs.chooseFileType)).is(\":visible\")||(t=e.getSelectedList()),t},exportSessions:function(e,t,n){if(n=n||this.getExportSessions(),n&&n.length){var r=Se(this.refs.exportSessionsForm);Se(this.refs.exportFilename).value=t||\"\",Se(this.refs.exportFileType).value=e,\"har\"===e&&(n={log:{version:\"1.2\",creator:{name:\"Whistle\",version:this.state.version,comment:\"\"},browser:{name:\"Whistle\",version:this.state.version},pages:[],entries:n.map(Q.toHar),comment:\"\"}}),Se(this.refs.sessions).value=JSON.stringify(n,null,\"  \"),r.submit()}},hideChooseFileTypeDialog:function(e){e||(y(Se(this.refs.chooseFileType)).modal(\"hide\"),Se(this.refs.sessionsName).value=\"\")},exportBySave:function(e){if(!e||\"click\"===e.type||13===e.keyCode){var t=Se(this.refs.sessionsName),n=t.value.trim();t.value=\"\",this.exportSessions(this.state.exportFileType,n),this.hideChooseFileTypeDialog()}},replayRepeat:function(e){e&&\"click\"!==e.type&&13!==e.keyCode||(this.refs.setReplayCount.hide(),this.replay(\"\",this.replayList,this.state.replayCount),H.trigger(\"focusNetworkList\"))},showAboutDialog:function(e){this.state.hasNewVersion&&(this.refs.aboutDialog.showAboutInfo(),e.preventDefault())},onTopContextMenu:function(e){if(\"network\"===this.getTabName()&&!y(e.target).closest(\".w-menu-item\").length){e.preventDefault();var t=Q.getMenuPosition(e,110,100);t.list=Le,this.refs.topContextMenu.show(t)}},onContextMenu:function(e){var t=0,n=ke;if(n[2].hide&&++t,n[3].hide&&++t,n[4].hide&&++t,3>t){var r=Q.getMenuPosition(e,110,100-30*t),o=this.state;r.list=n,n[2].checked=!!o.network.isTreeView,n[3].checked=!o.disabledAllRules,n[4].checked=!o.disabledAllPlugins;var i=y(e.target);n[0].hide=!0,n[1].hide=!0,i.closest(\".w-network-menu\").length?n[0].hide=!1:i.closest(\".w-save-menu\").length&&(n[1].hide=!1,i.closest(\".w-rules-menu\").length?n[1].disabled=!o.rules.hasChanged():n[1].disabled=!o.values.hasChanged()),this.refs.contextMenu.show(r)}e.preventDefault()},onClickTopMenu:function(e){switch(e){case\"top\":this.container&&(this.container[0].scrollTop=0);break;case\"selected\":H.trigger(\"ensureSelectedItemVisible\");break;case\"bottom\":this.container&&(this.container[0].scrollTop=1e7)}},onClickContextMenu:function(e){var t=this,n=t.state,r=ke;switch(e){case\"Tree View\":r[2].checked=!n.network.isTreeView,setTimeout(t.toggleTreeView,0);break;case\"Rules\":t.disableAllRules(null,function(e){r[3].checked=!e,t.setState({})});break;case\"Plugins\":t.disableAllPlugins(null,function(e){r[4].checked=!e,t.setState({})});break;case\"Clear\":return void t.clear();case\"Save\":return void t.saveRulesOrValues()}this.refs.contextMenu.show({})},forceShowLeftMenu:function(){var e=this;clearTimeout(e.hideTimer),clearTimeout(e.showTimer),e.showTimer=setTimeout(function(){e.setState({forceShowLeftMenu:!0})},200)},forceHideLeftMenu:function(){var e=this;clearTimeout(e.hideTimer),clearTimeout(e.showTimer),e.hideTimer=setTimeout(function(){e.setState({forceShowLeftMenu:!1})},500)},updateMenuView:function(e){var t=e.networkOptions[e.networkOptions.length-1];return e.network.isTreeView?(t.icon=\"globe\",t.name=\"Show List View\"):(t.icon=\"tree-conifer\",t.name=\"Show Tree View\"),e},toggleTreeView:function(){var e=this,t=e.state.network;t.setTreeView(!t.isTreeView),e.updateMenuView(e.state),e.setState({},function(){t.isTreeView||e.autoRefresh&&e.autoRefresh()})},toggleTreeViewByIcon:function(){\"network\"==this.getTabName()&&this.toggleTreeView()},download:function(e){if(e&&(Q.isString(e.content)||Q.isString(e.value)||Q.isString(e.base64))){var t=Q.getString(e.base64);Se(this.refs.filename).value=Q.getString(e.name),Se(this.refs.dataType).value=t?\"rawBase64\":\"\",Se(this.refs.content).value=t||Q.getString(e.value||e.content),Se(this.refs.downloadForm).submit()}},getTabName:function(){var e=this.state,t=e.rulesMode,n=e.pluginsMode,r=e.name;return e.networkMode?r=\"network\":e.rulesOnlyMode?r=\"values\"===r?\"values\":\"rules\":t&&n?r=\"plugins\":t?r=\"network\"===r?\"rules\":r:n&&(r=\"plugins\"!==r?\"network\":r),r||\"network\"},isHideRules:function(){return this.state.networkMode||this.state.pluginsMode},onClickHelpMenu:function(e,t){\"Update\"===e.name&&z.showLatestClientVersion()&&t.preventDefault()},render:function(){var e=this.state,t=e.networkMode,n=e.rulesMode,o=e.rulesOnlyMode,i=e.pluginsMode,a=z.isMultiEnv(),s=this.getTabName(),l=\"account\"==s,c=\"network\"==s,u=\"rules\"==s,d=\"values\"==s,p=\"plugins\"==s,h=u||d,g=r(!h),f=r(p||l),m=!0,M=!0,v=e.rulesTheme||\"cobalt\",b=e.valuesTheme||\"cobalt\",y=e.rulesFontSize||\"14px\",T=e.valuesFontSize||\"14px\",C=e.showRulesLineNumbers||!1,E=e.showValuesLineNumbers||!1,O=e.autoRulesLineWrapping,H=e.autoValuesLineWrapping,F=e.rulesOptions,G=e.pluginsOptions,ee={},ce=e.showNetworkOptions,ue=e.showRulesOptions,pe=e.showValuesOptions,he=e.showPluginsOptions,ge=e.showWeinreOptions,fe=e.showHelpOptions,Ae=e.network,Me=Ae.isTreeView,we=(Me?\"tree-conifer\":\"globe\")+(e.record?\" w-disabled\":\"\");F[0].name===de&&F.forEach(function(e,t){e.icon=t&&a?\"edit\":\"checkbox\",e.selected||(ee[e.name]=1)});var ye,xe;if(u){xe=e.rules.data;for(ye in xe)if(xe[ye].active){m=M=xe[ye].isDefault;break}}else if(d){xe=e.values.data;for(ye in xe)if(xe[ye].active){m=M=!1;break}}Ae.rulesModal=e.rules,e.rules.editorTheme={theme:v,fontSize:y,lineNumbers:C};var Te=e.networkOptions,Ce=Ae.hasUnselected();Ae.hasSelected()?Te.forEach(function(e){e.disabled=!1,\"removeUnselected\"===e.id&&(e.disabled=!Ce)}):(Te.forEach(function(e){-1!==ve.indexOf(e.id)?e.disabled=!0:\"removeUnselected\"===e.id&&(e.disabled=!Ce)}),Te[0].disabled=!Ce);var Ee,De,Se=w&&!e.forceShowLeftMenu,Le=i&&n,je=(t||e.showLeftMenu)&&!Le,Ue=e.disabledAllPlugins,Re=e.disabledAllRules,ze=r(o||Le||t);je&&w&&(Ee=this.forceShowLeftMenu,De=this.forceHideLeftMenu),ke[2].hide=n,ke[3].hide=i,ke[4].hide=o;var Qe=e.caType||\"crt\",Oe=e.caHash,He=\"cgi-bin/rootca\",Fe=\"http://rootca.pro/\";\"cer\"!==Qe&&(He+=\"?type=\"+Qe,Fe+=Qe);var Ve=this.isHideRules(),Pe=r(Ve),Ye=Ie?\" hide\":\"\";return z.hideMockMenu=Ve,this.hideNetwork=n,this.hideRules=this.hideValues=Ve,\nthis.hidePlugins=ze,x.createElement(\"div\",{className:\"main v-box\"+(je?\" w-show-left-menu\":\"\")+(h&&!o?\" w-show-editor\":\"\")+(u?\" w-show-rules\":\"\")+(o||n?\" w-show-rules-mode\":\"\")},x.createElement(\"div\",{className:\"w-menu w-\"+s+\"-menu-list\"+Ye+(je?\"\":\" w-top\"),onContextMenu:this.onTopContextMenu},x.createElement(\"a\",{onClick:this.toggleLeftMenu,draggable:\"false\",className:\"w-switch-layout\",onMouseEnter:Ee,onMouseLeave:De,style:r(t||Le),title:\"Ctrl[Command] + M\"},x.createElement(se,{name:\"chevron-\"+(je?Se?\"down\":\"up\":\"left\")})),x.createElement(\"div\",{style:r(n),onMouseEnter:this.showNetworkOptions,onMouseLeave:this.hideNetworkOptions,className:\"w-nav-menu w-menu-wrapper\"+(ce?\" w-menu-wrapper-show\":\"\")},x.createElement(\"a\",{onClick:this.showNetwork,onDoubleClick:this.toggleTreeView,className:\"w-network-menu\"+(c?\" w-menu-selected\":\"\"),title:\"Double-click to open\"+(Me?\" List View\":\" Tree View\"),draggable:\"false\"},x.createElement(se,{name:we}),\"Network\"),x.createElement(k,{ref:\"networkMenuItem\",options:e.networkOptions,className:\"w-network-menu-item\",onClickOption:this.handleNetwork})),x.createElement(\"div\",{style:Pe,onMouseEnter:this.showRulesOptions,onMouseLeave:this.hideRulesOptions,className:\"w-nav-menu w-menu-wrapper\"+(ue?\" w-menu-wrapper-show\":\"\")+(u?\" w-menu-auto\":\"\")},x.createElement(\"a\",{onClick:this.showRules,className:\"w-rules-menu\"+(u?\" w-menu-selected\":\"\"),draggable:\"false\"},x.createElement(se,{name:\"list\",className:Re?\"w-disabled\":\"\"}),\"Rules\"),x.createElement(k,{ref:\"rulesMenuItem\",name:u?null:\"Open\",options:F,checkedOptions:ee,disabled:Re,className:\"w-rules-menu-item\",onClick:this.showRules,onClickOption:this.showAndActiveRules,onChange:this.selectRulesByOptions})),x.createElement(\"div\",{style:Pe,onMouseEnter:this.showValuesOptions,onMouseLeave:this.hideValuesOptions,className:\"w-nav-menu w-menu-wrapper\"+(pe?\" w-menu-wrapper-show\":\"\")+(d?\" w-menu-auto\":\"\")},x.createElement(\"a\",{onClick:this.showValues,className:\"w-values-menu\"+(d?\" w-menu-selected\":\"\"),draggable:\"false\"},x.createElement(se,{name:\"folder-close\"}),\"Values\"),x.createElement(k,{ref:\"valuesMenuItem\",name:d?null:\"Open\",options:e.valuesOptions,className:\"w-values-menu-item\",onClick:this.showValues,onClickOption:this.showAndActiveValues})),x.createElement(\"div\",{style:ze,ref:\"pluginsMenu\",onMouseEnter:this.showPluginsOptions,onMouseLeave:this.hidePluginsOptions,className:\"w-nav-menu w-menu-wrapper\"+(he?\" w-menu-wrapper-show\":\"\")},x.createElement(\"a\",{onClick:this.showPlugins,className:\"w-plugins-menu\"+(p?\" w-menu-selected\":\"\"),draggable:\"false\"},x.createElement(se,{name:\"th-large\",className:Ue?\"w-disabled\":\"\"}),\"Plugins\"),x.createElement(k,{ref:\"pluginsMenuItem\",name:p?null:\"Open\",options:G,checkedOptions:e.disabledPlugins,disabled:Ue,className:\"w-plugins-menu-item\",onClick:this.showPlugins,onChange:this.disablePlugin,onClickOption:this.showAndActivePlugins})),!e.ndr&&x.createElement(\"a\",{onClick:this.confirmDisableAllRules,className:\"w-enable-rules-menu w-switch-btn\",title:Re?\"Enable all rules\":\"Disable all rules\",style:r(!u),draggable:\"false\"},x.createElement(se,{name:\"stop\",className:Re?\"w-pause\":\"\"}),\"ON\"),!e.ndp&&x.createElement(\"a\",{onClick:this.confirmDisableAllPlugins,className:\"w-enable-plugin-menu w-switch-btn\",title:Ue?\"Enable all plugins\":\"Disable all plugins\",style:r(!p),draggable:\"false\"},x.createElement(se,{name:\"stop\",className:Ue?\"w-pause\":\"\"}),\"ON\"),x.createElement(W,{hide:!p}),x.createElement(\"a\",{onClick:this.installPlugins,className:\"w-plugins-menu\",style:r(!p),draggable:\"false\"},x.createElement(se,{name:\"download-alt\"}),\"Install\"),x.createElement(j,{ref:\"recordBtn\",hide:!c,onClick:this.handleAction}),x.createElement(\"a\",{onClick:this.importData,style:f,className:\"w-import-menu\",draggable:\"false\"},x.createElement(se,{name:\"import\"}),\"Import\"),x.createElement(\"a\",{onClick:this.exportData,className:\"w-export-menu\",style:f,draggable:\"false\"},x.createElement(se,{name:\"export\"}),\"Export\"),x.createElement(\"a\",{onClick:this.clear,style:r(!c),className:\"w-remove-menu w-remove-menu-list\",title:\"Ctrl[Command] + X\",draggable:\"false\"},x.createElement(se,{name:\"remove\"}),\"Clear\"),x.createElement(\"a\",{onClick:this.onClickMenu,className:\"w-save-menu\",style:g,draggable:\"false\",title:\"Ctrl[Command] + S\"},x.createElement(se,{name:\"save-file\"}),\"Save\"),x.createElement(\"a\",{className:\"w-create-menu\",style:g,draggable:\"false\",onClick:this.handleCreate},x.createElement(se,{name:\"plus\"}),\"Create\"),x.createElement(\"a\",{onClick:this.onClickMenu,className:\"w-edit-menu\"+(m?\" w-disabled\":\"\"),style:g,draggable:\"false\"},x.createElement(se,{name:\"transfer\"}),\"Rename\"),x.createElement(\"div\",{onMouseEnter:this.showAbortOptions,onMouseLeave:this.hideAbortOptions,style:r(!c),className:\"w-menu-wrapper w-abort-menu-list w-menu-auto\"+(e.showAbortOptions?\" w-menu-wrapper-show\":\"\")},x.createElement(\"a\",{onClick:this.clickReplay,className:\"w-replay-menu\",draggable:\"false\"},x.createElement(se,{name:\"repeat\"}),\"Replay\"),x.createElement(k,{options:Be,className:\"w-remove-menu-item\",onClickOption:this.abort})),x.createElement(\"a\",{onClick:this.composer,className:\"w-com-menu\",style:r(!c),draggable:\"false\"},x.createElement(se,{name:\"send\"}),\"Edit\"),x.createElement(\"a\",{onClick:this.onClickMenu,className:\"w-delete-menu\"+(M?\" w-disabled\":\"\"),style:g,draggable:\"false\"},x.createElement(se,{name:\"trash\"}),\"Delete\"),x.createElement(Y,{onClick:this.showSettings,disabledRules:u&&Re,backRulesFirst:u&&e.backRulesFirst,isNetwork:c,hide:p}),x.createElement(te,{name:s}),x.createElement(\"div\",{onMouseEnter:this.showWeinreOptions,onMouseLeave:this.hideWeinreOptions,className:\"w-menu-wrapper\"+(ge?\" w-menu-wrapper-show\":\"\")},x.createElement(\"a\",{onClick:this.showWeinreOptionsQuick,onDoubleClick:this.showAnonymousWeinre,className:\"w-weinre-menu\",draggable:\"false\"},x.createElement(se,{name:\"console\"}),x.createElement(\"span\",{className:\"w-weinre-name\"},\"Weinre\")),x.createElement(k,{ref:\"weinreMenuItem\",name:\"anonymous\",icon:\"console\",options:e.weinreOptions,className:\"w-weinre-menu-item\",onClick:this.showAnonymousWeinre,onClickOption:this.showWeinre})),x.createElement(\"a\",{onClick:this.showHttpsSettingsDialog,className:\"w-https-menu\",draggable:\"false\",style:{color:z.hasInvalidCerts?\"var(--c-error)\":void 0}},x.createElement(se,{name:e.interceptHttpsConnects?\"ok-circle\":\"lock\"}),x.createElement(\"span\",{className:\"w-https-name\"},\"HTTPS\")),x.createElement(\"div\",{onMouseEnter:this.showHelpOptions,onMouseLeave:this.hideHelpOptions,className:\"w-menu-wrapper\"+(fe?\" w-menu-wrapper-show\":\"\")},x.createElement(\"a\",{onClick:this.showAboutDialog,title:e.hasNewVersion?\"A new version is available, click to see details\":void 0,href:\"https://github.com/avwo/whistle#whistle\",target:\"_blank\"},e.hasNewVersion?x.createElement(\"i\",{className:\"w-new-version-icon\"}):null,x.createElement(se,{name:\"question-sign\"}),x.createElement(\"span\",{className:\"w-help-name\"},\"Help\")),x.createElement(k,{ref:\"helpMenuItem\",options:e.helpOptions,onClickOption:this.onClickHelpMenu,name:x.createElement(S,{ref:\"aboutDialog\",clientVersion:Ne,onClick:this.hideHelpOptions,onCheckUpdate:this.showHasNewVersion}),className:\"w-help-menu-item\"})),x.createElement(L,{name:s,clientVersion:Ne}),x.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:e.showCreateRules?\"block\":\"none\"},className:\"w-shadow w-input-menu-item w-create-rules-input\"},x.createElement(\"input\",{ref:\"createRulesInput\",onKeyDown:this.createRules,onBlur:this.hideRulesInput,type:\"text\",maxLength:\"64\",placeholder:\"Enter name\"}),x.createElement(\"button\",{type:\"button\",onClick:this.createRules,className:\"btn btn-primary\"},\"+Rule\"),x.createElement(\"button\",{type:\"button\",onClick:this.createRules,\"data-type\":\"top\",className:\"btn btn-default\"},\"+Top\"),x.createElement(\"button\",{type:\"button\",onClick:this.createRules,\"data-type\":\"group\",className:\"btn btn-default\"},\"+Group\")),x.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:e.showCreateValues?\"block\":\"none\"},className:\"w-shadow w-input-menu-item w-create-values-input\"},x.createElement(\"input\",{ref:\"createValuesInput\",onKeyDown:this.createValues,onBlur:this.hideValuesInput,type:\"text\",maxLength:\"64\",placeholder:\"Enter name\"}),x.createElement(\"button\",{type:\"button\",onClick:this.createValues,className:\"btn btn-primary\"},\"+Key\"),x.createElement(\"button\",{type:\"button\",onClick:this.createValues,\"data-type\":\"group\",className:\"btn btn-default\"},\"+Group\")),x.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:e.showEditRules?\"block\":\"none\"},className:\"w-shadow w-input-menu-item w-edit-rules-input\"},x.createElement(\"input\",{ref:\"editRulesInput\",onKeyDown:this.editRules,onBlur:this.hideRenameRuleInput,type:\"text\",maxLength:\"64\"}),x.createElement(\"button\",{type:\"button\",onClick:this.editRules,className:\"btn btn-primary\"},\"OK\")),x.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:e.showEditValues?\"block\":\"none\"},className:\"w-shadow w-input-menu-item w-edit-values-input\"},x.createElement(\"input\",{ref:\"editValuesInput\",onKeyDown:this.editValues,onBlur:this.hideRenameValueInput,type:\"text\",maxLength:\"64\"}),x.createElement(\"button\",{type:\"button\",onClick:this.editValues,className:\"btn btn-primary\"},\"OK\"))),x.createElement(\"div\",{className:\"w-container box fill\"},x.createElement(X,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}),x.createElement(X,{onClick:this.onClickTopMenu,ref:\"topContextMenu\"}),x.createElement(\"div\",{onContextMenu:this.onContextMenu,onDoubleClick:this.onContextMenu,className:\"w-left-menu\"+(Ee?\" w-hover-left-menu\":\"\")+Ye,style:r(t||Se),onMouseEnter:Ee,onMouseLeave:De},x.createElement(\"a\",{onClick:this.showNetwork,className:\"w-network-menu\"+(c?\" w-menu-selected\":\"\"),style:r(n),draggable:\"false\"},x.createElement(se,{name:we}),x.createElement(\"i\",{className:\"w-left-menu-name\"},\"Network\")),x.createElement(\"a\",{onClick:this.showRules,className:\"w-save-menu w-rules-menu\"+(u?\" w-menu-selected\":\"\"),style:Pe,draggable:\"false\"},x.createElement(se,{name:\"list\",className:Re?\"w-disabled\":\"\"}),x.createElement(\"i\",{className:\"w-left-menu-name\"},\"Rules\"),x.createElement(\"i\",{className:\"w-menu-changed\",style:r(!e.rules.hasChanged())},\"*\")),x.createElement(\"a\",{onClick:this.showValues,className:\"w-save-menu w-values-menu\"+(d?\" w-menu-selected\":\"\"),style:Pe,draggable:\"false\"},x.createElement(se,{name:\"folder-close\"}),x.createElement(\"i\",{className:\"w-left-menu-name\"},\"Values\"),x.createElement(\"i\",{className:\"w-menu-changed\",style:r(!e.values.hasChanged())},\"*\")),x.createElement(\"a\",{onClick:this.showPlugins,className:\"w-plugins-menu\"+(p?\" w-menu-selected\":\"\"),style:ze,draggable:\"false\"},x.createElement(se,{name:\"th-large\",className:Ue?\"w-disabled\":\"\"}),x.createElement(\"i\",{className:\"w-left-menu-name\"},\"Plugins\"))),e.hasRules?x.createElement(I,{ref:\"rules\",disabled:Re,theme:v,lineWrapping:O,fontSize:y,lineNumbers:C,onSelect:this.selectRules,onUnselect:this.unselectRules,onActive:this.activeRules,modal:e.rules,hide:!u,name:\"rules\"}):void 0,e.hasValues?x.createElement(I,{theme:b,onDoubleClick:this.showEditValuesByDBClick,fontSize:T,lineWrapping:H,lineNumbers:E,onSelect:this.saveValues,onActive:this.activeValues,modal:e.values,hide:!d,className:\"w-values-list\",foldGutter:e.foldGutter}):void 0,e.hasNetwork?x.createElement(D,{ref:\"network\",hide:!c,modal:Ae,rulesModal:e.rules}):void 0,e.hasPlugins?x.createElement(R,A({},e,{onOpen:this.activePluginTab,onClose:this.closePluginTab,onActive:this.activePluginTab,onChange:this.disablePlugin,ref:\"plugins\",hide:!p})):void 0),x.createElement(\"div\",{ref:\"rulesSettingsDialog\",className:\"modal fade w-rules-settings-dialog\"},x.createElement(\"div\",{className:\"modal-dialog\"},x.createElement(\"div\",{className:\"modal-content\"},x.createElement(\"div\",{className:\"modal-body\"},x.createElement(le,null),x.createElement(U,{name:\"rules\",theme:v,fontSize:y,lineNumbers:C,lineWrapping:O,onLineWrappingChange:this.onRulesLineWrappingChange,onThemeChange:this.onRulesThemeChange,onFontSizeChange:this.onRulesFontSizeChange,onLineNumberChange:this.onRulesLineNumberChange}),!e.drm&&x.createElement(\"p\",{className:\"w-editor-settings-box\"},x.createElement(\"label\",{className:\"w-align-items\",style:{color:a?\"var(--c-disabled)\":void 0}},x.createElement(\"input\",{type:\"checkbox\",disabled:a,checked:!a&&e.allowMultipleChoice,onChange:this.allowMultipleChoice}),\" \",\"Use multiple rules\")),!e.drb&&x.createElement(\"p\",{className:\"w-editor-settings-box\"},x.createElement(\"label\",{className:\"w-align-items\"},x.createElement(\"input\",{type:\"checkbox\",checked:e.backRulesFirst,onChange:this.enableBackRulesFirst}),\" \",\"The later rules first\"))),x.createElement(\"div\",{className:\"modal-footer\"},x.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),x.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.importRulesSettings},\"Import\"),x.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:this.exportRulesSettings},\"Export\"))))),x.createElement(\"div\",{ref:\"valuesSettingsDialog\",className:\"modal fade w-values-settings-dialog\"},x.createElement(\"div\",{className:\"modal-dialog\"},x.createElement(\"div\",{className:\"modal-content\"},x.createElement(\"div\",{className:\"modal-body\"},x.createElement(le,null),x.createElement(U,{theme:b,fontSize:T,lineNumbers:E,lineWrapping:H,onLineWrappingChange:this.onValuesLineWrappingChange,onThemeChange:this.onValuesThemeChange,onFontSizeChange:this.onValuesFontSizeChange,onLineNumberChange:this.onValuesLineNumberChange}),x.createElement(\"p\",{className:\"w-editor-settings-box\"},x.createElement(\"label\",{className:\"w-align-items\"},x.createElement(\"input\",{type:\"checkbox\",checked:e.foldGutter,onChange:this.showFoldGutter}),\" \",\"Show fold gutter\"))),x.createElement(\"div\",{className:\"modal-footer\"},x.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),x.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.importValuesSettings},\"Import\"),x.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:this.exportValuesSettings},\"Export\"))))),n?null:x.createElement(B,{ref:\"networkSettings\"}),x.createElement(ie,{ref:\"httpsSettings\",caHash:Oe,port:e.port,caUrlList:e.caUrlList,multiEnv:a,interceptHttpsConnects:e.interceptHttpsConnects,enableHttp2:e.enableHttp2,onEnableHttps:this.interceptHttpsConnects,onEnableHttp2:this.enableHttp2}),x.createElement(\"div\",{ref:\"chooseFileType\",className:\"modal fade w-choose-filte-type\"},x.createElement(\"div\",{className:\"modal-dialog\"},x.createElement(\"div\",{className:\"modal-content\"},x.createElement(\"div\",{className:\"modal-body\"},x.createElement(\"label\",{className:\"w-choose-filte-type-label\"},\"Save as:\",x.createElement(\"input\",{ref:\"sessionsName\",value:e.filename,onChange:this.filterFilename,onKeyDown:this.exportBySave,placeholder:\"Enter filename (optional)\",className:\"form-control\",maxLength:\"64\"}),x.createElement(\"select\",{ref:\"fileType\",className:\"form-control\",value:e.exportFileType,onChange:this.chooseFileType},x.createElement(\"option\",{value:\"whistle\"},\"*.txt\"),x.createElement(\"option\",{value:\"Fiddler\"},\"*.saz\"),x.createElement(\"option\",{value:\"har\"},\"*.har\")))),x.createElement(\"div\",{className:\"modal-footer\"},x.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),x.createElement(ne,{type:\"network\",onComplete:this.hideChooseFileTypeDialog,getFilename:this.getInputValue,data:this.getExportSessions}),x.createElement(\"button\",{type:\"button\",onKeyDown:this.exportBySave,tabIndex:\"0\",onMouseDown:this.preventBlur,className:\"btn btn-primary\",onClick:this.exportBySave},\"Export\"))))),x.createElement(J,{ref:\"editorWin\",className:\"w-editor-win\"}),x.createElement(V,{ref:\"setReplayCount\",wstyle:\"w-replay-count-dialog\"},x.createElement(\"div\",{className:\"modal-body\"},x.createElement(\"label\",null,\"Times:\",x.createElement(\"input\",{ref:\"replayCount\",placeholder:\"<= \"+me,onKeyDown:this.replayRepeat,onChange:this.replayCountChange,value:e.replayCount,className:\"form-control\",maxLength:\"3\"})),x.createElement(\"button\",{type:\"button\",onKeyDown:this.replayRepeat,tabIndex:\"0\",onMouseDown:this.preventBlur,className:\"btn btn-primary\",disabled:!e.replayCount,onClick:this.replayRepeat},\"Replay\"))),x.createElement(\"div\",{ref:\"showUpdateTipsDialog\",className:\"modal fade w-show-update-tips-dialog\"},x.createElement(\"div\",{className:\"modal-dialog\"},x.createElement(\"div\",{className:\"modal-content\"},x.createElement(\"div\",{className:\"modal-body\"},x.createElement(le,null),x.createElement(\"p\",{className:\"w-show-update-tips\"},\"Whistle has critical updates available. Update to the latest version immediately.\"),x.createElement(\"p\",null,\"Current version: \",e.version),x.createElement(\"p\",null,\"Latest version: \",e.latestVersion),x.createElement(\"p\",null,\"View change:\",\" \",x.createElement(\"a\",{title:\"Change log\",href:\"https://github.com/avwo/whistle/blob/master/CHANGELOG.md\",target:\"_blank\"},\"CHANGELOG.md\"))),x.createElement(\"div\",{className:\"modal-footer\"},x.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",onClick:this.donotShowAgain,\"data-dismiss\":\"modal\"},\"Don't Show Again\"),x.createElement(\"a\",{type:\"button\",className:\"btn btn-primary\",onClick:this.hideUpdateTipsDialog,href:Q.UPDATE_URL,target:\"_blank\"},\"View Update Guide\"))))),x.createElement(V,{ref:\"confirmReload\",wstyle:\"w-confirm-reload-dialog\"},x.createElement(\"div\",{className:\"modal-body w-confirm-reload\"},x.createElement(le,null),x.createElement(\"div\",{className:\"w-reload-data-tips\"})),x.createElement(\"div\",{className:\"modal-footer\"},x.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"No\"),x.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.reloadData,\"data-dismiss\":\"modal\"},\"Yes\"))),x.createElement(P,{ref:\"deleteRulesDialog\",title:\"Delete Rules\",tips:\"Do you confirm the deletion of all follow rules or group?\",onConfirm:this.removeRulesBatch,name:\"rules\",isRules:\"1\",list:e.rules.list}),x.createElement(P,{ref:\"deleteValuesDialog\",title:\"Delete Values\",tips:\"Do you confirm the deletion of all follow values or group?\",onConfirm:this.removeValuesBatch,name:\"values\",list:e.values.list}),x.createElement(P,{ref:\"selectRulesDialog\",name:\"rules\",modal:e.rules,list:e.rules.list}),x.createElement(P,{ref:\"selectValuesDialog\",title:\"Export Values\",name:\"values\",list:e.values.list}),x.createElement(\"iframe\",{name:\"downloadTargetFrame\",style:be}),x.createElement(\"form\",{ref:\"exportSessionsForm\",action:\"cgi-bin/sessions/export\",style:be,method:\"post\",target:\"downloadTargetFrame\"},x.createElement(\"input\",{ref:\"exportFilename\",name:\"exportFilename\",type:\"hidden\"}),x.createElement(\"input\",{ref:\"exportFileType\",name:\"exportFileType\",type:\"hidden\"}),x.createElement(\"input\",{ref:\"sessions\",name:\"sessions\",type:\"hidden\"})),x.createElement(q,{ref:\"syncDialog\"}),x.createElement(K,{ref:\"jsonDialog\"}),x.createElement(\"div\",{id:\"copyTextBtn\",style:be}),x.createElement(Z,{ref:\"mockDialog\"}),x.createElement(_,{ref:\"rulesDialog\"}),x.createElement(\"form\",{ref:\"downloadForm\",action:\"cgi-bin/download\",style:be,method:\"post\",target:\"downloadTargetFrame\"},x.createElement(\"input\",{ref:\"dataType\",name:\"type\",type:\"hidden\"}),x.createElement(\"input\",{ref:\"filename\",name:\"filename\",type:\"hidden\"}),x.createElement(\"input\",{ref:\"content\",name:\"content\",type:\"hidden\"})),x.createElement($,{ref:\"iframeDialog\"}),x.createElement(re,{ref:\"importDialog\"}),x.createElement(oe,{ref:\"exportDialog\"}),x.createElement(N,{textEditor:!0,standalone:!0}),x.createElement(ae,null))}});z.getInitialData(function(e){T.render(x.createElement(Re,{modal:e}),document.getElementById(\"container\"))})},function(e,t){\"use strict\";function n(e){var t=e.length;if(t%4>0)throw new Error(\"Invalid string. Length must be a multiple of 4\");var n=e.indexOf(\"=\");-1===n&&(n=t);var r=n===t?0:4-n%4;return[n,r]}function r(e){var t=n(e),r=t[0],o=t[1];return 3*(r+o)/4-o}function o(e,t,n){return 3*(t+n)/4-n}function i(e){var t,r,i=n(e),a=i[0],s=i[1],l=new d(o(e,a,s)),c=0,p=s>0?a-4:a;for(r=0;p>r;r+=4)t=u[e.charCodeAt(r)]<<18|u[e.charCodeAt(r+1)]<<12|u[e.charCodeAt(r+2)]<<6|u[e.charCodeAt(r+3)],l[c++]=t>>16&255,l[c++]=t>>8&255,l[c++]=255&t;return 2===s&&(t=u[e.charCodeAt(r)]<<2|u[e.charCodeAt(r+1)]>>4,l[c++]=255&t),1===s&&(t=u[e.charCodeAt(r)]<<10|u[e.charCodeAt(r+1)]<<4|u[e.charCodeAt(r+2)]>>2,l[c++]=t>>8&255,l[c++]=255&t),l}function a(e){return c[e>>18&63]+c[e>>12&63]+c[e>>6&63]+c[63&e]}function s(e,t,n){for(var r,o=[],i=t;n>i;i+=3)r=(e[i]<<16&16711680)+(e[i+1]<<8&65280)+(255&e[i+2]),o.push(a(r));return o.join(\"\")}function l(e){for(var t,n=e.length,r=n%3,o=[],i=16383,a=0,l=n-r;l>a;a+=i)o.push(s(e,a,a+i>l?l:a+i));return 1===r?(t=e[n-1],o.push(c[t>>2]+c[t<<4&63]+\"==\")):2===r&&(t=(e[n-2]<<8)+e[n-1],o.push(c[t>>10]+c[t>>4&63]+c[t<<2&63]+\"=\")),o.join(\"\")}t.byteLength=r,t.toByteArray=i,t.fromByteArray=l;for(var c=[],u=[],d=\"undefined\"!=typeof Uint8Array?Uint8Array:Array,p=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",h=0,g=p.length;g>h;++h)c[h]=p[h],u[p.charCodeAt(h)]=h;u[\"-\".charCodeAt(0)]=62,u[\"_\".charCodeAt(0)]=63},function(e,t,n){var r,o;(function(n){!function(t,n){e.exports=n(t)}(\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:\"undefined\"!=typeof n?n:this,function(n){\"use strict\";n=n||{};var i,a=n.Base64,s=\"2.6.4\",l=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",c=function(e){for(var t={},n=0,r=e.length;r>n;n++)t[e.charAt(n)]=n;return t}(l),u=String.fromCharCode,d=function(e){if(e.length<2){var t=e.charCodeAt(0);return 128>t?e:2048>t?u(192|t>>>6)+u(128|63&t):u(224|t>>>12&15)+u(128|t>>>6&63)+u(128|63&t)}var t=65536+1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320);return u(240|t>>>18&7)+u(128|t>>>12&63)+u(128|t>>>6&63)+u(128|63&t)},p=/[\\uD800-\\uDBFF][\\uDC00-\\uDFFFF]|[^\\x00-\\x7F]/g,h=function(e){return e.replace(p,d)},g=function(e){var t=[0,2,1][e.length%3],n=e.charCodeAt(0)<<16|(e.length>1?e.charCodeAt(1):0)<<8|(e.length>2?e.charCodeAt(2):0),r=[l.charAt(n>>>18),l.charAt(n>>>12&63),t>=2?\"=\":l.charAt(n>>>6&63),t>=1?\"=\":l.charAt(63&n)];return r.join(\"\")},f=n.btoa&&\"function\"==typeof n.btoa?function(e){return n.btoa(e)}:function(e){if(e.match(/[^\\x00-\\xFF]/))throw new RangeError(\"The string contains invalid characters.\");return e.replace(/[\\s\\S]{1,3}/g,g)},m=function(e){return f(h(String(e)))},A=function(e){return e.replace(/[+\\/]/g,function(e){return\"+\"==e?\"-\":\"_\"}).replace(/=/g,\"\")},M=function(e,t){return t?A(m(e)):m(e)},w=function(e){return M(e,!0)};n.Uint8Array&&(i=function(e,t){for(var n=\"\",r=0,o=e.length;o>r;r+=3){var i=e[r],a=e[r+1],s=e[r+2],c=i<<16|a<<8|s;n+=l.charAt(c>>>18)+l.charAt(c>>>12&63)+(\"undefined\"!=typeof a?l.charAt(c>>>6&63):\"=\")+(\"undefined\"!=typeof s?l.charAt(63&c):\"=\")}return t?A(n):n});var v,b=/[\\xC0-\\xDF][\\x80-\\xBF]|[\\xE0-\\xEF][\\x80-\\xBF]{2}|[\\xF0-\\xF7][\\x80-\\xBF]{3}/g,y=function(e){switch(e.length){case 4:var t=(7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3),n=t-65536;return u((n>>>10)+55296)+u((1023&n)+56320);case 3:return u((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return u((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},x=function(e){return e.replace(b,y)},T=function(e){var t=e.length,n=t%4,r=(t>0?c[e.charAt(0)]<<18:0)|(t>1?c[e.charAt(1)]<<12:0)|(t>2?c[e.charAt(2)]<<6:0)|(t>3?c[e.charAt(3)]:0),o=[u(r>>>16),u(r>>>8&255),u(255&r)];return o.length-=[0,0,2,1][n],o.join(\"\")},C=n.atob&&\"function\"==typeof n.atob?function(e){return n.atob(e)}:function(e){return e.replace(/\\S{1,4}/g,T)},N=function(e){return C(String(e).replace(/[^A-Za-z0-9\\+\\/]/g,\"\"))},I=function(e){return x(C(e))},E=function(e){return String(e).replace(/[-_]/g,function(e){return\"-\"==e?\"+\":\"/\"}).replace(/[^A-Za-z0-9\\+\\/]/g,\"\")},D=function(e){return I(E(e))};n.Uint8Array&&(v=function(e){return Uint8Array.from(N(E(e)),function(e){return e.charCodeAt(0)})});var S=function(){var e=n.Base64;return n.Base64=a,e};if(n.Base64={VERSION:s,atob:N,btoa:f,fromBase64:D,toBase64:M,utob:h,encode:M,encodeURI:w,btou:x,decode:D,noConflict:S,fromUint8Array:i,toUint8Array:v},\"function\"==typeof Object.defineProperty){var L=function(e){return{value:e,enumerable:!1,writable:!0,configurable:!0}};n.Base64.extendString=function(){Object.defineProperty(String.prototype,\"fromBase64\",L(function(){return D(this)})),Object.defineProperty(String.prototype,\"toBase64\",L(function(e){return M(this,e)})),Object.defineProperty(String.prototype,\"toBase64URI\",L(function(){return M(this,!0)}))}}return n.Meteor&&(Base64=n.Base64),\"undefined\"!=typeof e&&e.exports?e.exports.Base64=n.Base64:(r=[],o=function(){return n.Base64}.apply(t,r),!(void 0!==o&&(e.exports=o))),{Base64:n.Base64}})}).call(t,function(){return this}())},function(e,t){\"use strict\";function n(e,t){t=t||0;for(var n=Math.min(e.length,r);n>t;t++){var o=e[t];if(!(9==o||10==o||13==o||o>=32&&127>=o)){++t;var i=e[t];if(o>=194&&223>=o){if(i>=128&&191>=i)continue;return!i}++t;var a=e[t];if(224==o){if(i>=160&&191>=i&&a>=128&&191>=a)continue;return!a}if(o>=225&&236>=o||238==o||239==o){if(i>=128&&191>=i&&a>=128&&191>=a)continue;return!a}if(237==o){if(i>=128&&159>=i&&a>=128&&191>=a)continue;return!a}++t;var s=e[t];if(240==o){if(i>=144&&191>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}if(o>=241&&243>=o){if(i>=128&&191>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}if(244==o){if(i>=128&&143>=i&&a>=128&&191>=a&&s>=128&&191>=s)continue;return!s}return!1}}return!0}var r=32768;e.exports=function(e){return n(e)?!0:0===e[0]&&n(e,5)}},function(e,t,n){\"use strict\";n(5),n(14),n(16),window.jQuery=n(18),n(19)},function(e,t,n){var r=n(6);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,'/*!\\n * Bootstrap v3.3.5 (http://getbootstrap.com)\\n * Copyright 2011-2015 Twitter, Inc.\\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\\n */\\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}\\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:\" (\" attr(href) \")\"}abbr[title]:after{content:\" (\" attr(title) \")\"}a[href^=\"#\"]:after,a[href^=\"javascript:\"]:after{content:\"\"}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:Glyphicons Halflings;src:url('+n(8)+\");src:url(\"+n(8)+\"?#iefix) format('embedded-opentype'),url(\"+n(9)+\") format('woff2'),url(\"+n(10)+\") format('woff'),url(\"+n(11)+\") format('truetype'),url(\"+n(12)+'#glyphicons_halflingsregular) format(\\'svg\\')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:Glyphicons Halflings;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:\"*\"}.glyphicon-plus:before{content:\"+\"}.glyphicon-eur:before,.glyphicon-euro:before{content:\"\\\\20AC\"}.glyphicon-minus:before{content:\"\\\\2212\"}.glyphicon-cloud:before{content:\"\\\\2601\"}.glyphicon-envelope:before{content:\"\\\\2709\"}.glyphicon-pencil:before{content:\"\\\\270F\"}.glyphicon-glass:before{content:\"\\\\E001\"}.glyphicon-music:before{content:\"\\\\E002\"}.glyphicon-search:before{content:\"\\\\E003\"}.glyphicon-heart:before{content:\"\\\\E005\"}.glyphicon-star:before{content:\"\\\\E006\"}.glyphicon-star-empty:before{content:\"\\\\E007\"}.glyphicon-user:before{content:\"\\\\E008\"}.glyphicon-film:before{content:\"\\\\E009\"}.glyphicon-th-large:before{content:\"\\\\E010\"}.glyphicon-th:before{content:\"\\\\E011\"}.glyphicon-th-list:before{content:\"\\\\E012\"}.glyphicon-ok:before{content:\"\\\\E013\"}.glyphicon-remove:before{content:\"\\\\E014\"}.glyphicon-zoom-in:before{content:\"\\\\E015\"}.glyphicon-zoom-out:before{content:\"\\\\E016\"}.glyphicon-off:before{content:\"\\\\E017\"}.glyphicon-signal:before{content:\"\\\\E018\"}.glyphicon-cog:before{content:\"\\\\E019\"}.glyphicon-trash:before{content:\"\\\\E020\"}.glyphicon-home:before{content:\"\\\\E021\"}.glyphicon-file:before{content:\"\\\\E022\"}.glyphicon-time:before{content:\"\\\\E023\"}.glyphicon-road:before{content:\"\\\\E024\"}.glyphicon-download-alt:before{content:\"\\\\E025\"}.glyphicon-download:before{content:\"\\\\E026\"}.glyphicon-upload:before{content:\"\\\\E027\"}.glyphicon-inbox:before{content:\"\\\\E028\"}.glyphicon-play-circle:before{content:\"\\\\E029\"}.glyphicon-repeat:before{content:\"\\\\E030\"}.glyphicon-refresh:before{content:\"\\\\E031\"}.glyphicon-list-alt:before{content:\"\\\\E032\"}.glyphicon-lock:before{content:\"\\\\E033\"}.glyphicon-flag:before{content:\"\\\\E034\"}.glyphicon-headphones:before{content:\"\\\\E035\"}.glyphicon-volume-off:before{content:\"\\\\E036\"}.glyphicon-volume-down:before{content:\"\\\\E037\"}.glyphicon-volume-up:before{content:\"\\\\E038\"}.glyphicon-qrcode:before{content:\"\\\\E039\"}.glyphicon-barcode:before{content:\"\\\\E040\"}.glyphicon-tag:before{content:\"\\\\E041\"}.glyphicon-tags:before{content:\"\\\\E042\"}.glyphicon-book:before{content:\"\\\\E043\"}.glyphicon-bookmark:before{content:\"\\\\E044\"}.glyphicon-print:before{content:\"\\\\E045\"}.glyphicon-camera:before{content:\"\\\\E046\"}.glyphicon-font:before{content:\"\\\\E047\"}.glyphicon-bold:before{content:\"\\\\E048\"}.glyphicon-italic:before{content:\"\\\\E049\"}.glyphicon-text-height:before{content:\"\\\\E050\"}.glyphicon-text-width:before{content:\"\\\\E051\"}.glyphicon-align-left:before{content:\"\\\\E052\"}.glyphicon-align-center:before{content:\"\\\\E053\"}.glyphicon-align-right:before{content:\"\\\\E054\"}.glyphicon-align-justify:before{content:\"\\\\E055\"}.glyphicon-list:before{content:\"\\\\E056\"}.glyphicon-indent-left:before{content:\"\\\\E057\"}.glyphicon-indent-right:before{content:\"\\\\E058\"}.glyphicon-facetime-video:before{content:\"\\\\E059\"}.glyphicon-picture:before{content:\"\\\\E060\"}.glyphicon-map-marker:before{content:\"\\\\E062\"}.glyphicon-adjust:before{content:\"\\\\E063\"}.glyphicon-tint:before{content:\"\\\\E064\"}.glyphicon-edit:before{content:\"\\\\E065\"}.glyphicon-share:before{content:\"\\\\E066\"}.glyphicon-check:before{content:\"\\\\E067\"}.glyphicon-move:before{content:\"\\\\E068\"}.glyphicon-step-backward:before{content:\"\\\\E069\"}.glyphicon-fast-backward:before{content:\"\\\\E070\"}.glyphicon-backward:before{content:\"\\\\E071\"}.glyphicon-play:before{content:\"\\\\E072\"}.glyphicon-pause:before{content:\"\\\\E073\"}.glyphicon-stop:before{content:\"\\\\E074\"}.glyphicon-forward:before{content:\"\\\\E075\"}.glyphicon-fast-forward:before{content:\"\\\\E076\"}.glyphicon-step-forward:before{content:\"\\\\E077\"}.glyphicon-eject:before{content:\"\\\\E078\"}.glyphicon-chevron-left:before{content:\"\\\\E079\"}.glyphicon-chevron-right:before{content:\"\\\\E080\"}.glyphicon-plus-sign:before{content:\"\\\\E081\"}.glyphicon-minus-sign:before{content:\"\\\\E082\"}.glyphicon-remove-sign:before{content:\"\\\\E083\"}.glyphicon-ok-sign:before{content:\"\\\\E084\"}.glyphicon-question-sign:before{content:\"\\\\E085\"}.glyphicon-info-sign:before{content:\"\\\\E086\"}.glyphicon-screenshot:before{content:\"\\\\E087\"}.glyphicon-remove-circle:before{content:\"\\\\E088\"}.glyphicon-ok-circle:before{content:\"\\\\E089\"}.glyphicon-ban-circle:before{content:\"\\\\E090\"}.glyphicon-arrow-left:before{content:\"\\\\E091\"}.glyphicon-arrow-right:before{content:\"\\\\E092\"}.glyphicon-arrow-up:before{content:\"\\\\E093\"}.glyphicon-arrow-down:before{content:\"\\\\E094\"}.glyphicon-share-alt:before{content:\"\\\\E095\"}.glyphicon-resize-full:before{content:\"\\\\E096\"}.glyphicon-resize-small:before{content:\"\\\\E097\"}.glyphicon-exclamation-sign:before{content:\"\\\\E101\"}.glyphicon-gift:before{content:\"\\\\E102\"}.glyphicon-leaf:before{content:\"\\\\E103\"}.glyphicon-fire:before{content:\"\\\\E104\"}.glyphicon-eye-open:before{content:\"\\\\E105\"}.glyphicon-eye-close:before{content:\"\\\\E106\"}.glyphicon-warning-sign:before{content:\"\\\\E107\"}.glyphicon-plane:before{content:\"\\\\E108\"}.glyphicon-calendar:before{content:\"\\\\E109\"}.glyphicon-random:before{content:\"\\\\E110\"}.glyphicon-comment:before{content:\"\\\\E111\"}.glyphicon-magnet:before{content:\"\\\\E112\"}.glyphicon-chevron-up:before{content:\"\\\\E113\"}.glyphicon-chevron-down:before{content:\"\\\\E114\"}.glyphicon-retweet:before{content:\"\\\\E115\"}.glyphicon-shopping-cart:before{content:\"\\\\E116\"}.glyphicon-folder-close:before{content:\"\\\\E117\"}.glyphicon-folder-open:before{content:\"\\\\E118\"}.glyphicon-resize-vertical:before{content:\"\\\\E119\"}.glyphicon-resize-horizontal:before{content:\"\\\\E120\"}.glyphicon-hdd:before{content:\"\\\\E121\"}.glyphicon-bullhorn:before{content:\"\\\\E122\"}.glyphicon-bell:before{content:\"\\\\E123\"}.glyphicon-certificate:before{content:\"\\\\E124\"}.glyphicon-thumbs-up:before{content:\"\\\\E125\"}.glyphicon-thumbs-down:before{content:\"\\\\E126\"}.glyphicon-hand-right:before{content:\"\\\\E127\"}.glyphicon-hand-left:before{content:\"\\\\E128\"}.glyphicon-hand-up:before{content:\"\\\\E129\"}.glyphicon-hand-down:before{content:\"\\\\E130\"}.glyphicon-circle-arrow-right:before{content:\"\\\\E131\"}.glyphicon-circle-arrow-left:before{content:\"\\\\E132\"}.glyphicon-circle-arrow-up:before{content:\"\\\\E133\"}.glyphicon-circle-arrow-down:before{content:\"\\\\E134\"}.glyphicon-globe:before{content:\"\\\\E135\"}.glyphicon-wrench:before{content:\"\\\\E136\"}.glyphicon-tasks:before{content:\"\\\\E137\"}.glyphicon-filter:before{content:\"\\\\E138\"}.glyphicon-briefcase:before{content:\"\\\\E139\"}.glyphicon-fullscreen:before{content:\"\\\\E140\"}.glyphicon-dashboard:before{content:\"\\\\E141\"}.glyphicon-paperclip:before{content:\"\\\\E142\"}.glyphicon-heart-empty:before{content:\"\\\\E143\"}.glyphicon-link:before{content:\"\\\\E144\"}.glyphicon-phone:before{content:\"\\\\E145\"}.glyphicon-pushpin:before{content:\"\\\\E146\"}.glyphicon-usd:before{content:\"\\\\E148\"}.glyphicon-gbp:before{content:\"\\\\E149\"}.glyphicon-sort:before{content:\"\\\\E150\"}.glyphicon-sort-by-alphabet:before{content:\"\\\\E151\"}.glyphicon-sort-by-alphabet-alt:before{content:\"\\\\E152\"}.glyphicon-sort-by-order:before{content:\"\\\\E153\"}.glyphicon-sort-by-order-alt:before{content:\"\\\\E154\"}.glyphicon-sort-by-attributes:before{content:\"\\\\E155\"}.glyphicon-sort-by-attributes-alt:before{content:\"\\\\E156\"}.glyphicon-unchecked:before{content:\"\\\\E157\"}.glyphicon-expand:before{content:\"\\\\E158\"}.glyphicon-collapse-down:before{content:\"\\\\E159\"}.glyphicon-collapse-up:before{content:\"\\\\E160\"}.glyphicon-log-in:before{content:\"\\\\E161\"}.glyphicon-flash:before{content:\"\\\\E162\"}.glyphicon-log-out:before{content:\"\\\\E163\"}.glyphicon-new-window:before{content:\"\\\\E164\"}.glyphicon-record:before{content:\"\\\\E165\"}.glyphicon-save:before{content:\"\\\\E166\"}.glyphicon-open:before{content:\"\\\\E167\"}.glyphicon-saved:before{content:\"\\\\E168\"}.glyphicon-import:before{content:\"\\\\E169\"}.glyphicon-export:before{content:\"\\\\E170\"}.glyphicon-send:before{content:\"\\\\E171\"}.glyphicon-floppy-disk:before{content:\"\\\\E172\"}.glyphicon-floppy-saved:before{content:\"\\\\E173\"}.glyphicon-floppy-remove:before{content:\"\\\\E174\"}.glyphicon-floppy-save:before{content:\"\\\\E175\"}.glyphicon-floppy-open:before{content:\"\\\\E176\"}.glyphicon-credit-card:before{content:\"\\\\E177\"}.glyphicon-transfer:before{content:\"\\\\E178\"}.glyphicon-cutlery:before{content:\"\\\\E179\"}.glyphicon-header:before{content:\"\\\\E180\"}.glyphicon-compressed:before{content:\"\\\\E181\"}.glyphicon-earphone:before{content:\"\\\\E182\"}.glyphicon-phone-alt:before{content:\"\\\\E183\"}.glyphicon-tower:before{content:\"\\\\E184\"}.glyphicon-stats:before{content:\"\\\\E185\"}.glyphicon-sd-video:before{content:\"\\\\E186\"}.glyphicon-hd-video:before{content:\"\\\\E187\"}.glyphicon-subtitles:before{content:\"\\\\E188\"}.glyphicon-sound-stereo:before{content:\"\\\\E189\"}.glyphicon-sound-dolby:before{content:\"\\\\E190\"}.glyphicon-sound-5-1:before{content:\"\\\\E191\"}.glyphicon-sound-6-1:before{content:\"\\\\E192\"}.glyphicon-sound-7-1:before{content:\"\\\\E193\"}.glyphicon-copyright-mark:before{content:\"\\\\E194\"}.glyphicon-registration-mark:before{content:\"\\\\E195\"}.glyphicon-cloud-download:before{content:\"\\\\E197\"}.glyphicon-cloud-upload:before{content:\"\\\\E198\"}.glyphicon-tree-conifer:before{content:\"\\\\E199\"}.glyphicon-tree-deciduous:before{content:\"\\\\E200\"}.glyphicon-cd:before{content:\"\\\\E201\"}.glyphicon-save-file:before{content:\"\\\\E202\"}.glyphicon-open-file:before{content:\"\\\\E203\"}.glyphicon-level-up:before{content:\"\\\\E204\"}.glyphicon-copy:before{content:\"\\\\E205\"}.glyphicon-paste:before{content:\"\\\\E206\"}.glyphicon-alert:before{content:\"\\\\E209\"}.glyphicon-equalizer:before{content:\"\\\\E210\"}.glyphicon-king:before{content:\"\\\\E211\"}.glyphicon-queen:before{content:\"\\\\E212\"}.glyphicon-pawn:before{content:\"\\\\E213\"}.glyphicon-bishop:before{content:\"\\\\E214\"}.glyphicon-knight:before{content:\"\\\\E215\"}.glyphicon-baby-formula:before{content:\"\\\\E216\"}.glyphicon-tent:before{content:\"\\\\26FA\"}.glyphicon-blackboard:before{content:\"\\\\E218\"}.glyphicon-bed:before{content:\"\\\\E219\"}.glyphicon-apple:before{content:\"\\\\F8FF\"}.glyphicon-erase:before{content:\"\\\\E221\"}.glyphicon-hourglass:before{content:\"\\\\231B\"}.glyphicon-lamp:before{content:\"\\\\E223\"}.glyphicon-duplicate:before{content:\"\\\\E224\"}.glyphicon-piggy-bank:before{content:\"\\\\E225\"}.glyphicon-scissors:before{content:\"\\\\E226\"}.glyphicon-bitcoin:before,.glyphicon-btc:before,.glyphicon-xbt:before{content:\"\\\\E227\"}.glyphicon-jpy:before,.glyphicon-yen:before{content:\"\\\\A5\"}.glyphicon-rub:before,.glyphicon-ruble:before{content:\"\\\\20BD\"}.glyphicon-scale:before{content:\"\\\\E230\"}.glyphicon-ice-lolly:before{content:\"\\\\E231\"}.glyphicon-ice-lolly-tasted:before{content:\"\\\\E232\"}.glyphicon-education:before{content:\"\\\\E233\"}.glyphicon-option-horizontal:before{content:\"\\\\E234\"}.glyphicon-option-vertical:before{content:\"\\\\E235\"}.glyphicon-menu-hamburger:before{content:\"\\\\E236\"}.glyphicon-modal-window:before{content:\"\\\\E237\"}.glyphicon-oil:before{content:\"\\\\E238\"}.glyphicon-grain:before{content:\"\\\\E239\"}.glyphicon-sunglasses:before{content:\"\\\\E240\"}.glyphicon-text-size:before{content:\"\\\\E241\"}.glyphicon-text-color:before{content:\"\\\\E242\"}.glyphicon-text-background:before{content:\"\\\\E243\"}.glyphicon-object-align-top:before{content:\"\\\\E244\"}.glyphicon-object-align-bottom:before{content:\"\\\\E245\"}.glyphicon-object-align-horizontal:before{content:\"\\\\E246\"}.glyphicon-object-align-left:before{content:\"\\\\E247\"}.glyphicon-object-align-vertical:before{content:\"\\\\E248\"}.glyphicon-object-align-right:before{content:\"\\\\E249\"}.glyphicon-triangle-right:before{content:\"\\\\E250\"}.glyphicon-triangle-left:before{content:\"\\\\E251\"}.glyphicon-triangle-bottom:before{content:\"\\\\E252\"}.glyphicon-triangle-top:before{content:\"\\\\E253\"}.glyphicon-console:before{content:\"\\\\E254\"}.glyphicon-superscript:before{content:\"\\\\E255\"}.glyphicon-subscript:before{content:\"\\\\E256\"}.glyphicon-menu-left:before{content:\"\\\\E257\"}.glyphicon-menu-right:before{content:\"\\\\E258\"}.glyphicon-menu-down:before{content:\"\\\\E259\"}.glyphicon-menu-up:before{content:\"\\\\E260\"}*,:after,:before{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:9pt}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:1pc;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline{margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:10pc;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:\\'\\\\2014   \\\\A0\\'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:\\'\\'}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:\\'\\\\A0   \\\\2014\\'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,Courier New,monospace}code{color:#c7254e;background-color:#f9f2f4;border-radius:4px}code,kbd{padding:2px 4px;font-size:90%}kbd{color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777}caption,th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered,.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;margin:0}fieldset,legend{padding:0;border:0}legend{display:block;width:100%;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\\\\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{padding-top:7px}.form-control,output{display:block;font-size:14px;line-height:1.42857143;color:#555}.form-control{width:100%;height:34px;padding:6px 9pt;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\\\\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.checkbox-inline.disabled,.checkbox.disabled label,.radio-inline.disabled,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio label,fieldset[disabled] .radio-inline,fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:9pt;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:9pt;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:2pc;padding:6px 10px;font-size:9pt;line-height:1.5}.input-lg{height:46px;padding:10px 1pc;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 1pc;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 1pc;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.333333px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:9pt}}.btn{display:inline-block;padding:6px 9pt;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary.active,.btn-primary:active,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success.active,.btn-success:active,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info.active,.btn-info:active,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger.active,.btn-danger:active,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 1pc;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:9pt;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:9pt;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-timing-function:ease;transition-duration:.35s;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\\\\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10pc;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;box-shadow:0 6px 9pt rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:9pt;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:\"\";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\\\\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:9pt;padding-left:9pt}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 1pc;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:9pt;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 9pt;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:9pt;border-radius:3px}.input-group-addon.input-lg{padding:10px 1pc;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{font-size:0;white-space:nowrap}.input-group-btn,.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li,.nav>li>a{position:relative;display:block}.nav>li>a{padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;box-shadow:inset 0 1px 0 hsla(0,0%,100%,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin:8px -15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 hsla(0,0%,100%,.1),0 1px 0 hsla(0,0%,100%,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-nav>li>a,.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>li>a,.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:\"/\\\\A0\"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 9pt;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 1pc;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:9pt;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:9pt;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;background-color:#eee}.jumbotron,.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:3pc;padding-bottom:3pc}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@keyframes progress-bar-stripes{0%{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:9pt;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 25%,transparent 50%,hsla(0,0%,100%,.15) 50%,hsla(0,0%,100%,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 25%,transparent 50%,hsla(0,0%,100%,.15) 50%,hsla(0,0%,100%,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 25%,transparent 50%,hsla(0,0%,100%,.15) 50%,hsla(0,0%,100%,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 25%,transparent 50%,hsla(0,0%,100%,.15) 50%,hsla(0,0%,100%,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 25%,transparent 50%,hsla(0,0%,100%,.15) 50%,hsla(0,0%,100%,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:625pc}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle,.panel-title{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:1pc}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.list-group+.panel-footer,.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:transparent;border:0}.modal,.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0, -25%)}.modal.in .modal-dialog{transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.42857143px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:9pt;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:\"\";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:\" \";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:\" \";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:\" \";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:\" \";border-right-width:0;border-left-color:#fff}.carousel,.carousel-inner{position:relative}.carousel-inner{width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media (-webkit-transform-3d),all and (transform-3d){.carousel-inner>.item{transition:transform .6s ease-in-out;backface-visibility:hidden;perspective:750pt}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:linear-gradient(to right,rgba(0,0,0,.5) 0%,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=\\'#80000000\\',endColorstr=\\'#00000000\\',GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0%,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=\\'#00000000\\',endColorstr=\\'#80000000\\',GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:\\'\\\\2039\\'}.carousel-control .icon-next:before{content:\\'\\\\203A\\'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\\\\9;background-color:transparent;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:9pt;height:9pt;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:\" \"}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}',\"\"]);\n},function(e,t){e.exports=function(){var e=[];return e.toString=function(){for(var e=[],t=0;t<this.length;t++){var n=this[t];n[2]?e.push(\"@media \"+n[2]+\"{\"+n[1]+\"}\"):e.push(n[1])}return e.join(\"\")},e.i=function(t,n){\"string\"==typeof t&&(t=[[null,t,\"\"]]);for(var r={},o=0;o<this.length;o++){var i=this[o][0];\"number\"==typeof i&&(r[i]=!0)}for(o=0;o<t.length;o++){var a=t[o];\"number\"==typeof a[0]&&r[a[0]]||(n&&!a[2]?a[2]=n:n&&(a[2]=\"(\"+a[2]+\") and (\"+n+\")\"),e.push(a))}},e}},function(e,t){e.exports=\"data:application/vnd.ms-fontobject;base64,n04AAEFNAAACAAIABAAAAAAABQAAAAAAAAABAJABAAAEAExQAAAAAAAAAAIAAAAAAAAAAAEAAAAAAAAAJxJ/LAAAAAAAAAAAAAAAAAAAAAAAACgARwBMAFkAUABIAEkAQwBPAE4AUwAgAEgAYQBsAGYAbABpAG4AZwBzAAAADgBSAGUAZwB1AGwAYQByAAAAeABWAGUAcgBzAGkAbwBuACAAMQAuADAAMAA5ADsAUABTACAAMAAwADEALgAwADAAOQA7AGgAbwB0AGMAbwBuAHYAIAAxAC4AMAAuADcAMAA7AG0AYQBrAGUAbwB0AGYALgBsAGkAYgAyAC4ANQAuADUAOAAzADIAOQAAADgARwBMAFkAUABIAEkAQwBPAE4AUwAgAEgAYQBsAGYAbABpAG4AZwBzACAAUgBlAGcAdQBsAGEAcgAAAAAAQlNHUAAAAAAAAAAAAAAAAAAAAAADAKncAE0TAE0ZAEbuFM3pjM/SEdmjKHUbyow8ATBE40IvWA3vTu8LiABDQ+pexwUMcm1SMnNryctQSiI1K5ZnbOlXKmnVV5YvRe6RnNMFNCOs1KNVpn6yZhCJkRtVRNzEufeIq7HgSrcx4S8h/v4vnrrKc6oCNxmSk2uKlZQHBii6iKFoH0746ThvkO1kJHlxjrkxs+LWORaDQBEtiYJIR5IB9Bi1UyL4Rmr0BNigNkMzlKQmnofBHviqVzUxwdMb3NdCn69hy+pRYVKGVS/1tnsqv4LL7wCCPZZAZPT4aCShHjHJVNuXbmMrY5LeQaGnvAkXlVrJgKRAUdFjrWEah9XebPeQMj7KS7DIBAFt8ycgC5PLGUOHSE3ErGZCiViNLL5ZARfywnCoZaKQCu6NuFX42AEeKtKUGnr/Cm2Cy8tpFhBPMW5Fxi4Qm4TkDWh4IWFDClhU2hRWosUWqcKLlgyXB+lSHaWaHiWlBAR8SeSgSPCQxdVQgzUixWKSTrIQEbU94viDctkvX+VSjJuUmV8L4CXShI11esnp0pjWNZIyxKHS4wVQ2ime1P4RnhvGw0aDN1OLAXGERsB7buFpFGGBAre4QEQR0HOIO5oYH305G+KspT/FupEGGafCCwxSe6ZUa+073rXHnNdVXE6eWvibUS27XtRzkH838mYLMBmYysZTM0EM3A1fbpCBYFccN1B/EnCYu/TgCGmr7bMh8GfYL+BfcLvB0gRagC09w9elfldaIy/hNCBLRgBgtCC7jAF63wLSMAfbfAlEggYU0bUA7ACCJmTDpEmJtI78w4/BO7dN7JR7J7ZvbYaUbaILSQsRBiF3HGk5fEg6p9unwLvn98r+vnsV+372uf1xBLq4qU/45fTuqaAP+pssmCCCTF0mhEow8ZXZOS8D7Q85JsxZ+Azok7B7O/f6J8AzYBySZQB/QHYUSA+EeQhEWiS6AIQzgcsDiER4MjgMBAWDV4AgQ3g1eBgIdweCQmCjJEMkJ+PKRWyFHHmg1Wi/6xzUgA0LREoKJChwnQa9B+5RQZRB3IlBlkAnxyQNaANwHMowzlYSMCBgnbpzvqpl0iTJNCQidDI9ZrSYNIRBhHtUa5YHMHxyGEik9hDE0AKj72AbTCaxtHPUaKZdAZSnQTyjGqGLsmBStCejApUhg4uBMU6mATujEl+KdDPbI6Ag4vLr+hjY6lbjBeoLKnZl0UZgRX8gTySOeynZVz1wOq7e1hFGYIq+MhrGxDLak0PrwYzSXtcuyhXEhwOYofiW+EcI/jw8P6IY6ed+etAbuqKp5QIapT77LnAe505lMuqL79a0ut4rWexzFttsOsLDy7zvtQzcq3U1qabe7tB0wHWVXji+zDbo8x8HyIRUbXnwUcklFv51fvTymiV+MXLSmGH9d9+aXpD5X6lao41anWGig7IwIdnoBY2ht/pO9mClLo4NdXHAsefqWUKlXJkbqPOFhMoR4aiA1BXqhRNbB2Xwi+7u/jpAoOpKJ0UX24EsrzMfHXViakCNcKjBxuQX8BO0ZqjJ3xXzf+61t2VXOSgJ8xu65QKgtN6FibPmPYsXbJRHHqbgATcSZxBqGiDiU4NNNsYBsKD0MIP/OfKnlk/Lkaid/O2NbKeuQrwOB2Gq3YHyr6ALgzym5wIBnsdC1ZkoBFZSQXChZvlesPqvK2c5oHHT3Q65jYpNxnQcGF0EHbvYqoFw60WNlXIHQF2HQB7zD6lWjZ9rVqUKBXUT6hrkZOle0RFYII0V5ZYGl1JAP0Ud1fZZMvSomBzJ710j4Me8mjQDwEre5Uv2wQfk1ifDwb5ksuJQQ3xt423lbuQjvoIQByQrNDh1JxGFkOdlJvu/gFtuW0wR4cgd+ZKesSV7QkNE2kw6AV4hoIuC02LGmTomyf8PiO6CZzOTLTPQ+HW06H+tx+bQ8LmDYg1pTFrp2oJXgkZTyeRJZM0C8aE2LpFrNVDuhARsN543/FV6klQ6Tv1OoZGXLv0igKrl/CmJxRmX7JJbJ998VSIPQRyDBICzl4JJlYHbdql30NvYcOuZ7a10uWRrgoieOdgIm4rlq6vNOQBuqESLbXG5lzdJGHw2m0sDYmODXbYGTfSTGRKpssTO95fothJCjUGQgEL4yKoGAF/0SrpUDNn8CBgBcSDQByAeNkCXp4S4Ro2Xh4OeaGRgR66PVOsU8bc6TR5/xTcn4IVMLOkXSWiXxkZQCbvKfmoAvQaKjO3EDKwkwqHChCDEM5loQRPd5ACBki1TjF772oaQhQbQ5C0lcWXPFOzrfsDGUXGrpxasbG4iab6eByaQkQfm0VFlP0ZsDkvvqCL6QXMUwCjdMx1ZOyKhTJ7a1GWAdOUcJ8RSejxNVyGs31OKMyRyBVoZFjqIkmKlLQ5eHMeEL4MkUf23cQ/1SgRCJ1dk4UdBT7OoyuNgLs0oCd8RnrEIb6QdMxT2QjD4zMrJkfgx5aDMcA4orsTtKCqWb/Veyceqa5OGSmB28YwH4rFbkQaLoUN8OQQYnD3w2eXpI4ScQfbCUZiJ4yMOIKLyyTc7BQ4uXUw6Ee6/xM+4Y67ngNBknxIPwuppgIhFcwJyr6EIj+LzNj/mfR2vhhRlx0BILZoAYruF0caWQ7YxO66UmeguDREAFHYuC7HJviRgVO6ruJH59h/C/PkgSle8xNzZJULLWq9JMDTE2fjGE146a1Us6PZDGYle6ldWRqn/pdpgHKNGrGIdkRK+KPETT9nKT6kLyDI8xd9A1FgWmXWRAIHwZ37WyZHOVyCadJEmMVz0MadMjDrPho+EIochkVC2xgGiwwsQ6DMv2P7UXqT4x7CdcYGId2BJQQa85EQKmCmwcRejQ9Bm4oATENFPkxPXILHpMPUyWTI5rjNOsIlmEeMbcOCEqInpXACYQ9DDxmFo9vcmsDblcMtg4tqBerNngkIKaFJmrQAPnq1dEzsMXcwjcHdfdCibcAxxA+q/j9m3LM/O7WJka4tSidVCjsvo2lQ/2ewyoYyXwAYyr2PlRoR5MpgVmSUIrM3PQxXPbgjBOaDQFIyFMJvx3Pc5RSYj12ySVF9fwFPQu2e2KWVoL9q3Ayv3IzpGHUdvdPdrNUdicjsTQ2ISy7QU3DrEytIjvbzJnAkmANXjAFERA0MUoPF3/5KFmW14bBNOhwircYgMqoDpUMcDtCmBE82QM2YtdjVLB4kBuKho/bcwQdeboqfQartuU3CsCf+cXkgYAqp/0Ee3RorAZt0AvvOCSI4JICIlGlsV0bsSid/NIEALAAzb6HAgyWHBps6xAOwkJIGcB82CxRQq4sJf3FzA70A+TRqcqjEMETCoez3mkPcpnoALs0ugJY8kQwrC+JE5ik3w9rzrvDRjAQnqgEVvdGrNwlanR0SOKWzxOJOvLJhcd8Cl4AshACUkv9czdMkJCVQSQhp6kp7StAlpVRpK0t0SW6LHeBJnE2QchB5Ccu8kxRghZXGIgZIiSj7gEKMJDClcnX6hgoqJMwiQDigIXg3ioFLCgDgjPtYHYpsF5EiA4kcnN18MZtOrY866dEQAb0FB34OGKHGZQjwW/WDHA60cYFaI/PjpzquUqdaYGcIq+mLez3WLFFCtNBN2QJcrlcoELgiPku5R5dSlJFaCEqEZle1AQzAKC+1SotMcBNyQUFuRHRF6OlimSBgjZeTBCwLyc6A+P/oFRchXTz5ADknYJHxzrJ5pGuIKRQISU6WyKTBBjD8WozmVYWIsto1AS5rxzKlvJu4E/vwOiKxRtCWsDM+eTHUrmwrCK5BIfMzGkD+0Fk5LzBs0jMYXktNDblB06LMNJ09U8pzSLmo14MS0OMjcdrZ31pyQqxJJpRImlSvfYAK8inkYU52QY2FPEVsjoWewpwhRp5yAuNpkqhdb7ku9Seefl2D0B8SMTFD90xi4CSOwwZy9IKkpMtI3FmFUg3/kFutpQGNc3pCR7gvC4sgwbupDu3DyEN+W6YGLNM21jpB49irxy9BSlHrVDlnihGKHwPrbVFtc+h1rVQKZduxIyojccZIIcOCmhEnC7UkY68WXKQgLi2JCDQkQWJRQuk60hZp0D3rtCTINSeY9Ej2kIKYfGxwOs4j9qMM7fYZiipzgcf7TamnehqdhsiMiCawXnz4xAbyCkLAx5EGbo3Ax1u3dUIKnTxIaxwQTHehPl3V491H0+bC5zgpGz7Io+mjdhKlPJ01EeMpM7UsRJMi1nGjmJg35i6bQBAAxjO/ENJubU2mg3ONySEoWklCwdABETcs7ck3jgiuU9pcKKpbgn+3YlzV1FzIkB6pmEDOSSyDfPPlQskznctFji0kpgZjW5RZe6x9kYT4KJcXg0bNiCyif+pZACCyRMmYsfiKmN9tSO65F0R2OO6ytlEhY5Sj6uRKfFxw0ijJaAx/k3QgnAFSq27/2i4GEBA+UvTJKK/9eISNvG46Em5RZfjTYLdeD8kdXHyrwId/DQZUaMCY4gGbke2C8vfjgV/Y9kkRQOJIn/xM9INZSpiBnqX0Q9GlQPpPKAyO5y+W5NMPSRdBCUlmuxl40ZfMCnf2Cp044uI9WLFtCi4YVxKjuRCOBWIb4XbIsGdbo4qtMQnNOQz4XDSui7W/N6l54qOynCqD3DpWQ+mpD7C40D8BZEWGJX3tlAaZBMj1yjvDYKwCJBa201u6nBKE5UE+7QSEhCwrXfbRZylAaAkplhBWX50dumrElePyNMRYUrC99UmcSSNgImhFhDI4BXjMtiqkgizUGCrZ8iwFxU6fQ8GEHCFdLewwxYWxgScAYMdMLmcZR6b7rZl95eQVDGVoUKcRMM1ixXQtXNkBETZkVVPg8LoSrdetHzkuM7DjZRHP02tCxA1fmkXKF3VzfN1pc1cv/8lbTIkkYpqKM9VOhp65ktYk+Q46myFWBapDfyWUCnsnI00QTBQmuFjMZTcd0V2NQ768Fhpby04k2IzNR1wKabuGJqYWwSly6ocMFGTeeI+ejsWDYgEvr66QgqdcIbFYDNgsm0x9UHY6SCd5+7tpsLpKdvhahIDyYmEJQCqMqtCF6UlrE5GXRmbu+vtm3BFSxI6ND6UxIE7GsGMgWqghXxSnaRJuGFveTcK5ZVSPJyjUxe1dKgI6kNF7EZhIZs8y8FVqwEfbM0Xk2ltORVDKZZM40SD3qQoQe0orJEKwPfZwm3YPqwixhUMOndis6MhbmfvLBKjC8sKKIZKbJk8L11oNkCQzCgvjhyyEiQSuJcgCQSG4Mocfgc0Hkwcjal1UNgP0CBPikYqBIk9tONv4kLtBswH07vUCjEaHiFGlLf8MgXKzSgjp2HolRRccAOh0ILHz9qlGgIFkwAnzHJRjWFhlA7ROwINyB5HFj59PRZHFor6voq7l23EPNRwdWhgawqbivLSjRA4htEYUFkjESu67icTg5S0aW1sOkCiIysfJ9UnIWevOOLGpepcBxy1wEhd2WI3AZg7sr9WBmHWyasxMcvY/iOmsLtHSWNUWEGk9hScMPShasUA1AcHOtRZlqMeQ0OzYS9vQvYUjOLrzP07BUAFikcJNMi7gIxEw4pL1G54TcmmmoAQ5s7TGWErJZ2Io4yQ0ljRYhL8H5e62oDtLF8aDpnIvZ5R3GWJyAugdiiJW9hQAVTsnCBHhwu7rkBlBX6r3b7ejEY0k5GGeyKv66v+6dg7mcJTrWHbtMywbedYqCQ0FPwoytmSWsL8WTtChZCKKzEF7vP6De4x2BJkkniMgSdWhbeBSLtJZR9CTHetK1xb34AYIJ37OegYIoPVbXgJ/qDQK+bfCtxQRVKQu77WzOoM6SGL7MaZwCGJVk46aImai9fmam+WpHG+0BtQPWUgZ7RIAlPq6lkECUhZQ2gqWkMYKcYMYaIc4gYCDFHYa2d1nzp3+J1eCBay8IYZ0wQRKGAqvCuZ/UgbQPyllosq+XtfKIZOzmeJqRazpmmoP/76YfkjzV2NlXTDSBYB04SVlNQsFTbGPk1t/I4Jktu0XSgifO2ozFOiwd/0SssJDn0dn4xqk4GDTTKX73/wQyBLdqgJ+Wx6AQaba3BA9CKEzjtQYIfAsiYamapq80LAamYjinlKXUkxdpIDk0puXUEYzSalfRibAeDAKpNiqQ0FTwoxuGYzRnisyTotdVTclis1LHRQCy/qqL8oUaQzWRxilq5Mi0IJGtMY02cGLD69vGjkj3p6pGePKI8bkBv5evq8SjjyU04vJR2cQXQwSJyoinDsUJHCQ50jrFTT7yRdbdYQMB3MYCb6uBzJ9ewhXYPAIZSXfeEQBZZ3GPN3Nbhh/wkvAJLXnQMdi5NYYZ5GHE400GS5rXkOZSQsdZgIbzRnF9ueLnsfQ47wHAsirITnTlkCcuWWIUhJSbpM3wWhXNHvt2xUsKKMpdBSbJnBMcihkoDqAd1Zml/R4yrzow1Q2A5G+kzo/RhRxQS2lCSDRV8LlYLBOOoo1bF4jwJAwKMK1tWLHlu9i0j4Ig8qVm6wE1DxXwAwQwsaBWUg2pOOol2dHxyt6npwJEdLDDVYyRc2D0HbcbLUJQj8gPevQBUBOUHXPrsAPBERICpnYESeu2OHotpXQxRGlCCtLdIsu23MhZVEoJg8Qumj/UMMc34IBqTKLDTp76WzL/dMjCxK7MjhiGjeYAC/kj/jY/Rde7hpSM1xChrog6yZ7OWTuD56xBJnGFE+pT2ElSyCnJcwVzCjkqeNLfMEJqKW0G7OFIp0G+9mh50I9o8k1tpCY0xYqFNIALgIfc2me4n1bmJnRZ89oepgLPT0NTMLNZsvSCZAc3TXaNB07vail36/dBySis4m9/DR8izaLJW6bWCkVgm5T+ius3ZXq4xI+GnbveLbdRwF2mNtsrE0JjYc1AXknCOrLSu7Te/r4dPYMCl5qtiHNTn+TPbh1jCBHH+dMJNhwNgs3nT+OhQoQ0vYif56BMG6WowAcHR3DjQolxLzyVekHj00PBAaW7IIAF1EF+uRIWyXjQMAs2chdpaKPNaB+kSezYt0+CA04sOg5vx8Fr7Ofa9sUv87h7SLAUFSzbetCCZ9pmyLt6l6/TzoA1/ZBG9bIUVHLAbi/kdBFgYGyGwRQGBpkqCEg2ah9UD6EedEcEL3j4y0BQQCiExEnocA3SZboh+epgd3YsOkHskZwPuQ5OoyA0fTA5AXrHcUOQF+zkJHIA7PwCDk1gGVmGUZSSoPhNf+Tklauz98QofOlCIQ/tCD4dosHYPqtPCXB3agggQQIqQJsSkB+qn0rkQ1toJjON/OtCIB9RYv3PqRA4C4U68ZMlZn6BdgEvi2ziU+TQ6NIw3ej+AtDwMGEZk7e2IjxUWKdAxyaw9OCwSmeADTPPleyk6UhGDNXQb++W6Uk4q6F7/rg6WVTo82IoCxSIsFDrav4EPHphD3u4hR53WKVvYZUwNCCeM4PMBWzK+EfIthZOkuAwPo5C5jgoZgn6dUdvx5rIDmd58cXXdKNfw3l+wM2UjgrDJeQHhbD7HW2QDoZMCujgIUkk5Fg8VCsdyjOtnGRx8wgKRPZN5dR0zPUyfGZFVihbFRniXZFOZGKPnEQzU3AnD1KfR6weHW2XS6KbPJxUkOTZsAB9vTVp3Le1F8q5l+DMcLiIq78jxAImD2pGFw0VHfRatScGlK6SMu8leTmhUSMy8Uhdd6xBiH3Gdman4tjQGLboJfqz6fL2WKHTmrfsKZRYX6BTDjDldKMosaSTLdQS7oDisJNqAUhw1PfTlnacCO8vl8706Km1FROgLDmudzxg+EWTiArtHgLsRrAXYWdB0NmToNCJdKm0KWycZQqb+Mw76Qy29iQ5up/X7oyw8QZ75kP5F6iJAJz6KCmqxz8fEa/xnsMYcIO/vEkGRuMckhr4rIeLrKaXnmIzlNLxbFspOphkcnJdnz/Chp/Vlpj2P7jJQmQRwGnltkTV5dbF9fE3/fxoSqTROgq9wFUlbuYzYcasE0ouzBo+dDCDzxKAfhbAZYxQiHrLzV2iVexnDX/QnT1fsT/xuhu1ui5qIytgbGmRoQkeQooO8eJNNZsf0iALur8QxZFH0nCMnjerYQqG1pIfjyVZWxhVRznmmfLG00BcBWJE6hzQWRyFknuJnXuk8A5FRDCulwrWASSNoBtR+CtGdkPwYN2o7DOw/VGlCZPusRBFXODQdUM5zeHDIVuAJBLqbO/f9Qua+pDqEPk230Sob9lEZ8BHiCorjVghuI0lI4JDgHGRDD/prQ84B1pVGkIpVUAHCG+iz3Bn3qm2AVrYcYWhock4jso5+J7HfHVj4WMIQdGctq3psBCVVzupQOEioBGA2Bk+UILT7+VoX5mdxxA5fS42gISQVi/HTzrgMxu0fY6hE1ocUwwbsbWcezrY2n6S8/6cxXkOH4prpmPuFoikTzY7T85C4T2XYlbxLglSv2uLCgFv8Quk/wdesUdWPeHYIH0R729JIisN9Apdd4eB10aqwXrPt+Su9mA8k8n1sjMwnfsfF2j3jMUzXepSHmZ/BfqXvzgUNQQWOXO8YEuFBh4QTYCkOAPxywpYu1VxiDyJmKVcmJPGWk/gc3Pov02StyYDahwmzw3E1gYC9wkupyWfDqDSUMpCTH5e5N8B//lHiMuIkTNw4USHrJU67bjXGqNav6PBuQSoqTxc8avHoGmvqNtXzIaoyMIQIiiUHIM64cXieouplhNYln7qgc4wBVAYR104kO+CvKqsg4yIUlFNThVUAKZxZt1XA34h3TCUUiXVkZ0w8Hh2R0Z5L0b4LZvPd/p1gi/07h8qfwHrByuSxglc9cI4QIg2oqvC/qm0i7tjPLTgDhoWTAKDO2ONW5oe+/eKB9vZB8K6C25yCZ9RFVMnb6NRdRjyVK57CHHSkJBfnM2/j4ODUwRkqrtBBCrDsDpt8jhZdXoy/1BCqw3sSGhgGGy0a5Jw6BP/TExoCmNFYjZl248A0osgPyGEmRA+fAsqPVaNAfytu0vuQJ7rk3J4kTDTR2AlCHJ5cls26opZM4w3jMULh2YXKpcqGBtuleAlOZnaZGbD6DHzMd6i2oFeJ8z9XYmalg1Szd/ocZDc1C7Y6vcALJz2lYnTXiWEr2wawtoR4g3jvWUU2Ngjd1cewtFzEvM1NiHZPeLlIXFbBPawxNgMwwAlyNSuGF3zizVeOoC9bag1qRAQKQE/EZBWC2J8mnXAN2aTBboZ7HewnObE8CwROudZHmUM5oZ/Ugd/JZQK8lvAm43uDRAbyW8gZ+ZGq0EVerVGUKUSm/Idn8AQHdR4m7bue88WBwft9mSCeMOt1ncBwziOmJYI2ZR7ewNMPiCugmSsE4EyQ+QATJG6qORMGd4snEzc6B4shPIo4G1T7PgSm8PY5eUkPdF8JZ0VBtadbHXoJgnEhZQaODPj2gpODKJY5Yp4DOsLBFxWbvXN755KWylJm+oOd4zEL9Hpubuy2gyyfxh8oEfFutnYWdfB8PdESLWYvSqbElP9qo3u6KTmkhoacDauMNNjj0oy40DFV7Ql0aZj77xfGl7TJNHnIwgqOkenruYYNo6h724+zUQ7+vkCpZB+pGA562hYQiDxHVWOq0oDQl/QsoiY+cuI7iWq/ZIBtHcXJ7kks+h2fCNUPA82BzjnqktNts+RLdk1VSu+tqEn7QZCCsvEqk6FkfiOYkrsw092J8jsfIuEKypNjLxrKA9kiA19mxBD2suxQKCzwXGws7kEJvlhUiV9tArLIdZW0IORcxEzdzKmjtFhsjKy/44XYXdI5noQoRcvjZ1RMPACRqYg2V1+OwOepcOknRLLFdYgTkT5UApt/JhLM3jeFYprZV+Zow2g8fP+U68hkKFWJj2yBbKqsrp25xkZX1DAjUw52IMYWaOhab8Kp05VrdNftqwRrymWF4OQSjbdfzmRZirK8FMJELEgER2PHjEAN9pGfLhCUiTJFbd5LBkOBMaxLr/A1SY9dXFz4RjzoU9ExfJCmx/I9FKEGT3n2cmzl2X42L3Jh+AbQq6sA+Ss1kitoa4TAYgKHaoybHUDJ51oETdeI/9ThSmjWGkyLi5QAGWhL0BG1UsTyRGRJOldKBrYJeB8ljLJHfATWTEQBXBDnQexOHTB+Un44zExFE4vLytcu5NwpWrUxO/0ZICUGM7hGABXym0V6ZvDST0E370St9MIWQOTWngeoQHUTdCJUP04spMBMS8LSker9cReVQkULFDIZDFPrhTzBl6sed9wcZQTbL+BDqMyaN3RJPh/anbx+Iv+qgQdAa3M9Z5JmvYlh4qop+Ho1F1W5gbOE9YKLgAnWytXElU4G8GtW47lhgFE6gaSs+gs37sFvi0PPVvA5dnCBgILTwoKd/+DoL9F6inlM7H4rOTzD79KJgKlZO/Zgt22UsKhrAaXU5ZcLrAglTVKJEmNJvORGN1vqrcfSMizfpsgbIe9zno+gBoKVXgIL/VI8dB1O5o/R3Suez/gD7M781ShjKpIIORM/nxG+jjhhgPwsn2IoXsPGPqYHXA63zJ07M2GPEykQwJBYLK808qYxuIew4frk52nhCsnCYmXiR6CuapvE1IwRB4/QftDbEn+AucIr1oxrLabRj9q4ae0+fXkHnteAJwXRbVkR0mctVSwEbqhJiMSZUp9DNbEDMmjX22m3ABpkrPQQTP3S1sib5pD2VRKRd+eNAjLYyT0hGrdjWJZy24OYXRoWQAIhGBZRxuBFMjjZQhpgrWo8SiFYbojcHO8V5DyscJpLTHyx9Fimassyo5U6WNtquUMYgccaHY5amgR3PQzq3ToNM5ABnoB9kuxsebqmYZm0R9qxJbFXCQ1UPyFIbxoUraTJFDpCk0Wk9GaYJKz/6oHwEP0Q14lMtlddQsOAU9zlYdMVHiT7RQP3XCmWYDcHCGbVRHGnHuwzScA0BaSBOGkz3lM8CArjrBsyEoV6Ys4qgDK3ykQQPZ3hCRGNXQTNNXbEb6tDiTDLKOyMzRhCFT+mAUmiYbV3YQVqFVp9dorv+TsLeCykS2b5yyu8AV7IS9cxcL8z4Kfwp+xJyYLv1OsxQCZwTB4a8BZ/5EdxTBJthApqyfd9u3ifr/WILTqq5VqgwMT9SOxbSGWLQJUUWCVi4k9tho9nEsbUh7U6NUsLmkYFXOhZ0kmamaJLRNJzSj/qn4Mso6zb6iLLBXoaZ6AqeWCjHQm2lztnejYYM2eubnpBdKVLORZhudH3JF1waBJKA9+W8EhMj3Kzf0L4vi4k6RoHh3Z5YgmSZmk6ns4fjScjAoL8GoOECgqgYEBYUGFVO4FUv4/YtowhEmTs0vrvlD/CrisnoBNDAcUi/teY7OctFlmARQzjOItrrlKuPO6E2Ox93L4O/4DcgV/dZ7qR3VBwVQxP1GCieA4RIpweYJ5FoYrHxqRBdJjnqbsikA2Ictbb8vE1GYIo9dacK0REgDX4smy6GAkxlH1yCGGsk+tgiDhNKuKu3yNrMdxafmKTF632F8Vx4BNK57GvlFisrkjN9WDAtjsWA0ENT2e2nETUb/n7qwhvGnrHuf5bX6Vh/n3xffU3PeHdR+FA92i6ufT3AlyAREoNDh6chiMWTvjKjHDeRhOa9YkOQRq1vQXEMppAQVwHCuIcV2g5rBn6GmZZpTR7vnSD6ZmhdSl176gqKTXu5E+YbfL0adwNtHP7dT7t7b46DVZIkzaRJOM+S6KcrzYVg+T3wSRFRQashjfU18NutrKa/7PXbtuJvpIjbgPeqd+pjmRw6YKpnANFSQcpzTZgpSNJ6J7uiagAbir/8tNXJ/OsOnRh6iuIexxrmkIneAgz8QoLmiaJ8sLQrELVK2yn3wOHp57BAZJhDZjTBzyoRAuuZ4eoxHruY1pSb7qq79cIeAdOwin4GdgMeIMHeG+FZWYaiUQQyC5b50zKjYw97dFjAeY2I4Bnl105Iku1y0lMA1ZHolLx19uZnRdILcXKlZGQx/GdEqSsMRU1BIrFqRcV1qQOOHyxOLXEGcbRtAEsuAC2V4K3p5mFJ22IDWaEkk9ttf5Izb2LkD1MnrSwztXmmD/Qi/EmVEFBfiKGmftsPwVaIoZanlKndMZsIBOskFYpDOq3QUs9aSbAAtL5Dbokus2G4/asthNMK5UQKCOhU97oaOYNGsTah+jfCKsZnTRn5TbhFX8ghg8CBYt/BjeYYYUrtUZ5jVij/op7V5SsbA4mYTOwZ46hqdpbB6Qvq3AS2HHNkC15pTDIcDNGsMPXaBidXYPHc6PJAkRh29Vx8KcgX46LoUQBhRM+3SW6Opll/wgxxsPgKJKzr5QCmwkUxNbeg6Wj34SUnEzOemSuvS2OetRCO8Tyy+QbSKVJcqkia+GvDefFwMOmgnD7h81TUtMn+mRpyJJ349HhAnoWFTejhpYTL9G8N2nVg1qkXBeoS9Nw2fB27t7trm7d/QK7Cr4uoCeOQ7/8JfKT77KiDzLImESHw/0wf73QeHu74hxv7uihi4fTX+XEwAyQG3264dwv17aJ5N335Vt9sdrAXhPOAv8JFvzqyYXwfx8WYJaef1gMl98JRFyl5Mv5Uo/oVH5ww5OzLFsiTPDns7fS6EURSSWd/92BxMYQ8sBaH+j+wthQPdVgDGpTfi+JQIWMD8xKqULliRH01rTeyF8x8q/GBEEEBrAJMPf25UQwi0b8tmqRXY7kIvNkzrkvRWLnxoGYEJsz8u4oOyMp8cHyaybb1HdMCaLApUE+/7xLIZGP6H9xuSEXp1zLIdjk5nBaMuV/yTDRRP8Y2ww5RO6d2D94o+6ucWIqUAvgHIHXhZsmDhjVLczmZ3ca0Cb3PpKwt2UtHVQ0BgFJsqqTsnzZPlKahRUkEu4qmkJt+kqdae76ViWe3STan69yaF9+fESD2lcQshLHWVu4ovItXxO69bqC5p1nZLvI8NdQB9s9UNaJGlQ5mG947ipdDA0eTIw/A1zEdjWquIsQXXGIVEH0thC5M+W9pZe7IhAVnPJkYCCXN5a32HjN6nsvokEqRS44tGIs7s2LVTvcrHAF+RVmI8L4HUYk4x+67AxSMJKqCg8zrGOgvK9kNMdDrNiUtSWuHFpC8/p5qIQrEo/H+1l/0cAwQ2nKmpWxKcMIuHY44Y6DlkpO48tRuUGBWT0FyHwSKO72Ud+tJUfdaZ4CWNijzZtlRa8+CkmO/EwHYfPZFU/hzjFWH7vnzHRMo+aF9u8qHSAiEkA2HjoNQPEwHsDKOt6hOoK3Ce/+/9boMWDa44I6FrQhdgS7OnNaSzwxWKZMcyHi6LN4WC6sSj0qm2PSOGBTvDs/GWJS6SwEN/ULwpb4LQo9fYjUfSXRwZkynUazlSpvX9e+G2zor8l+YaMxSEomDdLHGcD6YVQPegTaA74H8+V4WvJkFUrjMLGLlvSZQWvi8/QA7yzQ8GPno//5SJHRP/OqKObPCo81s/+6WgLqykYpGAgQZhVDEBPXWgU/WzFZjKUhSFInufPRiMAUULC6T11yL45ZrRoB4DzOyJShKXaAJIBS9wzLYIoCEcJKQW8GVCx4fihqJ6mshBUXSw3wWVj3grrHQlGNGhIDNNzsxQ3M+GWn6ASobIWC+LbYOC6UpahVO13Zs2zOzZC8z7FmA05JhUGyBsF4tsG0drcggIFzgg/kpf3+CnAXKiMgIE8Jk/Mhpkc8DUJEUzDSnWlQFme3d0sHZDrg7LavtsEX3cHwjCYA17pMTfx8Ajw9hHscN67hyo+RJQ4458RmPywXykkVcW688oVUrQhahpPRvTWPnuI0B+SkQu7dCyvLRyFYlC1LG1gRCIvn3rwQeINzZQC2KXq31FaR9UmVV2QeGVqBHjmE+VMd3b1fhCynD0pQNhCG6/WCDbKPyE7NRQzL3BzQAJ0g09aUzcQA6mUp9iZFK6Sbp/YbHjo++7/Wj8S4YNa+ZdqAw1hDrKWFXv9+zaXpf8ZTDSbiqsxnwN/CzK5tPkOr4tRh2kY3Bn9JtalbIOI4b3F7F1vPQMfoDcdxMS8CW9m/NCW/HILTUVWQIPiD0j1A6bo8vsv6P1hCESl2abrSJWDrq5sSzUpwoxaCU9FtJyYH4QFMxDBpkkBR6kn0LMPO+5EJ7Z6bCiRoPedRZ/P0SSdii7ZnPAtVwwHUidcdyspwncz5uq6vvm4IEDbJVLUFCn/LvIHfooUBTkFO130FC7CmmcrKdgDJcid9mvVzsDSibOoXtIf9k6ABle3PmIxejodc4aob0QKS432srrCMndbfD454q52V01G4q913mC5HOsTzWF4h2No1av1VbcUgWAqyoZl+11PoFYnNv2HwAODeNRkHj+8SF1fcvVBu6MrehHAZK1Gm69ICcTKizykHgGFx7QdowTVAsYEF2tVc0Z6wLryz2FI1sc5By2znJAAmINndoJiB4sfPdPrTC8RnkW7KRCwxC6YvXg5ahMlQuMpoCSXjOlBy0Kij+bsCYPbGp8BdCBiLmLSAkEQRaieWo1SYvZIKJGj9Ur/eWHjiB7SOVdqMAVmpBvfRiebsFjger7DC+8kRFGtNrTrnnGD2GAJb8rQCWkUPYHhwXsjNBSkE6lGWUj5QNhK0DMNM2l+kXRZ0KLZaGsFSIdQz/HXDxf3/TE30+DgBKWGWdxElyLccJfEpjsnszECNoDGZpdwdRgCixeg9L4EPhH+RptvRMVRaahu4cySjS3P5wxAUCPkmn+rhyASpmiTaiDeggaIxYBmtLZDDhiWIJaBgzfCsAGUF1Q1SFZYyXDt9skCaxJsxK2Ms65dmdp5WAZyxik/zbrTQk5KmgxCg/f45L0jywebOWUYFJQAJia7XzCV0x89rpp/f3AVWhSPyTanqmik2SkD8A3Ml4NhIGLAjBXtPShwKYfi2eXtrDuKLk4QlSyTw1ftXgwqA2jUuopDl+5tfUWZNwBpEPXghzbBggYCw/dhy0ntds2yeHCDKkF/YxQjNIL/F/37jLPHCKBO9ibwYCmuxImIo0ijV2Wbg3kSN2psoe8IsABv3RNFaF9uMyCtCYtqcD+qNOhwMlfARQUdJ2tUX+MNJqOwIciWalZsmEjt07tfa8ma4cji9sqz+Q9hWfmMoKEbIHPOQORbhQRHIsrTYlnVTNvcq1imqmmPDdVDkJgRcTgB8Sb6epCQVmFZe+jGDiNJQLWnfx+drTKYjm0G8yH0ZAGMWzEJhUEQ4Maimgf/bkvo8PLVBsZl152y5S8+HRDfZIMCbYZ1WDp4yrdchOJw8k6R+/2pHmydK4NIK2PHdFPHtoLmHxRDwLFb7eB+M4zNZcB9NrAgjVyzLM7xyYSY13ykWfIEEd2n5/iYp3ZdrCf7fL+en+sIJu2W7E30MrAgZBD1rAAbZHPgeAMtKCg3NpSpYQUDWJu9bT3V7tOKv+NRiJc8JAKqqgCA/PNRBR7ChpiEulyQApMK1AyqcWnpSOmYh6yLiWkGJ2mklCSPIqN7UypWj3dGi5MvsHQ87MrB4VFgypJaFriaHivwcHIpmyi5LhNqtem4q0n8awM19Qk8BOS0EsqGscuuydYsIGsbT5GHnERUiMpKJl4ON7qjB4fEqlGN/hCky89232UQCiaeWpDYCJINXjT6xl4Gc7DxRCtgV0i1ma4RgWLsNtnEBRQFqZggCLiuyEydmFd7WlogpkCw5G1x4ft2psm3KAREwVwr1Gzl6RT7FDAqpVal34ewVm3VH4qn5mjGj+bYL1NgfLNeXDwtmYSpwzbruDKpTjOdgiIHDVQSb5/zBgSMbHLkxWWgghIh9QTFSDILixVwg0Eg1puooBiHAt7DzwJ7m8i8/i+jHvKf0QDnnHVkVTIqMvIQImOrzCJwhSR7qYB5gSwL6aWL9hERHCZc4G2+JrpgHNB8eCCmcIWIQ6rSdyPCyftXkDlErUkHafHRlkOIjxGbAktz75bnh50dU7YHk+Mz7wwstg6RFZb+TZuSOx1qqP5C66c0mptQmzIC2dlpte7vZrauAMm/7RfBYkGtXWGiaWTtwvAQiq2oD4YixPLXE2khB2FRaNRDTk+9sZ6K74Ia9VntCpN4BhJGJMT4Z5c5FhSepRCRWmBXqx+whVZC4me4saDs2iNqXMuCl6iAZflH8fscC1sTsy4PHeC+XYuqMBMUun5YezKbRKmEPwuK+CLzijPEQgfhahQswBBLfg/GBgBiI4QwAqzJkkyYAWtjzSg2ILgMAgqxYfwERRo3zruBL9WOryUArSD8sQOcD7fvIODJxKFS615KFPsb68USBEPPj1orNzFY2xoTtNBVTyzBhPbhFH0PI5AtlJBl2aSgNPYzxYLw7XTDBDinmVoENwiGzmngrMo8OmnRP0Z0i0Zrln9DDFcnmOoBZjABaQIbPOJYZGqX+RCMlDDbElcjaROLDoualmUIQ88Kekk3iM4OQrADcxi3rJguS4MOIBIgKgXrjd1WkbCdqxJk/4efRIFsavZA7KvvJQqp3Iid5Z0NFc5aiMRzGN3vrpBzaMy4JYde3wr96PjN90AYOIbyp6T4zj8LoE66OGcX1Ef4Z3KoWLAUF4BTg7ug/AbkG5UNQXAMkQezujSHeir2uTThgd3gpyzDrbnEdDRH2W7U6PeRvBX1ZFMP5RM+Zu6UUZZD8hDPHldVWntTCNk7To8IeOW9yn2wx0gmurwqC60AOde4r3ETi5pVMSDK8wxhoGAoEX9NLWHIR33VbrbMveii2jAJlrxwytTHbWNu8Y4N8vCCyZjAX/pcsfwXbLze2+D+u33OGBoJyAAL3jn3RuEcdp5If8O+a4NKWvxOTyDltG0IWoHhwVGe7dKkCWFT++tm+haBCikRUUMrMhYKZJKYoVuv/bsJzO8DwfVIInQq3g3BYypiz8baogH3r3GwqCwFtZnz4xMjAVOYnyOi5HWbFA8n0qz1OjSpHWFzpQOpvkNETZBGpxN8ybhtqV/DMUxd9uFZmBfKXMCn/SqkWJyKPnT6lq+4zBZni6fYRByJn6OK+OgPBGRAJluwGSk4wxjOOzyce/PKODwRlsgrVkdcsEiYrqYdXo0Er2GXi2GQZd0tNJT6c9pK1EEJG1zgDJBoTVuCXGAU8BKTvCO/cEQ1Wjk3Zzuy90JX4m3O5IlxVFhYkSUwuQB2up7jhvkm+bddRQu5F9s0XftGEJ9JSuSk+ZachCbdU45fEqbugzTIUokwoAKvpUQF/CvLbWW5BNQFqFkJg2f30E/48StNe5QwBg8zz3YAJ82FZoXBxXSv4QDooDo79NixyglO9AembuBcx5Re3CwOKTHebOPhkmFC7wNaWtoBhFuV4AkEuJ0J+1pT0tLkvFVZaNzfhs/Kd3+A9YsImlO4XK4vpCo/elHQi/9gkFg07xxnuXLt21unCIpDV+bbRxb7FC6nWYTsMFF8+1LUg4JFjVt3vqbuhHmDKbgQ4e+RGizRiO8ky05LQGMdL2IKLSNar0kNG7lHJMaXr5mLdG3nykgj6vB/KVijd1ARWkFEf3yiUw1v/WaQivVUpIDdSNrrKbjO5NPnxz6qTTGgYg03HgPhDrCFyYZTi3XQw3HXCva39mpLNFtz8AiEhxAJHpWX13gCTAwgm9YTvMeiqetdNQv6IU0hH0G+ZManTqDLPjyrOse7WiiwOJCG+J0pZYULhN8NILulmYYvmVcV2MjAfA39sGKqGdjpiPo86fecg65UPyXDIAOyOkCx5NQsLeD4gGVjTVDwOHWkbbBW0GeNjDkcSOn2Nq4cEssP54t9D749A7M1AIOBl0Fi0sSO5v3P7LCBrM6ZwFY6kp2FX6AcbGUdybnfChHPyu6WlRZ2Fwv9YM0RMI7kISRgR8HpQSJJOyTfXj/6gQKuihPtiUtlCQVPohUgzfezTg8o1b3n9pNZeco1QucaoXe40Fa5JYhqdTspFmxGtW9h5ezLFZs3j/N46f+S2rjYNC2JySXrnSAFhvAkz9a5L3pza8eYKHNoPrvBRESpxYPJdKVUxBE39nJ1chrAFpy4MMkf0qKgYALctGg1DQI1kIymyeS2AJNT4X240d3IFQb/0jQbaHJ2YRK8A+ls6WMhWmpCXYG5jqapGs5/eOJErxi2/2KWVHiPellTgh/fNl/2KYPKb7DUcAg+mCOPQFCiU9Mq/WLcU1xxC8aLePFZZlE+PCLzf7ey46INWRw2kcXySR9FDgByXzfxiNKwDFbUSMMhALPFSedyjEVM5442GZ4hTrsAEvZxIieSHGSgkwFh/nFNdrrFD4tBH4Il7fW6ur4J8Xaz7RW9jgtuPEXQsYk7gcMs2neu3zJwTyUerHKSh1iTBkj2YJh1SSOZL5pLuQbFFAvyO4k1Hxg2h99MTC6cTUkbONQIAnEfGsGkNFWRbuRyyaEZInM5pij73EA9rPIUfU4XoqQpHT9THZkW+oKFLvpyvTBMM69tN1Ydwv1LIEhHsC+ueVG+w+kyCPsvV3erRikcscHjZCkccx6VrBkBRusTDDd8847GA7p2Ucy0y0HdSRN6YIBciYa4vuXcAZbQAuSEmzw+H/AuOx+aH+tBL88H57D0MsqyiZxhOEQkF/8DR1d2hSPMj/sNOa5rxcUnBgH8ictv2J+cb4BA4v3MCShdZ2vtK30vAwkobnEWh7rsSyhmos3WC93Gn9C4nnAd/PjMMtQfyDNZsOPd6XcAsnBE/mRHtHEyJMzJfZFLE9OvQa0i9kUmToJ0ZxknTgdl/XPV8xoh0K7wNHHsnBdvFH3sv52lU7UFteseLG/VanIvcwycVA7+BE1Ulyb20BvwUWZcMTKhaCcmY3ROpvonVMV4N7yBXTL7IDtHzQ4CCcqF66LjF3xUqgErKzolLyCG6Kb7irP/MVTCCwGRxfrPGpMMGvPLgJ881PHMNMIO09T5ig7AzZTX/5PLlwnJLDAPfuHynSGhV4tPqR3gJ4kg4c06c/F1AcjGytKm2Yb5jwMotF7vro4YDLWlnMIpmPg36NgAZsGA0W1spfLSue4xxat0Gdwd0lqDBOgIaMANykwwDKejt5YaNtJYIkrSgu0KjIg0pznY0SCd1qlC6R19g97UrWDoYJGlrvCE05J/5wkjpkre727p5PTRX5FGrSBIfJqhJE/IS876PaHFkx9pGTH3oaY3jJRvLX9Iy3Edoar7cFvJqyUlOhAEiOSAyYgVEGkzHdug+oRHIEOXAExMiTSKU9A6nmRC8mp8iYhwWdP2U/5EkFAdPrZw03YA3gSyNUtMZeh7dDCu8pF5x0VORCTgKp07ehy7NZqKTpIC4UJJ89lnboyAfy5OyXzXtuDRbtAFjZRSyGFTpFrXwkpjSLIQIG3N0Vj4BtzK3wdlkBJrO18MNsgseR4BysJilI0wI6ZahLhBFA0XBmV8d4LUzEcNVb0xbLjLTETYN8OEVqNxkt10W614dd1FlFFVTIgB7/BQQp1sWlNolpIu4ekxUTBV7NmxOFKEBmmN+nA7pvF78/RII5ZHA09OAiE/66MF6HQ+qVEJCHxwymukkNvzqHEh52dULPbVasfQMgTDyBZzx4007YiKdBuUauQOt27Gmy8ISclPmEUCIcuLbkb1mzQSqIa3iE0PJh7UMYQbkpe+hXjTJKdldyt2mVPwywoODGJtBV1lJTgMsuSQBlDMwhEKIfrvsxGQjHPCEfNfMAY2oxvyKcKPUbQySkKG6tj9AQyEW3Q5rpaDJ5Sns9ScLKeizPRbvWYAw4bXkrZdmB7CQopCH8NAmqbuciZChHN8lVGaDbCnmddnqO1PQ4ieMYfcSiBE5zzMz+JV/4eyzrzTEShvqSGzgWimkNxLvUj86iAwcZuIkqdB0VaIB7wncLRmzHkiUQpPBIXbDDLHBlq7vp9xwuC9AiNkIptAYlG7Biyuk8ILdynuUM1cHWJgeB+K3wBP/ineogxkvBNNQ4AkW0hvpBOQGFfeptF2YTR75MexYDUy7Q/9uocGsx41O4IZhViw/2FvAEuGO5g2kyXBUijAggWM08bRhXg5ijgMwDJy40QeY/cQpUDZiIzmvskQpO5G1zyGZA8WByjIQU4jRoFJt56behxtHUUE/om7Rj2psYXGmq3llVOCgGYKNMo4pzwntITtapDqjvQtqpjaJwjHmDzSVGLxMt12gEXAdLi/caHSM3FPRGRf7dB7YC+cD2ho6oL2zGDCkjlf/DFoQVl8GS/56wur3rdV6ggtzZW60MRB3g+U1W8o8cvqIpMkctiGVMzXUFI7FacFLrgtdz4mTEr4aRAaQ2AFQaNeG7GX0yOJgMRYFziXdJf24kg/gBQIZMG/YcPEllRTVNoDYR6oSJ8wQNLuihfw81UpiKPm714bZX1KYjcXJdfclCUOOpvTxr9AAJevTY4HK/G7F3mUc3GOAKqh60zM0v34v+ELyhJZqhkaMA8UMMOU90f8RKEJFj7EqepBVwsRiLbwMo1J2zrE2UYJnsgIAscDmjPjnzI8a719Wxp757wqmSJBjXowhc46QN4RwKIxqEE6E5218OeK7RfcpGjWG1jD7qND+/GTk6M56Ig4yMsU6LUW1EWE+fIYycVV1thldSlbP6ltdC01y3KUfkobkt2q01YYMmxpKRvh1Z48uNKzP/IoRIZ/F6buOymSnW8gICitpJjKWBscSb9JJKaWkvEkqinAJ2kowKoqkqZftRqfRQlLtKoqvTRDi2vg/RrPD/d3a09J8JhGZlEkOM6znTsoMCsuvTmywxTCDhw5dd0GJOHCMPbsj3QLkTE3MInsZsimDQ3HkvthT7U9VA4s6G07sID0FW4SHJmRGwCl+Mu4xf0ezqeXD2PtPDnwMPo86sbwDV+9PWcgFcARUVYm3hrFQrHcgMElFGbSM2A1zUYA3baWfheJp2AINmTJLuoyYD/OwA4a6V0ChBN97E8YtDBerUECv0u0TlxR5yhJCXvJxgyM73Bb6pyq0jTFJDZ4p1Am1SA6sh8nADd1hAcGBMfq4d/UfwnmBqe0Jun1n1LzrgKuZMAnxA3NtCN7Klf4BH+14B7ibBmgt0TGUafVzI4uKlpF7v8NmgNjg90D6QE3tbx8AjSAC+OA1YJvclyPKgT27QpIEgVYpbPYGBsnyCNrGz9XUsCHkW1QAHgL2STZk12QGqmvAB0NFteERkvBIH7INDsNW9KKaAYyDMdBEMzJiWaJHZALqDxQDWRntumSDPcplyFiI1oDpT8wbwe01AHhW6+vAUUBoGhY3CT2tgwehdPqU/4Q7ZLYvhRl/ogOvR9O2+wkkPKW5vCTjD2fHRYXONCoIl4Jh1bZY0ZE1O94mMGn/dFSWBWzQ/VYk+Gezi46RgiDv3EshoTmMSlioUK6MQEN8qeyK6FRninyX8ZPeUWjjbMJChn0n/yJvrq5bh5UcCAcBYSafTFg7p0jDgrXo2QWLb3WpSOET/Hh4oSadBTvyDo10IufLzxiMLAnbZ1vcUmj3w7BQuIXjEZXifwukVxrGa9j+DXfpi12m1RbzYLg9J2wFergEwOxFyD0/JstNK06ZN2XdZSGWxcJODpQHOq4iKqjqkJUmPu1VczL5xTGUfCgLEYyNBCCbMBFT/cUP6pE/mujnHsSDeWxMbhrNilS5MyYR0nJyzanWXBeVcEQrRIhQeJA6Xt4f2eQESNeLwmC10WJVHqwx8SSyrtAAjpGjidcj1E2FYN0LObUcFQhafUKTiGmHWRHGsFCB+HEXgrzJEB5bp0QiF8ZHh11nFX8AboTD0PS4O1LqF8XBks2MpjsQnwKHF6HgaKCVLJtcr0XjqFMRGfKv8tmmykhLRzu+vqQ02+KpJBjaLt9ye1Ab+BbEBhy4EVdIJDrL2naV0o4wU8YZ2Lq04FG1mWCKC+UwkXOoAjneU/xHplMQo2cXUlrVNqJYczgYlaOEczVCs/OCgkyvLmTmdaBJc1iBLuKwmr6qtRnhowngsDxhzKFAi02tf8bmET8BO27ovJKF1plJwm3b0JpMh38+xsrXXg7U74QUM8ZCIMOpXujHntKdaRtsgyEZl5MClMVMMMZkZLNxH9+b8fH6+b8Lev30A9TuEVj9CqAdmwAAHBPbfOBFEATAPZ2CS0OH1Pj/0Q7PFUcC8hDrxESWdfgFRm+7vvWbkEppHB4T/1ApWnlTIqQwjcPl0VgS1yHSmD0OdsCVST8CQVwuiew1Y+g3QGFjNMzwRB2DSsAk26cmA8lp2wIU4p93AUBiUHFGOxOajAqD7Gm6NezNDjYzwLOaSXRBYcWipTSONHjUDXCY4mMI8XoVCR/Rrs/JLKXgEx+qkmeDlFOD1/yTQNDClRuiUyKYCllfMiQiyFkmuTz2vLsBNyRW+xz+5FElFxWB28VjYIGZ0Yd+5wIjkcoMaggxswbT0pCmckRAErbRlIlcOGdBo4djTNO8FAgQ+lT6vPS60BwTRSUAM3ddkEAZiwtEyArrkiDRnS7LJ+2hwbzd2YDQagSgACpsovmjil5wfPuXq3GuH0CyE7FK3M4FgRaFoIkaodORrPx1+JpI9psyNYIFuJogZa0/1AhOWdlHQxdAgbwacsHqPZo8u/ngAH2GmaTdhYnBfSDbBfh8CHq6Bx5bttP2+RdM+MAaYaZ0Y/ADkbNCZuAyAVQa2OcXOeICmDn9Q/eFkDeFQg5MgHEDXq/tVjj+jtd26nhaaolWxs1ixSUgOBwrDhRIGOLyOVk2/Bc0UxvseQCO2pQ2i+Krfhu/WeBovNb5dJxQtJRUDv2mCwYVpNl2efQM9xQHnK0JwLYt/U0Wf+phiA4uw8G91slC832pmOTCAoZXohg1fewCZqLBhkOUBofBWpMPsqg7XEXgPfAlDo2U5WXjtFdS87PIqClCK5nW6adCeXPkUiTGx0emOIDQqw1yFYGHEVx20xKjJVYe0O8iLmnQr3FA9nSIQilUKtJ4ZAdcTm7+ExseJauyqo30hs+1qSW211A1SFAOUgDlCGq7eTIcMAeyZkV1SQJ4j/e1Smbq4HcjqgFbLAGLyKxlMDMgZavK5NAYH19Olz3la/QCTiVelFnU6O/GCvykqS/wZJDhKN9gBtSOp/1SP5VRgJcoVj+kmf2wBgv4gjrgARBWiURYx8xENV3bEVUAAWWD3dYDKAIWk5opaCFCMR5ZjJExiCAw7gYiSZ2rkyTce4eNMY3lfGn+8p6+vBckGlKEXnA6Eota69OxDO9oOsJoy28BXOR0UoXNRaJD5ceKdlWMJlOFzDdZNpc05tkMGQtqeNF2lttZqNco1VtwXgRstLSQ6tSPChgqtGV5h2DcDReIQadaNRR6AsAYKL5gSFsCJMgfsaZ7DpKh8mg8Wz8V7H+gDnLuMxaWEIUPevIbClgap4dqmVWSrPgVYCzAoZHIa5z2Ocx1D/GvDOEqMOKLrMefWIbSWHZ6jbgA8qVBhYNHpx0P+jAgN5TB3haSifDcApp6yymEi6Ij/GsEpDYUgcHATJUYDUAmC1SCkJ4cuZXSAP2DEpQsGUjQmKJfJOvlC2x/pChkOyLW7KEoMYc5FDC4v2FGqSoRWiLsbPCiyg1U5yiHZVm1XLkHMMZL11/yxyw0UnGig3MFdZklN5FI/qiT65T+jOXOdO7XbgWurOAZR6Cv9uu1cm5LjkXX4xi6mWn5r5NjBS0gTliHhMZI2WNqSiSphEtiCAwnafS11JhseDGHYQ5+bqWiAYiAv6Jsf79/VUs4cIl+n6+WOjcgB/2l5TreoAV2717JzZbQIR0W1cl/dEqCy5kJ3ZSIHuU0vBoHooEpiHeQWVkkkOqRX27eD1FWw4BfO9CJDdKoSogQi3hAAwsPRFrN5RbX7bqLdBJ9JYMohWrgJKHSjVl1sy2xAG0E3sNyO0oCbSGOxCNBRRXTXenYKuwAoDLfnDcQaCwehUOIDiHAu5m5hMpKeKM4sIo3vxACakIxKoH2YWF2QM84e6F5C5hJU4g8uxuFOlAYnqtwxmHyNEawLW/PhoawJDrGAP0JYWHgAVUByo/bGdiv2T2EMg8gsS14/rAdzlOYazFE7w4OzxeKiWdm3nSOnQRRKXSlVo8HEAbBfyJMKqoq+SCcTSx5NDtbFwNlh8VhjGGDu7JG5/TAGAvniQSSUog0pNzTim8Owc6QTuSKSTXlQqwV3eiEnklS3LeSXYPXGK2VgeZBqNcHG6tZHvA3vTINhV0ELuQdp3t1y9+ogD8Kk/W7QoRN1UWPqM4+xdygkFDPLoTaumKReKiLWoPHOfY54m3qPx4c+4pgY3MRKKbljG8w4wvz8pxk3AqKsy4GMAkAtmRjRMsCxbb4Q2Ds0Ia9ci8cMT6DmsJG00XaHCIS+o3F8YVVeikw13w+OEDaCYYhC0ZE54kA4jpjruBr5STWeqQG6M74HHL6TZ3lXrd99ZX++7LhNatQaZosuxEf5yRA15S9gPeHskBIq3Gcw81AGb9/O53DYi/5CsQ51EmEh8Rkg4vOciClpy4d04eYsfr6fyQkBmtD+P8sNh6e+XYHJXT/lkXxT4KXU5F2sGxYyzfniMMQkb9OjDN2C8tRRgTyL7GwozH14PrEUZc6oz05Emne3Ts5EG7WolDmU8OB1LDG3VrpQxp+pT0KYV5dGtknU64JhabdqcVQbGZiAxQAnvN1u70y1AnmvOSPgLI6uB4AuDGhmAu3ATkJSw7OtS/2ToPjqkaq62/7WFG8advGlRRqxB9diP07JrXowKR9tpRa+jGJ91zxNTT1h8I2PcSfoUPtd7NejVoH03EUcqSBuFZPkMZhegHyo2ZAITovmm3zAIdGFWxoNNORiMRShgwdYwFzkPw5PA4a5MIIQpmq+nsp3YMuXt/GkXxLx/P6+ZJS0lFyz4MunC3eWSGE8xlCQrKvhKUPXr0hjpAN9ZK4PfEDrPMfMbGNWcHDzjA7ngMxTPnT7GMHar+gMQQ3NwHCv4zH4BIMYvzsdiERi6gebRmerTsVwZJTRsL8dkZgxgRxmpbgRcud+YlCIRpPwHShlUSwuipZnx9QCsEWziVazdDeKSYU5CF7UVPAhLer3CgJOQXl/zh575R5rsrmRnKAzq4POFdgbYBuEviM4+LVC15ssLNFghbTtHWerS1hDt5s4qkLUha/qpZXhWh1C6lTQAqCNQnaDjS7UGFBC6wTu8yFnKJnExCnAs3Ok9yj5KpfZESQ4lTy5pTGTnkAUpxI+yjEldJfSo4y0QhG4i4IwkRFGcjWY8+EzgYYJUK7BXQksLxAww/YYWBMhJILB9e8ePEJ4OP7z+4/wOQDl64iOYDp26DaONPxpKtBxq/aTzRGarm3VkPYTLJKx6Z/Mw2YbBGseJhPMwhhNswrIkyvV2BYzrvZbxLpKwcWJhYmFtVZ+lPEq91FzVp1HlQY1bZVLqeNR9SAUn6n0E28k/UuGkNpP1DBI5ch/EehZfjUQ9aE41NhETExoPT2gGQz0IhWJbEOvTQ4wgcXCHHFBhewYUiFHuhRSAUVmEHeCRQHQkXGFwkAgyzREJCVN7TRnTon36Zw3tPhx4EALwNdwDv+J41YSP4B2CQqz0EFgARZ4ESgBHQgROwAVn9GTI+HYexTUevLUeta4/DqKrbMVS+Yqb8hUwYCrlgKtmAq1YCrFgKrd4qpXiqZcKn1oqdWipjYKpWwVPVYqW6xUpVipKqFR3QKjagVEtAqHpxUMTitsnFaJOKx2cVhswq35RVpyiq9lFVNIKnOQVMkgqtYxVNxiqQjFS7GKlSIVIsQqPIhUWwioigFQ++KkN8VHr49HDw9Ebo9EDo9DTo9Crg9BDg9/Wx7gWx7YWwlobYrOGxWPNisAaAHEyALpkAVDIAeWAArsABVXACYuAD5cAF6wAKFQAQqgAbVAAsoAAlQAUaYAfkwAvogBWQACOgAD9AAHSAAKT4GUdMiOvFngBTwCn2AZ7Dv6B6k/90B8+yRnkV144AIBoAMTQATGgAjNAA4YABgwABZgB/mQCwyAVlwCguASlwCEuAQFwB4uAMlwBYuAJlQAUVAAhUD2KgdpUDaJgaRMDFJgX5MC1JgWJEAokQCWRAHxEAWkQBMRADpEAMkQAYROAEecC484DRpwBDTnwNOdw05tjTmiNOYwtswhYFwLA7BYG4LA2BYGOLAwRYFuLAsxYFQJAohIEyJAMwkAwiQC0JAJgkAeiQBkJAFokAPCQA0JABwcD4Dgc4cDdDgaYcDIDgYgUC6CgWgUClCgUYUAVBQBOFAEYMALgwAgDA9QYAdIn8AZzeBB2L5EcWrenUT1KXienEsuJJ7x5U8XlTjc1NVzUyXFTGb1LlpUtWlTDIjqwE4LsagowoCi2gJLKAkpoBgJQNpAIhNqaEoneI6kiiqQ6Go/n6j0cS+a2gEU8gIHJ+BwfgZX4GL+Bd/gW34FZ+BS/gUH4FN6BTegTvoEv6BJegRnYEF2A79gOvYDl2BdEjCkqkGtwXp0LNToIskOTXzh/F062yJ7AAAAEDAWAAABWhJ+KPEIJgBFxMVP7w2QJBGHASQnOBKXKFIdUK4igKA9IEaYJg\"},function(e,t){e.exports=\"data:application/octet-stream;base64,d09GMgABAAAAAEZsAA8AAAAAsVwAAEYJAAECTQAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGiAGYACMcggEEQgKgqkkgeVlATYCJAOGdAuEMAAEIAWHIgeVUT93ZWJmBhtljDXsmI+A80Cgwj/+vggK2vaIIBusdPb/n5SghozBk8fY3CwzKw8ycQ3LRhauWU8b7AQmPrHpsWLSbaQ1gVqO5kgksapZihmcvXvsSAlqZIYL1YkM/LIl97nZp395IqcEA/f21yuNQLmMXb2rZZ/7e/rS+3aQoE5jiykOu275k8k/fj/okKRo8gD/nl/nJmkfxsrIHdGdBcGkiz+6PvzlXksg+3a0LRtj240x7fSAEokyS6Dhebf1LCdu5KvgAAco8DNFd2ngQgUXgqAmqf8L6c5UtGxo2DBNGtLY2tKGZOVZ2HLx77Kss250ad5d3Xl1cpW0vK77me4TVlhzag6hop7lZ01uGarTmUiBV5Wpw9QIIHIy9D5pVGBWN7jNUiixqMnPGuD/K6BvNvMnY8XIQrCP5gbrNOe31s653X+Hg4vjv5quVAldYVtRZDwzd3E4LI6F7nJUSRahOOESHI4wPkW4P/kqRajnl6aVI8/6NyeN7N39hlMJDAtvY/vKt+1fizcmIyrRKym9s6DQKzRhAbBBNrZjjOd5sdmjhmYoYhlG6ebk/+m0JDt7IFlBwzF2UC10R/j/jOHAsRXNIvuwldsBQ8JmLSBXgveuAprUmc51S9awSwjjI63tDuSs1ipLhjzb/AQgKNHf69T31/9a/mDZqwzltVuXJepZBVSKrHslr8mKJIitEKBze2/v7RmcF/KIgxjVu+92dCJw4Jw0YMjq36mKz6R9bwxg47PdFPonbhRl3D4K5EceNXMAevNfTvMKklBL06Z2bVXeC8m+e3q93PLu8/+fGfh/+IyHIjNgbA2SHAOWVyPUkL1eGEArjSwHY7nJa2+pjUFPG3AVbnW1p9R685Z6Sin13M6lHveY2zHHfeHh/0893n+ttoB4vlLGxGDBSolgp3GDFaWCVXMvvyv4a9J2xzF4bBrd3+dqEmwFlkVs7FxuRIzIw8a2r1aGseb/0Gpnm3taZOWJCHo3jwsUNf/fIQR4bcI1b8JbBxy9v3Xv+ya3rzHagkgQQmtB4uwIcXLqzlKQxA2jt7AWjyhcZ2j0EBTIN4ns0op5jz2GSLVa81VQaOnQJDgQUmfTBcQYgHrCZ82tyU46i+AAMXWsJNyFr6Shnj5S/V3l+hSXDqasIp/0Zje8lwv1S69efyeYquu9M5MrRS+8xF6JWVU1XahOQhcu3sqLpdI438Urzs2POI/5LHyJe018jEGKEeV1YXzQYYiSf+yO1d7LhdWdJQAKf2xLR6JQ7SwXTnUU5tzUa/5j7zhtWEDa02T/F8yYP3/x/NrzoudZ0ybP/nvq9pT4s8fPDj/bUNworhRHil22v8/G5K/kT+SP5Lfk1+SX5AZyLbmSXExGyQg5lywmp5N55DhyrPu0+zP3H9yfuD9wv+8+6n7b/br7FXPo5P8Fi54S0BCi00THCKR68zH6oT8SXFU1FnE9rdl00XrUkg6GJlqQbmqiJeltTbQifbyJ1nRr3kQbundooi09/22iHb1CE+3p9Tc28fSugyY60rvJcXQiC9YxOpMVrOvQlaypdTv0IktfoS9KZNZjMJZssvUcMB2yxSdeAxZCtvk4VkO21XpnsAayvawPBlsgO8r6ZOwK2VnWF2J/yIN1HQ6HvKl1O5xAnip9AQZ5iXwMLqmsJ0M+E1xnPRvyOeBW68WQrwG3W2+GfGfwoPVekB8MnrY+ivxkvAo5rc/H++QX7tjF+JQKKkV8QaUOj+MbKk2tW+NbKm1P3A7fUel6HD9Q6W7dGz9SKVmPwW9UJlvPAVUqi5U1EMBT2QxNQgv+7AShpfBbsxMKrYTfb1lEaK0Y1Xvs0Sx9MTxmjSYCNmikGIYnj4F/B8qlVSNWqAjeEa28H6GlRftEfyJUwaXeqdAGokFEOYP/ZUK5OqkHBhXEJQ8CT5zBINLQBBPxgofYRhJ1im4gFjc/JVIDRzQihLhmqWfHwUbquoEgDmE9gpEts9VRl+G9eStCvSzE+NAyw8sT1oU1opWH8JmEjHhuoQUVzqoEZiohobPm62zifEdYUfgg3oNVcJTkCsVFdSDCQJ4Bj6blLfCABB9Eby42WVr2gi0mYT5mEj+bAKuTTo9OnKIJXdRPL147XNoOwkrKDc9CBsdFc0pyGQSqkBkBoMSa9cYPFCfyhWcSL+Pj0UIXJZ+hHm8gH0P16rpulTeL3DoFfPV5g0t0sib3JKfYc698ufV3UIj5xFxpXb4kWhJAKwHNDLa21YA5MHhdu3K4rSW+yNUr9gdSVaxFbYcrFtywqqM7d6B1rMA5L0m8BdQ3yDfVprlR/mx1XKZ50A5XixBOKes4idywdlnuKnW0bQKUobG/6eKp4gS6bSgJZgbKRb3y/0c4sgyiaiNJrL1SjswX+XoMI3G437ffAQYJhClZoNckiwvh0JuGY18lv20teyEwLWALO+HlhazxFGh5VvXkwV1IdiEJzx90HGG9XEvvxRAeBqVbzDF7GgMi52ogNkDsljNUMCWlE78P6c6YIsfUmcZaSYZH5AabU5P3jYIusxHEzqNwB4HG06xTxjFl6fvZk8TYm535DFnBHv92uzgaCGSxXLFCoRdsoVP7/lIpBtIT04bn+a+WroALewJJitOG9NIlnZSvPvsw0I7aprNc8CeUY2e9MiU0oFGORKEKMM2SM0KyIslNjtWOJoDbimhJFcfC2qfSUmcQt01FpKGpobaaDUm9zigHqd7VNVWWRF0MffIdmQdi7Tgkl4fsOKg+8+FYIAGyB2iVImwetc6A4mocnS4liNuAGEhIxy0LSZqm3bgjMZIdQwE09d5Z3gE3hO3urhLtWd2WoVYMbwgaPlDKXaE2v7cHmPaZTzT/N2YaDb1+ABgeQUpkWUbVwoDKLpbeb/XD/nkpCcY4bMYLtjIyjmWKnB+m0jFIG6FbAXSJsEAhyIUMMlyAQLgINQbE2ZPKJVrX7vzba96SCAZh9Z2u3ED6LmBuqDPKT0aMohBSKPOFpbb3/71aAWtMawVGIO1IV2pZHw1JpOo11+cqE/E22s5ltVNiay6kvDVGLBfsLpUCTjDf1JmSuYB8lIZWpoB8fH4FTvSHKAkgNLed7NpdLOwaSnB8fvl4ZdPJQajUHKGvNYiIL7vau1Ok/QTk9JTQdvLX3Hk/m/myJ192fHLqhMtY3Ab47kjpUcoFsLUVBcSTQkA9C91YrN/6rEITGDnLNLOYq8NUqdhCiUKpY6CtwRirSJFQo84rgvKJgV+Tk9VZSNkjrCSqy8pgoOxG+KPxQjvjtcIr2xGUhUJQUrA0zLwgdAStOnQI9SJaE0W6Sl4hWMLHk+CscTRfZFRXKDXk3IAEp+X/5B+42kmxlFXFh9JBzXr+QFU2/24uV0dY/cDBBehI7FJLwBbbGiYIJ3N3TbFqisqOmIuxPJ+UsZgzpimAlp1gI0ZAEgwYDEYg1KLgCP7Ydo1vzWIkeAwH7yuy4Lx1+ya0fYl8ylgYJlvZqpA4RostuUUmLz6KLxfRR8UuYep6XoreL4PU/n0pnBGyE5LzJ5N4qZEkTz08AcfCepmkb+Sn4UE5TR/YnSYd8n7uoZm5MxlytQUzZ5+cpie/ONKjXLAttk1EesjoEZj4a7rNNYb5sbRBCt3C/apHOankfDEt2CEgxzg3+xBbnH/0pCxtUu51fKY1N64KHD1Y/pGkLJhhSqfZGxabuF50tE6bNNPYXGYQ0IRdQXobSF4CN7eqRpXoHP6VmYQmayIbTFU+few+53JC5Vgo24Kq64ICVJolv6sLSqoIv4StZGhLxB+U87ZQk7JLwR5URmFBhzNISIZDW3I7YZvAtmQCt5kXhxqVNTTIzAyJl2xMhGsDakcPGnuh7DifaH7kjwcNZlJAA9Ds/B45d+BCqKTg0DDrC3pT9fSw4v8nl6AUAmE3A4JA3UBOm7GK3ca5bJFiGGozD2hOBBPuslj2i0Yvye1lonOj2Sf6ikRzUavxPP5rXtPtHfLXvLL9iFpBU0+oaRdkulNK43gcTjREvbPAS9MhtLnU+Qkh2at2iaxoQWDbRZa3WBCQlQACvMotDaJQDe3EOp+C29GkG39D6jrCwlfNelO9c8RkTww6CBC2X7+r1Mtgijp0wWHOt9CRCx6lhrLN2LP6ohaBrg28SVnwBDTHDCMgEJD4KtIczSs8A+pxAG6wb9QAuHUKVQgEzGN3d4/zeCRktbPwG8a/Dp19z4H71sE5NMz9mu38AzlwrCpUOvolRxVR5oVeYZ+LFYcQ5APdyyeo52WDHvRi9qgEFBSKbC3V3CpY3UznJSrFuggZuC6F2orIXIpAcFIkVOUqS9YYzQW9CLhocIfAiMjowYLf46Zt+sEbkeItL5NvU9ozjt/CRY3gz850b3+4B55959C2Vodv9QdlSgtgPJkk9tl07dgSvd/8HwmqXWcq31qbD4S1NnGwwPlskgT4fhv3Ra+rCoZT+rgvipL5aaPEVMZ0zWuCx67gslfdw74M3D0/arkAR6LSzNRVVQVBSsb1Dv2bAhxghtJi1MuRl4NHwoj1Uc1Bz6upgfHDls4VxtrsY4P76r1Xy++pFegDV1NtCN3ArWezutpGy/GqkSapXhb1+tiY1KGINjtDMTo924hQieS6FNVgytqckFZW/5Md1EWdxjUitGhPq1jgfhQbq97YTjNfNdOBXbp6Lf6t5JJDV9PddNSljYLTiLTQGMtl3F2wXLaUqb8dVq8ZE5aL/2PUIx1tW8Zrdd6XrV/KsSKpyfZzjUizf/Q8fXjvsQKFbTBi5XgBSNNxYh+RYTN0ZudNVNvRzypdSbsYHAoV3n3XKBz6vpwsTZSEjZY9igndQIxKQdvG0GSJkKCsyz/CpzZQVrH2Ww1kVuN29OY0ap7S35uRbEhc4vfUFozF6HuY2PICTfTlvciYXLqdjeUBWf7cgYAcHYFgOU3DYEQTYoc8wQUSO2EjevKGkTyKeCIG8yyoZIJnQ2m/YJFjkpsWOsEBBcjiSbTiPmp3t8x9SgXIyXqnjV46Vi4d/TrX/tqLE3u/zbwGKMiyQvfmyxzJpgOSyfN4jjwYHkRiIyJTo6F79JJQ+Uh1vU6BLxPre3I2BTt3VbYT5tDyEnPWUBfQnpM8pOdYwOBZ4nPUxPfeTXh1sIcUXJpiAJHac7gkEY6YEXiOyiiiiS9efANeKhgwan5t4Kw7I7clSoTeTTSdx3CYUU3XrPA6OhpiXEMyZ2YBsLBdvXrSUDhUmSBVqpNRYtbodLqDHUMcvVSfPgpwoDgrNmdfMpZszqE2p0jyEQgg2s4Ax4YPSJ069w1kmzzmQ83pNrOv2KTqL6u/Nn/jRTrCS4uUIstga0qpPJvPxqLkPQj5dp43hKXiTjW3tWCw8pu2SnSLEtlcark2zYUlAw7Lnjf0KqUnD6UQlVWV2TSxOuIbWCsN5FwCYgD8kkUKEeTs9N5hZq6KeIwfk33BiTErcJmLQqXLMO428hfilOX9njNy9UEkG04Umn62EvQjs2SqfQjH16SfUDdo90g3YqNGqp7Cp4WCrDjwEQ0es1A++EJ0GR5HTtAUFY6i8G3kAYJ49ECPagmFkbh8e8BzORIZ4Ls9D/53UtkvratvREpzNRZ6PpM7iid43fFFBtBxFV4GculePUcaP72FOUHqoQZ/5pbHQeRfl6MG7UsltUTJrjp1aWtqa+5JGGXJ5r0arEf61Z0jKqGGKbVqbQaR4Xy9dKO5fWABSuapWtiI6db3FwcDSA89NO6de2ffgaK+KaFxWIhNQSwXmkj4jDcY+zGJ61YipdkUD28s51kjaBL9/PfdqFMX8l/qO4vNYV/Ul1peY240oq0QjaCCSLhFq64/iauwEX3RCsidobut3O682aQ9fUKeV3beqlVl8OVomheD2gBHHYqTRpCFiZHmO51AMlOl2AGcgEDLZiAF/sLL/G7N4jLQI42O5h658RNm3Vk6Xb9KeeUISF0arZUtt5hH14x3Z3YnoQcE4nyIxDBl8QrDXzeI8NKQq24rZh7f2bji4Fk8q+cozQqqP/bskhCpkXny+aEld22sK2oOgyYmIeiiY5NeoXUnnWL8JvFon202EATCpJrO+7kqMgw/HLRBx0kcq7bGsjVGBle+2Jlb4sacBqhC9VV670nORZSTIZJtOovS+5x4aNRll93Hrm68enxdJQyNkG0R2XLBVbhGjdqvkAWU+RF/rjHGCx2JfTshD24gRr4moGfy2vH/UImG3QGvrxsbOybX9qmc+O8YJCS4GulGqykaLnSbQu1RqDOmjr0VKJ5DPfq30+SmWMDO2GVz1Dvdafurtq3ZikC80Qh+/E7tyRsbzqFFAX/rCdRTUosUBBShiGidXOnoo/rBQmXxbxi6hr2coLS5zgFiVNEWhAZuzpIRanUCub7AGwkHZ0Dk9ycEcVHrlI5ueC51NmJWVSbUDJtduTvb76oVIUNfDIQWBgsIno01xireerkdybr7bYBSUXWRqnGCkuAWprFQ/NpaMIO2fW3xvKHMBsr1br2mXm7VT3LJVKbiwZG1zjqfVeMn12jA5qcwbg9aoXBeGVLpfERGql9iXPJAltZtgYLoREXrOIEAxntv6B5HTYnhoJwBcbjdzwZ93O5TZCAWFK4PQywb+wRpwNyaReodEorpL7Dew4tbGGQ4XY7XLE1DSZrO0PNfdZcsXVaZgWPxIpfkpHAYsAZnHUDsYCJ5KYssO0KzXmWtnmwQ2ggEoaoyJ4AuKJ3N0MSY4nk+4C0afM5orRjcE9PEd5r6/uo7qWrlpegdku3VjRjR0mnUvbHkr+pfGQhvfCFA9inJot0eqsQ9f9nMjFNQep2X6R0fiCohen0pvHzGp1R9vWoYkYZFo3RDrFrloW6MjRe9f8O9nCrVnvXJNNuG171buamxC745GrvQrgWojuiIF5EGkt2T9Yx6YFcIbRRl9G+Ci3xqOGqt7zXhGJA5vPa1QC76mkW/GFbML8xaVwVAF3yXgWZf5xBcIiQde+EFnJF2EKHg8oPznMDIL7gG8rY7YdcWHDpTZaZpM1TkR8sQKuvO/YNduMahL8xoFMAyHUMzMiS/0wEO9L/8MX2/jESkzU5Yyfj+dOw/Rs+d7X5uLFBqOQ8u7pY+16P8qM17Cjn9f8lFTi12fDNohhTykUPF0LhFlJWHIFhU4OLLO1CWJMM9jUrWLQ/d1Wfdlf35aWd6fnGXKEHpPDpoEzGxObMz4U7szL31UYmL48d9Q0zYf5BX+d+nwteO3H6DEhvhDRLaYpmlIoaBh818xzR1fe7wrdcB2WOZeYAE4IvINrChMv9bIKXY1lxkuCy10o7Vs2KBEWv5pMxE5eS+JTBU3Hitrns9O/bUt4uGASiEaQiHC43YTFO3+BPfMb2Y+P2p0TP/Ts9oL6Q2P+YnRV72fv/G1FCuf3tzWuwbmVrTS5TEnhNCe5JEzHT4Jom91HqS0/cptRdVb2H5NVGmM4+RyJeIcn6/jpG+CqYB9Nn5Rl0RoCS6POgE+nRtKJp9DPvDz01CQIeeW5xHeOwIzkbTBWgQOACbI32I9CyjI8CYdQv9TGF6KN5RaLE0JdN4AW0EYFUT4JXVuS5FEajjdjFhkp40Dl8nL1uoZLF7RnioSco1OZ6MDINE9RE86uwmkDhWiEXzRmfJyNkL6IqYI/VJkeSfjTJTss3u/18GD+OpXVFxQROabojRX/BRGecHEj5i3pg0Z6EZqK0TsS2uATAmB0UjY6bcaTi/CXZSL9U0/xhynorrCJpQN5WjSwNzT1cFtU4z1Y8edkVcYnGGf/tR3zUYEo1audq9Vnk1B12NE73W9uBoLwlpKcX7naaOLS+0sOOha7VOrNGOvsjEHBMjZewpIlAX7fH8CAl7/UtTUZB4ibK4naY+YeMmte22jjxhLOumjBdIRUjP8vOJDQIcXZQlLGVEnrNVfle7bP0XjwPam6s7Y77hmJP3B2D+nT8gob5wkU0Nsgts6+ouglCyVzf1BqHZo8guGi/0V5wjO1f1ZCqWOno7RTKGqJ/u9uP6aqEH+DkTecncQcdTkFM46HXAjLbgrDtmWTi7bSBL0a/o7NSE1LaJzaE+LIQXoA4NX+hnpbTxLW3hYzzXGG5d0KctFK41kTJjqLmhrvF6Daw3ZCBQnHrzE+UBtRng8vCyVoT2k/ulTx1Qdma8Uv4MUqTTxuCwkzmGWg0tn8Ee3mQShveumoi/Q5ua8fPHYCz2YXTBPRMUh2s/dqLtNCNQDeikQswWCKGa2KW4L1sX9QZzLjxhFTBlxnuPtCaOonb+EPKhYX4BHWUBCNDzOIvoKWbksRwX224UeQaS6gJm5EJQHEz5dfGzSXmySBg9U/gy9tEdlNIiW8PIKNnCvE9A7XoqSbi6QMX2MJfkqiOY49zgLBrQAAKt9MVJJFGhz3kNDWP00Z5GDethj9+eA3Yisu8OfFLH3JgJJ1ecE0agDHg/Ef4rYU6DTfauj0vOYMZEBd4DL+i3bmY6WLhJODpICbFJUm1dm0v0ujZpDiD8QFUSz0gqTu3QbwhGrOD9O5axqZvhh48iAledcaO+ZFyT74qIiZHQjSpDPSPjMs82eJQ37DxUz9UbCjd5iNRyVT4tYkgpERHJunrvICd9tte23e53nCEEF3LBWM4RWoq1CbQuOpJWbtcTO+4t7j6KOuEKHQI2AeBy/72HDh1VwWNz1TRrrBFWV6x7kvqJ8COtD5g135EwwULd4+zHYNyd/zB1mtEiLlHKxh+sm2RCtJgwo5Qd9ZhDntBy9R5d7e/gI+26UTkIbHGc4AJOXvTWs42v6fRofqBOVVy0ILwxNpoKfunoFZMc4ZRTkW6HVPIEbKKRXP5USNKy2pst2cl+qkd+KSSFb1E3Hi3rr0PvEbDMAcjsfXESJS8cYZmms3ZPsKp8W3E0loKKkrN+QmMtJE7cGzc8VhiFSEWAH2ktmZwX6FLIRpMMR05N4HvQIjOVkAz7NDmHWxWEajygkOG4HaxX060LyuNo1fiYAr9skW7bBsMg/MjYUdKo2olHB2NxqO9Ad68vZSBx/6PMFeYBZ84crsg8iKPNxhAPOiCg6uFh6ZK3opF1rxDqzfGUlV9Qi2AM3flie0XrHOGmSSgWz9lPV0fdHOarZkV5wNzpQUJhX57fO08IXo5EUaPiJ+i1c/Pl5wzu0OzzYETuI9Gaaa86GNG02yvfFlkBe6l70nDlJrbFXN8aUmGemsDBl2cQ/s+eMP/BH2f671T5TM5pPCefN/YPpj/ABdII51gxucDPQ+/WCmGlv+nubjBvuXIx0QyZHhcvVa2liZ0F9QvOb48vDz/pleKZr2H501+scBXqj0jWsQ1H9ey0oKbCOJ/doz8zRokw8AeYgNlgJcP3z5HE0zyNCkeaXdS9nBk4YmzNjyUtLMIpfSWeA0qUOha5WQKt0mrQGxBUzTvQq8i2NcWSPp42HL2fkHfSew+cVumkgy4mE6P2KIYOb7mpKvVuPKfYbjkGoQbBSpYKImGHB6kL0JQIzd0roYYLYcovu/26uvA7N3pE2FrOtxF713SPTQlNcJejCWnYmmu8TlB3iNiRzbrwSGBUDfYkMjMbloZmHtP2wNDaMJp6H8bIO62hpp7nIvBdjPKqgiqOWbKk6RAs5FGhV4HYG+AO9LhsU+m1xsVPjnJXJDUGXUuhVtm7QuIWhdyahUm4GIoYa9p83z2yJsFb1Ojq3tHexTU4RdNSpDDei0drq3MbU+7xwW7j8m4RbnXj+vFFeEuN0H9y9KKsjH2Hfm0f8dlgEI5HNAJ1e9DR8T1dNmakAPfiCNeoCkJv1h4mPA2Zw7FjOzKgrhBQJMPHg3ttV19jG571wqonQjbQij8kvV56W49DA5cdWbndrZnppWrQTvN+C/6m264wBb67m/p0oq8G+rDb4oQ2LyktiTF/OnAkROqlhciXCq4QGg4KLCezhvx54PWx+MF2mMQghW6ci0azVNfRgZlbBCdhpk1izkpduyWQJsOuEKxsYzYCJsLoSXBG5ZDEDajcb/CMaYMGqsTJ/uMVNbGg+CdyqOTL5XKRKHG87+iQ+q7r7r56NsGw9p7uySg189DhRQ704Mmi1Z9sE1wdhUzxnWu6N6uwMcVZNF4pAmLZl8KmOPm8efjGj6rk2wpOntg9g5s5elSWXltUJIdka8IZnA1R4mlLJeGINo61kPxxtenn9czuZk98A+Da4GPQOCSVamledhsEcv4CLlFRUiLiWeFyxIrj4vW4DajDa/iSpd5yn7q8Sw6IorU8UUmJIhG3QLTv6lIQFDkN9sAPL72rGFwmN1l9bYln0oo3u5wceja4LU35dT2CwOks9f5OM09cujaMw2FEQY673q7wTGRecuvJLy6uPvug5ugKTrdl7c8IUmkT+zSmvtUhM1L5oroVkCKNNKaIyPH6mm6ZYuFtyS15W1impv/P8S4ixvQZIZT43FFLr+VFXAdOj+u1NGfVoNed+AWnv6aD77FhTqZwgg0+ayk5wcEwiEKNWurMQnMK9qV5ihlyjpplcqspdq+irkTz63TocnaBXPt2+Vut/D7zcrVKbZyBApYKYZzyq7XMvJt+dd0X6urVj7o+tXJNWpywmGPtQjz44w9gKVx513R8243v/3InPIYYGgb0mOA++dfW/uNb5sOOl++t6Gg36/qt/lrFEASMOH9jYUmBIbkNtHDiop/NzK4ALLYPR8PtC7trB6A1QMjZ9PcIG/9g9Mlpdw2I0m7Qnh04cJ92vyDnyRPpKo+dssInTwoL3R3U/IqyFKDdQVvILqGkco8WaPNUDXBSPys7y//zXBEqSItzTHHe5utVmrlmluI6cWwtxIekDPEqNiGFaOcry6wEAHtot4n2LSBqZ7FryU1NyddQI+O25Dq8fZGxuHsv3evuVsvfxbZDXeyYmeq3JluzVyTaqwEDXt8j4Pu4tjRmHVdhXA2LBcE17PDourpNWzaevRwpVKczl5UbFZt+/Nodzg6tyRLUwArjOi4gWpSmvAKoYHPeaSjNUvSpUYW8ssx8L/pg+QppbM9esEwjoKf3HfJmpC3x1zstQzsTX9ze+Sr5e0BFTUNvb8OCX6ScxsP1Nxe+VPbjcnF63Ea1JRfXr3yZmlU8WqTcb8ETW1RBPY6EBNAnRFBKXbQ7LFU5Ga+1ylGbsdNwip5rBvE0foAd6uEGweIGXwWNQ6pemXFFosWukJxiDYFTR3Pa+N/tf1mFnTJOlkEOrtJ17a4fJfDwU0SEgiDXaGoJCv95Ozkk37RJQajVaOQERU+PzBGE4bLLfQqoFmeJs6yFFJcvKyD51YOT7zWdSlnKIEDkB0f6+I2N/L6C6q5mMhSQorQEl1mgxOcvuMLfvJl/ZYTft7mxfHbeLxYfuCLe/9Vw5YDYfuWIi/FU4/Q4Hk9L83Iq0g+e3SoNhoMdwBM0aGngQFGbmTNnIh/RBmqynxw69CT7lTsdOpT9pGbgzfyW94wsZL2urnrNyMia2cbUjOq6swOwqxp1Jeegy6N9T/Ums76CaRkyD1XoLAtAAs1r6moPJXU/2xrjNKdOnEtt9t750GQ/NcndkzvKMJlZ753a/GV9c1r0gBuHqj5FxqtVc14U3Zx2e6B/6wSkpmZRPMSQoYlWUPzvw8pUDmbNpu4/pZD1bdhw2VAqAMgmAab30FGHR4n5e2OcA0rv8UVQGGUyKY54UL0wBUEG0d/NAftNyapaSLZqlSIR17si2UEFrNBDK3pxiW0EVhF64ZaeBfNVJdhDtQA6FkAxDubj8Fe5igzuWxF5Kc5KQPdvsWIlDPdqlBVBPilOD9LHgNRpf+e8JJJB84jA7HRgPsw/ZjBnAP9IMzZw6DbhzER8+wRNm+QM4fYQNE6NobAKnJIgNEq9StqDHq8KtWoHpJ6YxocBtPNcDe1woDPTGfgcjqM4jcCmqtHjltCv75QTu602cK4R+VY/OqwkgnNE+cBO+hK1Dsa5kTLvkm6SLLaESN1PXIJbuPjVuJv2S9ktKZ2rV365aeltmT8Y/66DVNA6sMzw3rpV1mVZjNPjii0jZEplKa+x2s9aqtU1lD/4JLvmDqFcZKlXGTy3ubksyYZ/hpo7r9i3uMM1zc3yU7jVuK+8GpdUq1SW8ZrOCMyEZiiBUFkOsHY9UQ1+RFh/Kge83w/dOPjovqlzLQnCCAXLqK7OgAU1NQIMrQ1YolKlbCBRQ88IGOEZpM4M4ZP4A9HAbHzy/TXOe/vTplRcdOq8lSvp76Nlu27F27iLksJQc9PoH2z7MxWZnflVT6lb/Nvux1q7yVMz5cCd7p+dKujsLJiqht86w5taH/6+xtRMiZushtUFU52d9BUnzLXm4yoH9fKMKkCo+BmdH8Sxfnhnbm8ysbkZ4RaI4i0KhYwgs1ezFIqrvVYcADvkcFrlBDmNPxN+hBirJKs2nzyUtVFygmJROCbzFHNlG5XJRWKv2lEULLf+XnxCsrXv56KY71ZkrFYttijcXeMgLu/oy444HxIvcWhWoRtuUq7zrlHIRIkq+VUoKjFo5zEUw2DYnVFMEnsHhYFVagsLYBfg0iKabx4zANy75plWqAJsBYW1OhwJ0e3qwtjADWphBEZh4BCeRa22zJ5aiItnMbG3evywzDLWoNU6BM1BddlaSWY2loMBMtV0dysIiomJF2YZgadEj4se78noEaqpEUNMLX0UZ7u1WhizMD7ShPN4SqL9/8U+XO6QwetRibhB2l9DtmmCaN/SYg9sXQ0FGoc23tXeHdw0HioOmkHLrxbJsPxxWImkBDeEG7sUWfJYLoAtvora1biVYcmHw1biaBeslmlLZ5XUz3FOs1LEhk4ochEnwV284CXZmISPha30jYhAM9TNgM7CgWqnFlqs90qGLh87/ONubd36r9XOLFP7+9gEMHivs8MfAfX42M27o09GBzMzrdKntoWrPCQn2w67uEeXRSu02n2lpc7z+vOnhScx8GYzm8b90nnQNd0vJqRanFwaUkL0N2Rt7fRd5rw4p6fCXM39AYQz34KEyKqYQPfsb7/7VOm/M2V1XhIdt1dAiqoV/JSWjqZlN2yWHgchQuMswHOC5OYx3M3fJJrkG/Kv21qn4ybZFJLnPwOv4mRD6eEgnShZ0KZTbT6CSiImcHTe3IiqUOOHhANCGwFGrBT4tJ3aBLHg2fg0jEfhNZwJdF4dxIYkr97yai1h46CNZxpewQ7KkEOkEpaFg0ECc9ZUPWuhVFMsfA6AcuDlD5o5SbcPvULPmAfQrIb2JwHC7HZHAEG2zhFAkM10BBDAzGhR1U5qhiYYgAXlVD3OA3h0OzJdrxJQoXxULQcJTMOeg5LJ57/xZTEU4929BFfDWsWaKk1ySDU/hPGCPeAA/dFvsAOsIuvGOdFLNc74Pasna8ktKgeVhOhBphIPFkV8Cf4g3iBx0pQTkV8/XKM3JR72jnxNNrBmqiuTkyuSUyp951cAX9xdM6qo+rZmbdyu2NLLs9LcbSB3IZaX7vflLttSI4nprKo7xu0f+qaxcaBx8zcxigHW5CTCld2Z1a9fGcDzaUvgJuxKqc6sTa6KrPbeGsdlbRLlVsQ1UH/PMD4Uvr4gUZ0V57U1qoZXlalIrUlo1xrl+Sb5NNKNSWzTRTd94nPI6cRtW2PIvuwBooR8jWReCaLs9yVVdukBMQ+mRAeTsj6TLuhUrNIbNyrpPXSDWrhfp+OfvjHQpTo9MHBa+5oGNtKLik4EhHQXFAAo5Rd17Q4exp2tOyDHQtJds5EkgGuh2oyAwi7ze6pGxCoDEi9VHVqSH8ZOCPwS56CmfG9xisoVS5dHO17W5L6eOU6n+2Uf/+14S4sMkqGoXId3aP748X6h8vJaAnBI1GKREovN5Im4Hgy7iNtba7Y44snNzGv34i5iWA8uUb5YcAK4eA5ZYV61GALQIpjRI+ufGJnjQrMQd25ipL8R8+WQddPwoOltNZ5Gsg+9fj7H0DgfBYCtwWL9+o7kTjrdcBs0C7UBW2d2XgpCvdNG0FV6+yk/nLw2MI/QRsnJBziYggDCLwQyoIxDCDiojK4+GJ1OOEfuj80lEGzzJegf3TW6RkiYezSENmgcBKeO77g0jiXGASMNN7jomx3xjs36y3gM82+63E4gdKpclSffyKgPDagg+uZFo42O5r0wI4MS72q4TsOjVu/TuWTgP1dsY1eQgdfwiwvE7QrFvr3WtbV1+y2TBrt9DzKEMqi2pUVOkL99I4fktbUySF5hM/D1uxmlcrvBcXOnpLCIhC2PUzMmyAQU7/SEZrTth6MOzOvOZndsLpo9V/g45YQs9eDSY0gD4a5qnmNU6rFXrg6R16AFc4E5DvIwnu6UWuBEzk0Rk/q+QzKSWk2Sjd37kGRqtYx0nxYiOMA6Z+17LsaxsNAxRmI2gzHHOCIGedSmPpj1vwySrVfAOaPrINNWmhqKivYLr2DXEmq//a4Wmo+/VPKUlJGRgDxJEaO9TdSxVyclrWYbJrhceeRa62RrAc206PlSBHnRaneY5gUVffmI0IDP31s4whfUjQKGu6PHYkLtIKknZCdt/G/7Eic8nRH4fEXUys016vU6FbO52otvvJqpyT6ytXIsboOpacCtwQ0NPFSquFO5uZ8+pRZks4Ug//TpcU6nqt0MLmcEKyDvUwfCGuu8DVH6+beBvusPCQ2B4UsCYUIIAb6M2+A/X+2L21GNRSCHk7VyuIb/aqTugmg+9JVFppDTmzsTj0Od1603f4WLHLdeca8KxmBVr2X6Iy2fmBi3O29KmMSL49LmjtSdPikLx/2CO0pn7aPPf9etOVI7T2ftoh/F/WlJN/p9l+I4S6GSnB/bgQRxpmqPudFl2JOjK9mXJ27xz7drM4vBrbsH/GVGz4ED+wWe7A6FMLGa8q/fViOp7cZwpU1BemJeUI73Vs91pNt+3jF1upfSk5V3Hm7ICV6bLklJl6GKXxzGzNp2ZFeuyPaP885bUSzN3ugrTA8EvmKCFu2+yQKl5YTGxIdxvP4NOatWHH3vCZTOj1bRdzRxVeQzJmrbxLFIWWK8IPy5iAsVv3QVdI1UnPWIN8+B8pKr2WEWckJ3UDk/Kdt1lemLVC/ZYaOVjkExOZYRsWuqTQpc0+RQ3d9zmzzYVGGejdDjQII8P03iCygQf+oIvC6hLCclPyzHJYFhHH5lzgXrEo7AnY5V4ZYwtc0velHV9ijRuP2T96RhmayqcDouNqtqwv9kRkBcVq40psl/e9NSaez+GQuIzTjpr8mqBm51/a5G75hNX4anPaa99Vo44aQDSOPuimyHc3k1ayX1zHwXKPBpOQILItk25Lp91It+V0uE258EkWhZqWuKyvYXpBOXXOD712yTUm0Pjru0JtINuh3mpvHY8jC+78Fi+11nyhOUtb4iwufegERe/bLmvt6MqGr/sRVKKimemjYDqLUYiy1ZYtlo1uD38ukKWv2v6d89BN6RpkEsjsoojp1LI9AJDZayT2bISgIbOu47vkmGvschNgFZaSb7ZNng1iVtrjg2I6r2mVGBtdLUzFdfkRUb9kGbdn0/K+hH4ZrK+gljYw4qEP9t+/SSZ2DSPoUO9XGx2Csc+6M92Vs1xM2Ut7bW1z+yOaNXwMkrXv1vr15F4OM4c4Ep5Y9m5wuXMmH05gEWrVGfBXgBGn+kF7dph+kmCU5FPiJeTmHkYZ87ZorZzDldTkUmCXQYXrDAQ0waeifiZYU4WlLxB3MmNt4CsjdfAB/8w6NjeUqekTEaDcT+QFRasD9TAEQy+woah3zUUPXUy0/TjOlcZKoaUu/e8Ps3ekjV+IPusTlpyAMAi1Ejtb+2gnpys/NjLvI09oZH/VKdEzTOyHF4pvC+PDJ+WJJotfduCOEZ4xngqbOoBsUyiGF1Qq1OQ9EAK5uia5dY8zAO0Q0YE2FqNW4DPt6JqPWyEmUz9gcRdt6nF9P06TylPoGwX7KfkKAH2wx1SDqgBJBYUp3/JX454QQhNPb8b9EP0bym6BwCADOFuuKUOD+2giDOHzEBZBoj79TR/ByWmkEmi4SEe0EhaTYLi4zt3C9YYZ2foxrhBeOHpD0SVxaJO3zvBPDkGimBINBnFr5+ow0/Kr7mgr3DIH2/49qniEsRdMw+NXytRY610O7R3NUup/30QQf7mgtR8Tb8+g0CB7KAvig2GgoKNtGUxjcAltr3PDn5+V/wlUPBDGYxDxn+69CO6Wk4FQa+robluywNVrs0JMCfdXTJ+Jz4o8ZpwSwuYHY2cgnio/KOUA2vGr1nRkKQyY7HCnQb8sPn2g1DATO9O5gMHwQYLLxvw4KT5uOceHwJCi9L801wqTFTX76RWC5m91aNqoYjvFU+yJLI9YgjQvbxXbUNQRUdj5FJVm/AzNCGz7XAkRQVv/xHVFYxbnIro85PWMJTlSULi5sEwrO2mWanT1pb21/9OZz7EZFQrd+w9yAPe0dsEW6RBSXfI9rbaMBkd79IoPk9hn8guHmpZS/tqle8GbO0tj5/0izT9qywSVAsKk1WlfCEfsK6SybjZRWixIu7+00G7L2jPfIpFotxRr+gU7bfCBsFtCLJR9HrVJpGmY0quUxYLGiKW5e0upOnd453tO1l8VdRRdl42uu6DD/h6JN7EF7ahkWOeO9ou51p/bsFoteCjxKESpSzw8BIjwelfPNe2c2TioXJZSpeidCvLuN12nhFmejry2Ij7jubkvTUnTxdel1c7YPXAoGof3faTrtob7xjaHG4RZijPR665+ITNFExH7g3Dv3d51f8vcyTbMOVNo/hp78UrRJIRV/Mo6D5cXn/iR7hC1kGUo6k26saPHg91GNT31gVeSE9MPs4x5fzeNYMmJ30/j8fsXt9ov/A7t9GX4T84cegmXr4r4lrdKnJsfCIN7PK2oJ8dPunK2Gubbg8eAdlJILpZZaP48mNqtc8Wxy5VPem/49YWxz+4ZobC55/+AOj2fYAG79zux1Ww8yLq96nVZ7JKhGz4Yxol1OpSz1GZctzdyB1Welvzd/Zr25RqxezPU4bRTpb0ih/F3Rd5Q1r13znQJHZv3VaXDl7aIGxj3YQfxiAFNrcldOGLtqh+nNhg4kkdSufcbkZdzoj4x/mP+Vl+lSJMz3QFKwH0LvQIbVw7FBMYM06hZPd0FIDOwzYZwjKrgudBkZoYZ3OkDuvFAcTzBOGNUlloCsYltvY9bsODJ3XYnQwNkFXNDBUzWhKY2M8JgPAbUpjY+AKuBAMjQfzoU8cG0Nuq1c//PlOB8Jp/u6+b10oWNCE+59790x67Jj02Tu/8NjxZ7nvfMeP5z4Y5Dl+bDRz5lZ5+a2ZYIrXVd+bLPmf/vHXxSNfynW0+StEZerq7Zng6U3Z/KJ+A2izcarrsoeStyNZ+srm8Xr8JDvbDDXNrzkktcsgerIdPv8Kvipq9U+fjfiM8dsknNAkTy+vwA8Vw3hS7b2DwnT9Zi19Kp5v78mm+NnMfDOGTTsVeN6or1WUlbVsLy4U8X5Yx46vWeG8NJl4Mybm69d4riI7pCSNS0n2kjXbZNqtDL3K4fz6i353W8rUTRkfOU/Y4yU00uFRqBx96RlTXp7sdJad6EDRy+YOd1ubWTst3fb/jcC6czuiYr7Nd0gtKgUM75aWw2ltvbZJyggtth9/MWUvlX74qFROTq4u8nCy3/ApSCT766tX799+j87wA5C1ycam7bxPCiig6TnohizZDV1nTTZyHeorhCO7ByWD4C9z/HevQRicJBH1jHHGNMsRB08+CmQ5ffedEyvw0SSMc/Sas/0/AzCjmRRhLD6deYu52ohzPPD+PYYs8ItjXypc4oNE7bzcfcgyGU3tsM3MVDgXLxLtNOZn5ifapp6d4jgn+30ii0PiAyqEXDm9I1mPHz56JI7m9tQ3Y1tzk3wiJH27CXltzBbv1cCrelF4IDW3JeWgb/nlkyRqhmvQznASKfF4vcT7LTq6htCYfD+dmG/j+Ganh2dGcsCe3zIVGopTkcda94wCEXF9cYiKtQmFb4AdHyx3ecVPoWfKE5BDRjHWbJjnnycG7Uw1VDP18jP70fB5qqZNiTnaMiJzlJjyNRR1G0SVizbA1C1K7IlVCIZiBXO6zxgKq08pg8wWd7hSDS0y5i81Ztw8qkJRzDQWa4yY6pCtnUe5CRMfKSXfvA7jPGQexuDEqsSe7bwBM8gyC2COHBphAhLYw12pqlN7o0sl9FxdpjMIJoGKcBKEk66uG9q42huIlEPVuKIM/Zyp64a2kyz3wA3a+V7pVNDZ2ze/aLw1mXX7bETAo3jat7Yfl/EDTCdEtgbwhBhywzYd+nYMGdW3ZmNc/qP9p7VnQeoFkcKds6CGskAAP7a9nsLYf8GRCZyVR0bmwVYRQbdsLLa1xDqnvqCVaSN+TlX75pNEVn43vo9rt0tgGiGIUByW7E1Ys/xSzcYkI+5UaWloqJ6ub23VmMU8LjhVbcc8ks4z79PpGEVT5DQM3Kud+p9WHjmy8ie9mWJ20nu/ofg/7lZW3v2jM53XO5RVJ9askQLAtTFS2Vbpe0LH9MbuaZ8H67ofNEMLUmjc6YpyNn6YH9OWkEqUpR9Q4M2O1fdNH4cMCwQ3R4zQAC0sEE5Mb7z0PJ+yttGjeuf3lZUySCYSfBYks7KSvDx7DQam2pyTS+RfnObW/21tU4wpPn9yks+bZkAHHz2a4kJGmYvvQ0IAsamJiYOHJieHRn0ZQKkm08j/GQSEedd1YuLQwcnJQz8nqx7q5fHnGFMB5jQ5K5fDk+SxQ/ius+1Jw67wpNkfjCvX55jrZgUvUqsGVeoNzBLuQwuwAUZ1OhRDESqjfQyGVDofurZ9e8Lc3b0B4rK31HWqztcX+JWsZVshrpY++j8Li8QP5f3auLgix00KOGd6g/QwXEhrg9QGWrM6xGjlAq0bfpkDQBOqKx30I6tOneoM1mZqvucYebXu5Ytpb8AhhEL3Cf7x9LeTsVInqTU+2hMDYNryWyEawsRUGIhgbR9DAZqdC0mF0Z3DfbhuCo8+V98Q9AEhTX0YVcthdvW2ATSQgDMpIRAEpwEOaxtjyIIasvNt/j+Sjgnd5WTvGHeV43YXqyHXlDtYz6HbqH29HTjtdnSV69Ai07wjDGvCdhdYikoXmbFbk2ydtlta3ZlNw4Cn8cMWWEMHM2zqllsNw1RhvFZqi6GF2sq7peUYAYzRrCLFkxfR8gt0OhWCKJ7q4KbIwTy+CAZjWvN2ZZf9UZvH7lSFn6BxSOGRaXug0umKgFHln5MnwZPDlruTaaD2UNj277+t6PzIA6/h7W1LykHnSYr1pBmPkEJGgwqjFQU9iYm1B+LWB1Thhb224CjiD5wmVFMQnz8v79iBQTrWtx6su9CeVqco+PdAd+8PRgdhXuOmXYWMteRvXSrT8Tk5FhasUr9pDuHxX9TymMCZ/s7LMnZNk4DYYFCnk/RmA6a0BntRBlnPFqvtSH8jVjd2xTfM0rCgcT5A4POrGH51yZjXhkF4sMMvgwKreNkIsEL+4DOjxKDZ9ImddIPKwXkdhmIwjJ4WbkdgBMEMGPIERdoEROzZjRrkQZLUOgzGUNgQBXdJH9M3z+wQblfT9zJFRDxoGESQJlqYiMMJzqA3zTPhJvrNHOspTETLNDvcN+jm0bQ/JK3uy2tA2QMi9r8iTCZ+p/n2MR3KumarMTSKyrF87trZN09zjx7NffrGTDE76d0/wnsxJJAXgwOvdymZgDEYfdDgMOh+N4TaIwgLRRA1iqpgHdJxJm8Nx2933s0Ly9Nfk4XptIqq1DhRMdsaj0fzu7vz6/nTyYr56vkwGTjl1wJouORXv2WgmCu6slzq5RPUiYZSi9TKF5PDVT93ruBl2fTvT9kZj91TeBKBFkFV1syefzOYfAk9V0G1zd3FUp0OClDxsHRPJVEiMVnXlB0ZIXNvJSWtXp0Uev9faG4sBP17P9TcBR/4IkwcrBc1sV9ENqnu7AQr6u/Ky1MYYsY8geCnzGdmSsv0pTDkYuxf56HReNQtG+0Loxg7iUir4uPi4leROkeYTfBpxEVlzEl1qq52Sl1+bcjZ39hRSExLa+y7ymhinkE+fS4oaJXcIoLz41VdojlJ7Whf7lavQIebR1oQMEMK3HAVE2IN8xs645lMDDONoXROKqpODL0yv9MhvDOMjQ1DYRizl3luLpXK3cmLf1fiYMyz3H0YsVFCG8xDj6rDaSDBoTgqCALD73s1N4m57AVPI2FUossdQr2fgr1V7W/+aacw5w3zX8vw0fleCkNoclV9fnLITBkgMfJ6/z4uLvY9HCUWR8Gam0eMowvr/G8gmZCHDBiMRel1kVCzBVBz2JjeuOjzOK3wA/wF/lCon3UmO+bKKozr+XxpJqT/UGLbyJuwspho0ju0W5eAfBh5KmODVppohtK80ij/lH7OFl9BlXFVMre9//RHSVHHM2CuXsp2/j3uQKwP3EsnpLXQh+jLWiMINHNKAj0PuqQ6c1kFqegJFHPapWLCeWoMr+u3G1MfX0XcgyKOqouKQJ5+gp/nuQg+rTg2uvEjznmx2uTlW+/oY/JT74Sl2cWslpCU8vIjrVNKlEda+655GXZ2Et3fU/nRjxrmiZ1wuHdhVJqez/XFLxMsHxQKOSdKa3YlJS6Gfm/yW8zznyDooaf8HJwTwlKxQmqin1PoyIAqJCf46IWBCKlww6dTpXUAC+Ar5wc5GFys7V9mK+Xy/Pk49RB1XCy2yhSP03Tm5fBwntGN0B5r2K4TSjBo8yhdGE4RhFHIdvOzVx+sgcfMN/MMlTirgzY63Nbdo8/iC7fxV2OTr1lfaT76rIzdIpHfUqEQ5/WS4oEo02UYXd42+LmqBFJBJVWXNia0Rl2UvTdAzLNrM1gNaIE/jMFL7+ATrgTeAB5RpDKZQghrvls8b6UtWw0RAHN+nxzuMK+NXVScsMMywc3kr2jK8d1KxnHuS7l2p6ufKDMySha6/hrtLy9XCIUavCzjrBnDztt67wsRj2QkMtFjQbRrUJQPuQGXCaeUS/8rgO6tRWOlC9vCAdwH4FtRnvng8/T5+2n6lxZFZBpWHMP1eFI4GZrkQtA12swWxGEXPTqigUtRmLadA+fTHFygsEDGVrteO0tyzAmXTRh7/PcT8cZ7fyP+80OPd30Te14s7RunJDBSY/9cb76rUb3RvMHXpVD8yiTpAYYbWcp2cOCuPj8PLv8fgMMuS6HIS0Fijsx/Nv3exBQfNb9/t2vykmWOK12yRhY8SMtlIqo7e3dOiXl4L8bX5QcmZuaqhC9YWhhbn6Q3u5q2YyXfxYA1vWSVWV+feSLQq9+eozJcMzfXCpYLGmtcxOudsnxGAk8gipIPtDY4iqjx8IWRnJzD7/y9F4SN/25L8Bd6UiKPDhmD/Yeglp8/LzfQMzKaOtCw4T6OsGX2V0gEqVXyq/sHME/d16e+NYW0+P8NpPru5GUzSIeuY2/HPmwWXTC2MrGIY/25h91Iyjmae1oNe3NP9QSWIaVBLP43hj/FtzMAd+S/jkEcCuBGatr/uDi4QhbtJjhVJAYRR4WhwgC12d/pJBu1WTWYghiGDw5G4hFMhTVux+yy2PIxlpQ+Agxx87oyo6MuqzaTA2WX6QruDey82vWXnCuYlkAvrKLwmbVr7WJ74Pcoj8U3B9BpPRulyXtszY2s3YKt4s7mv6bvGaA4qwOFMWedKAO7/BPoJc4C02gv60Vmtk250o3ddJ8ANQ8fFL2fGsy8dme9bwPaIOp+AeCpm1dLaeeItlUHq9/Yo92WrXesUlOCRexG7d9UH6yyJaoNYD3tFxiL+HwqPTGC8iqO+RYfu/23U6dY9qyAHrfYXury03cpbB+Ww9ZmUZ1I4/qMKBRZU/70hFPLjEuPt+Yx3tji7VddtWaZn7ewN9eas14mD/1w9EBUJy7swCUzjbOVhMMNmp2vtN/e8rsR+TXPemFUZjbR66lBNdwZTJXzWMyh5rfBfPEITLh/LZ/lls63B+rEGlQDFtdne0Epqu6trkbRFZUuIhRo/BiT+WqioEE7EC7w4n7C/qCFb94lsOgM/UcjGtF9Jl0CGt7XvmPcYA9Du2hIOXhuToa3WSDOEhds8LJj3hQDpFwrdlxFn6WrxqcxpkQ5S7dY4SkyYgEuv/Otk070B9oX/Veip47cUdepJKUvBaOUEHw2dMOwmcMzMhTUm6O0N6GhF6YAljK40dvQuHl1/DBl1/GAKZJO2HVoJ2SctsPuhPWBH354WYnJCx4AkJG0PsTaIwxiiCJrM9MO8MIMA7yDrsw6E6A5v7qidhMPiPoGJNCfQ906FMopSLnLPgnVppp6x9scO2WTZFxqF20aZp/kGE/PYSXyOZqRiARjS5t409AP26XFIWupJUiB3kRukxB//HtZ3CKTF3tuX9Z9Ct8pOYM9DV8v+x6HWs4o6fk+Fmz6tq33WZ4Gn9ZW94sbBmdRI6ffrTpRxAGVF8hidweDx/fVJL4benex8NmuiyO/u+N/VRSYP3zF8O9HCNTOBYRowR5/evx7+W+6JHfx18+cnbS6BBwpfFZoido/u4wNFFpWjze+JZ/8R/tvL6PXhof06UXPIrlL07KFoOwVtQhsBqVwNzbOAB8teg0hwWyANBduPpS8JFzh13pWP3N6+3FlauxR5+vpXW2LmwTmXuY9XrUN5KftraUhoLK6bIX0SEI0c0wLaTl93h0yol7X/UvQNQTFT0L6KejtTw2t53ZefqoS6rX9792AeKaTcm1cHkvaJkde0Ac1j0Pn0BBMG7x9Jka68pTAy+KoQl1LhhShbjOGhnzNc0dqeRrwFmv+T6+1Ftpi5XPcveZhVz9SNvASobeyvkqQwsdmaOPaMgkMxMpsQlMcp1w9omrV1VaXHsoqlB/0WaaTFF6iosGZBITLul4aRSkH1egqlANcvZ8EoAoDwhSCctRyKGGiHUD4BRYIhDZu1IwUoz+lfdpkTLCpFx6mgRaaZ6IOSR12cdhOY9DHYY2Rxq5rjM33bUyM9n9jwUEhpLFoZLijsVbr8LW5zvJ3YwM9oqbmhpbh5haW1XNf0jqK/9KXlaJzTB/L7aNnPpGclzHcKjQtJfATJsv1MBEIVWIWgylF3KyNhioZYrjU1gY1MZfE74TnCeQr6Cs7mI48hauGkmAhcbBmzRrOTfkqxixbL0dLKxMHexcEwxKXro0sPkPiTBOBjBsB851SJSVjjLPCxsN+kZInRUePhkGJrke6wj2HaMIS5J+UjrA4HDpJROxOAinFV8y74UFGKXVjdydxaM1YH8OoskxAYYS+fow2zFBjMkzjIqVBCIUyYuzIVQmZwCaME4CL/wyvOfZBI9NRTE8HBKw6gUUUgDlrp6mSkcYaZt5LRpViOTN0ukwkY4nLrHD/THr/oL811GQS2nAIov7w+duwPiRgnC7376sdfljzBz22FwCh4z+EoBhOkBTNsBwvEIrEEqlMrlCq1BqtTm8wmswWq83ucLrcHq/PDyCCYjhBUjTDcrwgSrKiar99+J/QDdOyHdfzgzCKkzTLi7Kqm7brh3Gal3Xbj/O6n/f7QQhGUAwnSIpmWI4XRElWVE03TMt2XM8PwihO0iwvyqpu2q4fxmle1m0/zut+3u/3hxEUwwmSohmW4wVRkhVV0w3Tsh3X84MwipM0y4uyqpu264dxmpd124/zup/39/8AYiScq3RWJmeuz5btf8FyPr882Xnz5T+PkhmTmI37Zv57nee0t52jAIm1EZueJe6178fMft9a+/5hxXpXvr+899z13TKfHbVzdpDvwMzyHZCZ2WVXHasAibWR4AIAAAAAQEREREQkIiIiImJmZmZm1n0DkFgbCQ7TTwGEMMYYY0RERERErLXWWps2V/IwOELW5xBJG6UPAAAAAAAAAACQEwAAAIMuAUisjQRXCAAAAAAAAAqi34gTx9A5oACJdYQqpZRSKkpefYAeFMQ6TZS0JEmSJEnSDkaCi5mZmZl50Z+e+97zwF9Xzcb9PEc8/gMAAA==\";\n},function(e,t){e.exports=\"data:application/font-woff;base64,d09GRgABAAAAAFuAAA8AAAAAsVwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABWAAAABwAAAAcbSqX3EdERUYAAAF0AAAAHwAAACABRAAET1MvMgAAAZQAAABFAAAAYGe5a4ljbWFwAAAB3AAAAsAAAAZy2q3jgWN2dCAAAAScAAAABAAAAAQAKAL4Z2FzcAAABKAAAAAIAAAACP//AANnbHlmAAAEqAAATRcAAJSkfV3Cb2hlYWQAAFHAAAAANAAAADYFTS/YaGhlYQAAUfQAAAAcAAAAJApEBBFobXR4AABSEAAAAU8AAAN00scgYGxvY2EAAFNgAAACJwAAAjBv+5XObWF4cAAAVYgAAAAgAAAAIAFqANhuYW1lAABVqAAAAZ4AAAOisyygm3Bvc3QAAFdIAAAELQAACtG6o+U1d2ViZgAAW3gAAAAGAAAABsMYVFAAAAABAAAAAMw9os8AAAAA0HaBdQAAAADQdnOXeNpjYGRgYOADYgkGEGBiYGRgZBQDkixgHgMABUgASgB42mNgZulmnMDAysDCzMN0gYGBIQpCMy5hMGLaAeQDpRCACYkd6h3ux+DAoPD/P/OB/wJAdSIM1UBhRiQlCgyMADGWCwwAAAB42u2UP2hTQRzHf5ekaVPExv6JjW3fvTQ0sa3QLA5xylBLgyBx0gzSWEUaXbIoBBQyCQGHLqXUqYNdtIIgIg5FHJxEtwqtpbnfaV1E1KFaSvX5vVwGEbW6OPngk8/vvXfv7pt3v4SImojIDw6BViKxRgIVBaZwVdSv+xvXA+Iuzqcog2cOkkvDNE8Lbqs74k64i+5Sf3u8Z2AnIRLbyVCyTflVSEXVoEqrrMqrgiqqsqqqWQ5xlAc5zWOc5TwXucxVnuE5HdQhHdFRHdNJndZZndeFLc/zsKJLQ/WV6BcrCdWkwspVKZVROaw0qUqqoqZZcJhdTnGGxznHBS5xhad5VhNWCuturBTXKZ3RObuS98pb9c57k6ql9rp2v1as5deb1r6s9q1GV2IrHSt73T631424YXzjgPwqt+Rn+VG+lRvyirwsS/KCPCfPytPypDwhj8mjctRZd9acF86y89x55jxxHjkPnXstXfbt/pNjj/nwXW+cHa6/SYvZ7yEwbDYazDcIgoUGzY3h2HtqgUcs1AFPWKgTXrRQF7xkoQhRf7uF9hPFeyzUTTSwY6EoUUJY6AC8bSGMS4Ys1Au3WaiPSGGsMtkdGH2rzJgYHAaYjxIwQqtB1CnYkEZ9BM6ALOpROAfyqI/DBQudgidBETXuqRIooz4DV0AV9UV4GsyivkTEyMMmw1UYGdhkuAYjA5sMGMvIwCbDDRgZeAz1TXgcmDy3YeRhk+cOjCxsMjyAkYFNhscwMrDJ8BQ2886gXoaRhedQvyTSkDZ7uA6HLLQBI5vGntAbGHugTc53cMxC7+E4SKL+ACOzNpk3YWTWJid+iRo5NXIKM3fBItAPW55FdJLY3FeHBDr90606JCIU9Jk+Ms3/Y/8L8jUq3y79bJ/0/+ROoP4v9v/4/mj+i7HBXUd0/elU6IHfHt8Aj9EPGAAoAvgAAAAB//8AAnjaxb0JfBvVtTA+dxaN1hltI1m2ZVuSJVneLVlSHCdy9oTEWchqtrBEJRAgCYEsQNhC2EsbWmpI2dqkQBoSYgKlpaQthVL0yusrpW77aEubfq/ly+ujvJampSTW5Dvnzmi1E+jr//3+Xmbu3Llz77nnbuece865DMu0MAy5jGtiOEZkOp8lTNeUwyLP/DH+rEH41ZTDHAtB5lkOowWMPiwayNiUwwTjE46AI5xwhFrINPXYn/7ENY0dbWHfZAiTZbL8ID/InAd5xz2NpIH4STpDGonHIJNE3OP1KG4ISaSNeBuITAyRLgIxoiEUhFAnmUpEiXSRSGqAQEw0kuyFUIb0k2gnGSApyBFi0il2SI5YLGb5MdFjXCey4mNHzQ7WwLGEdZiPPgYR64we8THZHAt+wnT84D/x8YTpGPgheKH4CMEDVF9xBOIeP3EbQgGH29BGgpGkIxCMTCW9qUTA0Zsir+QUP1mt+P2KusevwIO6Bx/Iaj8/OD5O0VNrZW2EsqZBWbO1skRiEKE0DdlKKaSVO5VAuRpqk8VQJAqY7ydxaK44YJvrO2EWjOoDBoFYzQbDNkON+UbiKoRkywMWWf1j4bEY2iIY1AeMgvmEz/kVo9v4FSc/aMZMrFbjl4zWLL0+Y5FlyzNlEVYDudJohg8gPUP7kcB/mn+G6cd+5PV4Q72dXCgocWJADBgUuDTwiXiGSyZo14HOEQ2lE6k0XDIEusexDzZOMXwt1Dutz+tqmxTvlskNWXXUQIbhaurum9GrePqm9Yaeabjkiqf+bUvzDOvb2Y1E+EX2DnemcTP/zLcuu7xjQXdAtjR0Lo5n4/Hs/GtntMlysHt+29NXbH6se//WbFcyu+r28H0MwzI30DYeYTLMXIA2EG8QlHpAsyS0EfEToR0a3utIxFPJ3kiIHCCrZ66b0e2xEmL1dM9YN/MwS5p01N5jMX/BLKt/1R83l0LyC29M6+iYxo/UNg/EF7c2WyyW5tYl8WnhWg2/hyySbD5UhnDyS7OcU0dnrFw+DfGdI7v4QfYIIzOMq9hFtY55gmvC7jZ2FK7sEdrn6IXBuucYhjsGdQ8z0yEbWkkczjjsE5hNAIZrPx2zOLZDmKNXcXtg7EMqidAEEWg+SJCBBNwxvxJfc/bZa+KKf+xoKZybnq5vaqpPTye7CiF+ZFjxZ8/7Qij0hfOG/cowPA1rT1l4ymWnrKmxxqfErTVrpgwPlz1kC+Oy8NMDz6c+IO38K/x0xkPnLW8Kx6qGAoQdL+TD9V9rb+/ctn//trxz8dUrZrD/zk/ferF0cNt1BzctmX2FZPXt/jnFCQNz4Ah/iKllGiCMs1w5Lkg0kiEwj6VTXCDKsX9rMpnvIj9pcDecXAIXMnqn2dTUbN6w0XQ9ue6FV/nnXCH7S3lPWGltVcLsH75ub3ab7A8M28caNrIeOr3o5Q0yFsYL80xaa0EY/UEczV7icUMY5pnelAkmUAXmHYjvFWFGxuqlSaow3OM+/iYY7/l/hVELF4EjRqNR/bvRbOY+DUGzGR/Oh3EqmE/ugIQQguGt/eMYz/+L0cimjeZfQDI3phXMbMQsqH+CjwVz/hf4idHovgVmB8gLvjbicDcC/NypP536E/9N/puMibExdohBmNwyiaZdJGoigos7GpF222xrfnZhML/7Z+ylaqP63Hr+m7bdUkQ6/2cXqdfmvwixY+s2ksXFeXcE+iX0Z+Iow76DBNgjJ7TOdUK18iPsPflfQD+DPsZG2Aj9VmKMMJ4fYRrhIaxhTDR0Elh2vA6h/AE6xUb29mj3sjmL72petXjejPy+oel60M99tFduCI59N3221xe7apOvxs6aHs7vab1IqY2tv7q2xsHeHGml/cV06u/8S/xTjJ+JYc0bWEX0ukW6YmIbGkJRMdjJ9mYIH5QIdJF4hvRGyK7cC7ctImQRcUET99fGXOoft35GYLMQu+g2smnkgZUrH8AL/9Si217IssJ916nv14ZrJrvdxLkQvrvtBcjgPC0NXOicO8Qf4mcxPqh3hgUw3DDfdvLJXngg7N3dN2zbPJSaed3OfZnMU7dvmznp3C3bruO+Nmue0LFsy7S+6265+fCKFYdvvuW6vmlblnUI8xCXp37CrOZv4B9gauDBlYp7adcUXB5DNCwYImlXOJJKkAdvExXxVvKEYnCo+3eIskP9qrrfIYs71CccBjfXRC52udTHHdaP1A1ui/VvH1otbrLrpNXBsGX5B89QghDyimlvNB2KfkxZ5C9/em3+d1+d//IfFp2+2Oxn/s+9n/79p39S3s8idN6g0yZObwJOgKUpNB3GyU0Ls0PbRzIRq4lcarLKOJBkLRzJQD4j2090XrbA7DW8K3jNF5hlGS5e4V2D17zgss4T20egOJte5iD0bReM9yjTxnQxCRj3c5kFzGJmGbNKmwGw39IJDJcXJZGMkaAB4jyJAKw0jt5IAuIE+A+U3cVAZZrq9zhDyBrU8oosuxcGNTzCKJfla7JjNVmuSb/+tuzN2H+X4vlB+PpdfMXXmuVsNiub1T34SFbjYw5itEvVi0K0Nt9pNJUMI7SLGRhf2xipfCYf8z5OdlGKayOucFeVPeS/dbo3lBrbSMmwUiQN5/ed7g0Ds1s17IuZC5kNzM3MZ6EWCa0DtekdJfAxz+R/OX28sND7yRMTBcf++s8mQCQWHya4qBv/ufeMoWyslPA9DtMxUknxkH/yfTnm2CMYzs+Cq3r7PxY/MXomrvTEsRpfEGHa+WN8E1AHjElb7d06ddA7oK/+5Mdsv9EtPms0jv0Z5kf1FqPxWdFtfFr0kHfgDX0Y+5PRSG7RUj0tQr7rmfX8DH4G5W28kKeJLtmQsQkuwMP1pk16EV4sl7vrMJATfyUWo/GwEco4rh4XFQgaiUX9qxZHrMQqKnz/c2d8b9TysYrAuXpP/Rf/Gr8b1qwwc5a+euLa6S6sneNXToG2XrEJi4R5SGs8Sq2S3d97bsfCRaTdaLwKClRHt37mkudvXbjwVrLhuYeGhh56bvfQkHpk2CwvwClqgWwuBfndC3c8dwmstj81KkagcUgbfPY8Zje0W/82VPWJHmSq6pP8hPWpotc/EexDOK3qU+wngPhOCiO9MJRm8TJefjelrzoKnG2Bn+1NCUmPE4gHFmBN9jrTigRIpsACrc9Gstg58ULkp9467+Gf/eFnD5/31lNrt2967dhrm7bzI+VT5m+fzKhvf2MzpICEm79Bopkn07lt1762adNr127LwVqQLdJ5+lpQDcvHPQtVY5knhYrK6q8/JsiP6EuhGZdFdaNszjvpqvc+PI0CdjN0AXsFOC3ZfALDJwr4q2Xq+GF+GNbsxUg5NLLIEXi8otcDQcUts0D8eQ1iVDRAMBTsYiNdRIxE09EIBJO9A2xqgERTaW86BUFn0OD2xFO97FAgFhF6OoQ7prYt4XwSeUgQHiJyDbeke9IdQntciLQ1FlJMaYcUNvZBg+FB1ubjlnRNvl3o6IEU2w7fdNPhm/hh+FLysUu6++DLHkOkrSHYEjH0tEPe7WdD3uyDgvAgK/m4szFFR7ch0toUgBTdWHr7EpaWru6+6dmbbnqWEbV2EtxAsXiZAPTtGPSbHsotI2leoM8TePEqgSQprs7AGFf8kuOkPdZPXGb55POAW1d/jLST9v5YflasP6v/CO7+GNAPC2BMZWmsOjp2NNbfHwMCJD+LPVL+D/OYlWEEI/9jpPddOFkB5d1GSuKZYggmCCd7JUxD7EXAzxyirYnNDLdDZoFdx14kivkvGc3579Jm36reTTvDgBnaO6vzyQ6chQmlsMoIkIQ2+bBDWBud1Va4pcCn8CPqxlh/fgtG8IPaPH8C5wk6/nZDv69jurV5QhtwE0x2iqOsj9Mx8B9/0EaUdiPfOYYDCi/q9jhWRuupMDEU0+CtX0sDFxv07T/K5niBPqN9+tQjgEc31NGCXFeMcCEuQBIc/BK4CO78u7EPYvl3yaEfK3vcb6qP1R2tI7vUjVDDUdKubsSrNjYKY1qBEa2P50SJoaXiksIoLiCwnxS6EBuBde87botNfdEWwYvF/R0/u5yCqhGeEOR2ynSeyXjt6ka7neyye8kryBSWE52y+RBgogrXPZ8E1yIHoHIFUM+AbJhE7lbMtt8ApL+xmZW7PwbjAO0fAVoXQOuiSP/ksIVdFZ0aulsamKUzwPZ/NYDMJRBPCxsBqLzqHyneXF6Ej9HlIFo7+pg+jUb3unRmGpstGkm6etOuDBGA5wCMefp1gTHcdZlvPBXlOslvYTp1cd8UjYLVd/J5awNrIOKLnIt9MD9qdrKrWCvA6ALm3QV9VrsPm60Q7+RHJHP+2hqfugo/MvI2H/mqr4b9tFnKSRY1Y5Ek80Nm/WIhr1ikKnxGz9TWXrokf9xwujfvcOTtNTWnxd0F37Y2W79tteBqZ4G5qLCuomw+nSr28QESCRVLTyYKILGJOPfcnaIFOsewhRdvv+rWa/Wih0vlbX6Zb75T5C0qNKVFvH1QL/vazSWgC2s6oWXXIuUxQelKiJbowuJDQViatLmLijg9CQBMg8WiPgiw3LEeYRmm5f+XdnvkDnxLLjMLxtvX74C3OlwPQqx4xwIdpPx38LrlDphiyWUWHWKAzzxurS/xTo+P5wGFak62ap1PVFFN4v/y+xuR39WnIO7lsWfwgVsK17wxrs9K8ltIKuhkw7f/6dhK6gQokFKhWX3urrjk/rnI0pgfpGMeuQIUaEM7+GF5q2iMkCaMQwxxOzcvU0eXbsnS9XknXvP7Gtw5dwPXlFu2ecvSHEZgNDsU6x/GdXBYXyOQjzZReSedeEPY6nEv9gJR4oBQJtFO6Kd0fwC6BO4LNHDeBujB6dSNcUQC9zIv2LnAzGk99bUDrdFY+9yGFQtEo0GQPNv6vS2drj4+1jHbv3aJSMUWP+QTZrmbNTjU8wyG/iXNNpskybLcJ3CiTF5Ir+JYzmJwE0mSVhlxbtbmvweB3ulB6Til5UuUZydpgiFVeobhU0WaBqpJ198d+/XeNRTZ9/1OPfG7+2hwzd5W3D+hmyjsRcUg/+Cavb++Vh2ls3L7zT/etOnHNxeerv313vzLVqPai4nJv+K1FC6040/4udw7sAb3laSg0XCkAAs0npBO6VJabS4Elk/U+D4gTXW+j0wnrMlqNamq4tMIYB87tE10i0FR3LZNhJsb7/R561btmes8YBCRkhYNByRtKd55mqTas9FYhJnbRGHuOh3M4QTdgQSqmgRxuzGdSvZGcbMxNQGk5C3ebLjoXIOFM4l+WKHmLTJwRv9E8GWJ6dYvf/FmEyEGr+gyrr1p5zrgkz0Cw2j94Hv8Jdx7dIVegBSNtgsqGsRQEYiIBoXwD0LNvQ5d7s5Z00QzwNhqZA0b+tMG1tQq5nd84uq8R0zPvX35G8uRaze4jcOHzz0w1+Q2BIRvf6J6Kgatnrbiem+CFvAxfkrndzD9MFPP1GWTUHclpASUkCNAQkpCCcCgDSUDAhDZ+CuEkgn8J7i9nMA7pA4lISappxILKfAeSAbIcSDuN2bJcfZILqeO5rLs0MnngSHYRdrHjmaz7JEsEPw51ZqDJDmUIOZIe34WaQeegNsJn1qz8AIpT3yCjyEih/xELkuJ0lEMYTLVCiWpo5oYMleMH6USyYJcD+uOe+kWKpn1Qns34iyYDjkSLvgnZXcgVQNeqINXr48m3iS7cjm8tedyY0f1QvTnHHdsrKby/+SSbPY8/NH6vpl/Esq3Ae4ZU1HC44KFiI9o7CEgab/RqHbj7s5KAg06s39ZP/zxI/mVuF/TbTSy+3Fb8If9/cv7+wt91yy8RfP1QXtW5RzQn7qIiZyuFM5QfJ5E9uVnqT85TanFx0lkP3ukBAMprvsRyi/C8NAJL1xbIIirSvnSj4O5netb4JxmNANHPssHAcHMHsFRgEug816gDBeMbdfiuRcghqYcm0+Xxx/5IAEtN3fqFF3LzAXqwoT0PN0OVTNqxo8sxMkd5Ig6k79Zk7VxxX6gMLOZFQgvpW2RrMW1D0BDihaXQ9wVRoBxPLfpknmkeMtoB/qM9cRc9IqmMD2XUmdZ7GSRKPUZvChf8BoykriM2MnKYbOHX8R7cLdNCxSFFVQqoYswnlWtlFS2mNkhswVpZiQW1J/UKFfipHGlUkM6UKBhMz1istELIHJLMSctu3ugzfaVSOjKvUgc/THK4Sdg2Wscz69leKIkkrwuuWiOe9yGYKQXRumkC3qbRcMwrvhjNXgdZk3RxAUEhuSPvn3nnd++U/3vlVOmrJzCD8JLxV1OHRjrZifbcFDOuRNTGqdgQm1tSNJ2OcQ04YiEXuxtII1ECSQRoQGYioEsgCfchB4ghAtw7FfJre4WZ9hkVi9MtjuWqtdNDlpMrfEG9fOT6q21okg+e4As38MfGquNt7oUws6Ysarj1/efE+yst86YUVNvDdts3Pv5c8m/aP0C+f8/Qb+IMnGq09BgwN01oIOAnAdagI8mBSrqk1gxTDUBOtk2ousEtBH2z4Ir2d3f6k8PXXVlt2qN9RODxRuoJT/v27wm09jRYVc/e++iyx2tyzJb/n3J0htXP87eSsQaf2Ly0s6Zmxela88REy1cf4273mI3iXNJ7KxrZibOm9xm6rl4fqy/t27smU8tOfdW2ucBzg2UfmOIVyLIl3kpYlwphDISTXJXsctmiDtN7fNV6zelgxwnWxsVr83Aj/S5ki1jL/a0GC6+2L6Um+aoddlNFuj+bJ8mH/iaLh8I0/U51NspIEfq0dohwyFXKgm4NggwQ4rRhCOUFtxxo8XnitT4cnGfT93IS8FaT85XE3H5LMY4zIEPL1hw443wz+1UmhTJyJGxZzw+wsKkKZgUiVtKOKMEb2AKHTv61FNc01PQFwKnvsZ/9pPA4RKTASWahmh+8MxwzHxKy74IRn5LGRjsPUUwTu64UYNY38caqd7HKucZ/tHnODtENw/2UfHRMaq1UUPDJQ0OKkWCeet5fYOhII1VRz8+/Elg5j4Gxur3J8o2PJ4rg+2d08T/fwEzSVbyZ9XPro95T477lRKqUSRXQnauHNsISAl27oWi6Fv9z48JMv8r/aMMj8onCP/DuDZOuN+GPPr/+p7bx+7JlbYdppcNhzKU/1Px5aiaGDn/s1iGMaBcleKUo/v9rcxkZj7DBEKOfrayytXNLYiUdBY+pleQXdnscKlQcpzuWluxsieeyuXIK6SdxozitWyGOV3vOHHjguyCQ6fpIYy2JwvrQEF/Qa9Pdf/QqOSqCiE/EE1/XIVKTc2tzWbHnimrEd+Vyz311Ml3P0GVTj7PD5aDnsvCvH36alEaPMePcMegXs7x8igTu4B9v7G9vTHvhCu/kzIdx+BxC0ay9zRSvoS0F2lIxI+X7klU63I40gLQ3w5ep5na+SFnba3z5D64zv+QtM4n4ffG3tq4aNHGRfxgrXPMim+5487abL7xhdseIRn1KDl+7aINixdv0OD+JSPwKf5+xoP6aiTeQIDVlIhMcL1H5R9PYXvprs3fv2bO7MOplCmweuiq2JRZ1zz+9a/v2PH1Hfz9236w+ZrPXvWfAxlj4NLLHpq3c/PQ3uvmvbrjG7fe+o2y/cLdtE6VUlXi0ASb1VLUBVSUWSU4HdvAraTyS8xzM8NxvxFkXV6pUVRiJwcgC5zEeht4rwcp7ki0k41G0qlQhG1Vzlq8alEmnFi58caB5Q9vn988MLhqyVlHvLEWjtQFeupdiocF/tkkOGPW2ibWaBTkeZ/dvPWazXfOnnvL6jkRXpi85sFzZt+55ZptW3bl1cCCHZPD06MhySha7UFzjcjbp8fOecFCirzAG/yVjBX6OFIaadSjQq1nNhyIe8tVbaaSdHlXIWKacMeuZA1uxS95zILhyrxAdsXTL6m7kNQlx2P9uZf2qhufePFFbpI6/OU0WcP99RrCsrwseVot5mtytpf6Y0gm9sdeyKnPQ7onyK4nXlR/rg7H95M1upzu89DH6pgUcikoiihJ6NJKmRxV1x+MJiOA3YwhDRQrWU0u/0rvq0VYXnyCwsLeTJYBq3dAtJDavuzyoVpzZ99Z0+a0uoiFH/xcqgDR7rUFeOrUn6Cywb8ZeNMbhLV5ugP9l0zv9UN5b5mFkjzxUcpPJCn3V402pRxtJd2GrnLdhtVk9ZSZh9W91fCSH5B7ofxPiWL+j3D/uwhBRdyAyozeZwvQzs79soi+BKSnafLviZCcfrpBpLyimfLfTyJtbyruIQKD01tUwJyKEo/ybaxkSNFUMdMkhQoJyRBQFhnUkDQSXhTM+3NmY0EDM7ffLIjqWEGt8lCO6mLia3PukFnghosJD5p5SIho/VDkzQfLE+IrYoJXkD19pdP7OwG/voIUtagiWiZ4PAFTHHlTVhRZ7dYmPar+NJ+8JhmR6DFK5DV1foHoLNO/pHrvZfmWZ15RQlwvoVDKhCWNK3CCch9lfFBuAqUgpFSShmNaPj+i5++WZfKeViJfW5HnUakVL4UCNVkA4+ETfIqx4B5xSaP2L1yn0zn2ltPn4+OqZGmwwEVCaCSqG53ldtL1oLGAhdMLd09MpCCF6tD6ZnAZBY9hDaYsP0jzZ0j5ZjKsF4i1UmLuhbJMCnYJPt5VwFNvmZawXjEvLJqIH8STonZjq7BZ8gKgR20C9MDFqJAX1H64QW2NEup6qgzLP8cvppL/NNTOBTCJABOHeWoXzLhw4Wuy7gaBtjKr9kgKq8ZlRYBS32Lpxc8vIhpNDTfyNXWybMJbn2RyQ5EmWc2QF9wmSZ0KYCE+cPuYO6b15Uotj2Kd4MItLS7gtFbkTdrFND6pvEZqv5Yv7jXAus7Pg7avo7KDot50NX3CPkP+Kps8J9/3mGQIteY/LGPC+L7872SPR2br5fy8MtKBMHedGuM28/MZmPJMrGgi3Gb1S+Si1/L/zrZwO9XH1ce/z7ZQ1WSoY/+pMb5FT4ua0Wm+Jf/298nFmChEQ+Ti71est4mq9VYI6RsymoRJKYidElT2FGnDTZvqtfhGAFTbeqEw68GqtfmbVa/1IFO1/jdWr/8BDRRtQh9XNjubEm4aWVpVonpTGR7PVGc+KJNoBIWF7kYi4gUV3r1U6723i6TxUl3n3/tM27aZfKb7THiHW9VzFSwHJ05VfK6Ar7kaB0XgPPE0BSkSFKsBUpaLihEWoA9wBt8qirh2VSOkZwXEwyrxZ5jyt2rJmSo9gX7cg6jsEUGJU9z9xJPOEM3uQQxKgkh35DNATnVyrmJ3mbCNyIB/yox4wH1bg2DwN7q9kov4pFqny8oSm3RQbGgJ1QQTs6ZMLilOVYJ9v6Wha3HcJ9jddsXp9YhGUXLXt/qMDnvLpPNTXfNa60z5/yjXQOMq+lNmwh5egpYrdfZQZV9rI47xlRkuyTjpzsmCBSWNkAXVoK8sgYWqQJWbo1RLo6QH0YW6pxqfCnRgkd+RiFjUQUQ7poIaYoakgXxwFd9BuuI38H1xBxXSFb/pBDIKQFn7YB3dB36l7sG1FLaKiBdp1KxLvfswap/30lnVESgNnvjbUoT6w9N+Xoio0qcYOIM+heg940YimsucQVvli9NEcft2UZwGQwLuilj1fFr1i3NP94X+PE7Hpvtj6lBJfJ4R6NvWiaL6MgzWHxiN66DExa+dAdAbMYX6HVF8A+7rjEZIXAVbDe7PVI9rmN69JOLV1DOSvRPxWNPZBZf/Nf+Ny65BhYxxxV+77XJ2wfQ389/IQPgajXbwMsuAz/0IaQcXJavKbRqR2IqyZruXjVC2+hdee/5vdnYOedpmVtR3NGXldxSzDSIiBVpkGb9by89UpEPKrSLZmyFDzMab/wXl2CNe7s/qCtTvWgG5kpBmCBlSzDS/r8N4uwBwohRW63JTS1y32f0TQsPfXVGEHQrV8/NCfiOUVirYcBbIeA2+iF68rQIo3B/S628vYESr79ehzS7Q9LEL9UXmik9XVHb1yBO3Ngvt5935+k1efkV51mzzrM0LL3/20avnwMeKuWyOUZg2TasSqZ+KcZQiOn1Iu2Vh497ALUVZiCKt/gh6IvTIj1ZLRjWAkpHKOKovNwp00eqPROiAbiNEKieXwMLcXhVJ1/uzmLP4tfxaHR59cBdJVG1kTAgl9ze9QKUEQ946Hkb+okJ5JRDyf54Axur1D+WS49cLr0tTPEu7UmXrxcSr3XNvumv4yXzInXKH4F7Tc7p17Zt+t/qW2+93k063X7VW6lALxTY7i1nBXMxcxmzQbabxz+tJo+wijYaIGMNS8AoSMgAPt84DdHOoMPfjXhF+kuH1tZvuFQrRCN07xGcXRX9MYxYchDe5BcHj+Z4i+42WyPc8Xofi7bbZJN5nJLJ5qr6IqRtzqNlM17SpFsnkEyTWoABEjz4JXOQvzWYuwdnV5LNGOwTM5v9r4RpQ8ZXsYodks3o31JBlzbYtNotisnm22MxiwGFXam5oN1n0TA/hRvshvTSDwHff4nNzRo9Dum6PaJbMXzDz+x+Fkj4L4bFNBb1asqsgH7Dyh4DvbkPtf5yMDKzEwyoaESMSNS9P9gJVA3/RTlwoMwZvxECFWxIPNw9gi01nOHjP32esZTtmXHnxvZd8ZtakqQ7ekajbXetpNa6ocTVxJtY+uSe69OLz77zh5bDR3xjZMzUz6fxrz1nqrZGcHQHfPVefN+fiK86LeXj+Sc5lPKy+k/vCUI/DaLFYCWHr6nbXuILTIsb5imNKY/rCm28fSMxPhkN1XbNMNZGuqwOBhtTSxWuTk6bw0ZaG86b1hKddePOKuBvmiguYBn4T/yOqOyGRBt7bKUI1GjioBC8aUKwF7Q319UgcmtFGIzCJGBqwQij0ynDsfdFGc3TS3BlNfJ25xmzniMkpXXTPvCaD3ZaZvyzjmZdudBostmhb0ORZNN2sJBeed1HXkrUsywueQH+L0eCPxmsa5ZpgRJSDZ11yDv+jmbd86vxZfc1WcZJ3UkMq1BOOOVtvu/+pB+en186d3GTwWAw2jheaJs09/+LNfZft37DALyrNj1wABMuUKbODyTVnT/KYbJ3Tpq8IrNh92dkxOj5P/YpZx4/ycyiVcDYdn4JbEoKdQi9054iBKsygLW46FRGxAb0NPNCm8BSNCPjoKcj6EAus4SuP3rB+cV99/eTF6294dA8+TK6v74MHVpYNRt/I30e8QGTOOdfGWzzxcy+87a7bLjw37rHw1nPzp0KyyRSeZO+QQhInt3dYgvycjrPOv+T8s1rptaP84VeywdWX2T4ysr0/7TLIs6+x9zib56ye1dM9e/XsZmePY3NDs9zlnNVt4+WgHJbbz3Livg4P9WWgviOMm4kCRT6I8vw0NbUUEnFvOuFKoxQW1gTsvFirsF5pb7qTUCx4i7VmtToveaDxvK9uOaedVvPRpVOnNz0Q6bry7uiSdQ8t7Vy4JQKVS+XPplV2ts4bvCwZu+KzgITtxepaPRzWdpv74muvv6RO0SorX6cu/dqKn/XWnrtp/Zragz13DUCl5myiFW2Ycvb0PtsXnU+tx8pvLFbUspLX68mdegwmOif/NPDONajTGoUh6tU56HBJCTBASVvNUB5VIiKpc9kd7kludodSFz7xQbiOmMk5dOYk56gzL6uaf7N8a6MQOHm0ae6snZpFDfuT3/jdYzjzwkXXIVHoXNuCfQslQZqBZjTsoHMqrkE4jaYdgkGz2ATOgB3cPkSukD01DnV3ttb1wx+6arPqbkcNAHoFPzKUUQ+qL0k97pjbZv1I/egC9zTFbrrlFpNdmea+gIgfWW3wqkcis8ky5FAcRd1If5nNZrl2FFpungc8wpoCl1BpQV/ScS+zjlASyUTVv/AJ46gkJI4bHX4lTnloctxPZE1ckS3+jG2fKIjkQFyzuo8jvYQG1OrGvJPSTu/nSp9PHNTl4z5hK/8gtXVKF6gEKiglgcKiRlCESsQCV5QIlKWKpr34lt/wkSx/JCmP5/cBKQfl/5gd+rOS/+p91/+YCg5CXK2W4M9fu+/6xxX+vnelVuldIDCG0VQTpU9Dw4pRfei+6zWx0MLie0gPbyrkmRU7OwT16JGeyXLHqOLqAfVN1GPlBzWtFNzj0TRTCjogtP1NjIvu5habN5Aoa1k66wGpqriVetJgiGdwDZtKhnN0y4n9sXYnsqGmZfDSR15+5NLBlhoDaedEm7sxmpqRija6ZEEg2EAnTiAC8IrmFbGz1q08P9PSkjl/5bqzYqT9hMmptEXDgTqP3Wiye+sD4Wir4jCeoHbbp5hRfpB7BakUIppIlPCD30dR1GtslDz8OsqbXmejFC/v8wu5X2myq7SJ8Avzv9DFUJySf5uNvq4+Ti7W9D/OZrLChdwxmPNiBRqVjnpK/aGxRCDspVYKAW9AN1JANoo8wP4BJUlGqdgw6m1qPQ2QW3+OfU5/ieLS/NuKpDU3uf8bcAXyBal5jMR2NEAbPAZt0K3hvxHBEDlUxfIGcD+N2gNSNx36nfqlAYow0puatNpRz0e4W2oahKzQHsjf2c16ad/3t2KTtPobnX6D8C8pd0MDP+Kx7wnXqGGlLQcvikMErm6TmfsuxJXbSAxqNjOogJLQBLiKEHAE+JGTS3JoEhTrz8/CB+5YlupJ58aOat8Kv4JvregxwcU5Cp8GFAFm1FyOfto6GS2m1NGTS6CPNKkbsTdCBlnN9onMho55BX8IJZtEQ35lk+htwN5A0V3RCPoD/yXAcv6pAtbZczRUA64JmcUf4q7Q89ZHLeJVZ5D1Ps/t+0iCT3AHVtZC7JDCXfR7OSb/Xja5H3zQbZL1B+ULX1BMTEk3AseSpmnKEK4T9ekMIidUCRQFfcbj7z8gNLvzF7mbhQN8h6ZbRset+nQWdS/ZX3k7WpS8P9sfo0iGS64wV516pOhjI6TZ2dApgI5+LhxywYoWxKUrykKJsIoDsR4mSrCTg0egMPnLW/3Q5Nn8BZEuzqEI7HK3n0+zFmuO3TtWQ5WJoG9YqCD6Gc32SxnbnVPfsxvrFXK2dILl7bLthDp6glhcsfp4bYvbSmj/mQ94uBTw0E73x2jbNRCvC6VL6GCFDwU7eWQDcC5FY5s0slieRDwtAbRsbLXbaXAuu14e2OJw1dc6jQ3ZdY8v7rv2/BWZLqvFWVvvcmwZkK9f5jS4muO9yR5res4kfkRxhV03L1RfPOiPtYi8pd7jNEsOpyTwxpaY/yCZu/Amd5Or9uS3DYaeqVOhH7gZN/8I/wi1fEuLXvyNivibjuKvN+1Nc01HF/3h+ef/sOhox8MPd5SFucPjorQwXT+ytA8EmA5mamHNFDVhBI5pjZbQpugBNkO8MvRub8KVDKST1Wag7D3xlin1ZF7LFP/79nbvCXFOY+PUjrT7/otsPXXZ4exdPzuhZuL5LUXVAn7k7PbhG89uz3b41X01gbjP1xwlu5rrvvf9+pbs6E/Vu7Nk642/PYRaAiUBdrmO6CDTBLPQFA1ur0uXoBR1INDMkypKpoTqnSMx5GiEdTEaSHLs0Alvu/19/5QW9Rv1U1ridT22i+53pzumbs+XFFXYC++CGsTj5JUT/GCgRt3n78i2n71FHG4/u6X++9+raya7os3ZbDmgWfXun44e+u2NZKuGZ0HiF8M4TlMPR+EU6rPKRJ8wOU2RFUFLex3egEsz3YqEAq0cqhAAW19dBZIlVzR61tuIdTnpXH7l+uXrbjPUyep+8cl6aXKWhPHpDcXl9KiTWDNr4mBQc8Tq+NzK/OKSbsfl79o9G20R+brBXYvUg0rLHhtrc4TN81TTOWSZ0gL1ZVlOYH2ery/7XVUjFMbzYpg7UswcqJPQwBd0LKLabJ8IaCr2otcjSkIrGwootKECaUd4XH1+SdazRrfddkBU98t1htvWrbjqSqjaCguxrffM/5zDCpBALUycmajhd+R6ww4SWafuZ5eU+tPid4lgd3gt+b/Y9rQoZNmiXYPXyRHbRs8zX/f4WIFjWZJtUdSD55AP3xtXH+ZipC0EqdBGDA4CoYEU6gRLGPU11QhkLTBiEYPiqOeQgwTCl9aok1Qr5pFf71qEeNxjy/8F0GoqYPv75Yh9j3x4DuJ+uEzHRpAq2lMqb+qfTdiq6kGtzfOWsv0c7lSeMXDHBDe1MT+LUgx0Pg/p87u2UicdIvqQi8DkxhcUwUXCedMpb4NQjwY3npTmgsURJavLwCRyEcN2HfWsDVGfv/u9ZUWUx+PYFueUKwaNvbtu+Xps3eVWbN1GcgVrdMnWJ7WmJz9SD66EBidag0NF1Ukep0t5A7sFCWdhzvYwHv6L/BehXuHqfaBwBEU7hfVLcXvS4VQv+T/vaSIl7cbeMc7ekv9i8S3e1L5xxpvMGcu1EYPbKyCiijjGXcDKckm43PqU2qNWlXusZMiqF82cuVzolUHN9NNR0HZPxFPV9V0wLtvq+k4DqOwVWDlzuQLVdqFiP08cRX7aRlBVfR8cb55bWe5LExnlcsDp1vAP8Q9BucPMk1Ulh4GnN0SAdxcNHv3q9ohx1Ati4S/tkWjIDe3hQdkUGrGRaFBiUdiTSkI41UkMuuQHP+EaSQYlPQTFWJF03BNPpTu5KFAdkWgDukzsZKMG0Q1TAQQglScOaP/dsZ8+fP75D/9Uu5Gs3FY/2SxPld0DHOciXI9gqjcEidXjE+3BLosy0OcX3T7O5g65ROGyzQ2BZs7WbZVnO5ydLe32hMwTQ4wnnKXW6XW5LAa7oaXOIHoUl0FgLQLH2by8wSTWeAx2Y5PDazK3BqZbeJZwXGPaYhX87ZNszoDdaRxotXO1nNlpdvAPFWHDm8PqEE0sZxDEqGzxisFNnuCWetPcGrObN0p23tTZwMuRVodSV8+LTrOV3eRvzjQZiSjaLYS1WEJe0kNsJlZu9LFun7++wW4gRDRbaxw2nrOGm+xOj9cmtbp9ZqeTM1m8UXfQQCSTVSQox6pvtjot/FpHvIUjJovFEoYvHYV9C5Y/xN9OfcalvII37UEhTbTg/AQIaPb4Vz6j5u8/aViycMod/fkDcpu8QZbZoeBi/vbzP3XPsZvOubMtaPHkD9jt6+U2O7vqU/9C9SMvgrXpQNG/E0oJxun+CiElUa0IKQSUwERxOntKSV7ekcuh9VBZBBo3VUcB58ofKBHCwLyf9qFosz9Ibf8dGqwaBMjRig4SGOZ2UkWI7UiO9OfUPdxOYFApUZyfpY7mgEc5rtNGGk2H1lPhAk1Hp/VAMqQEHEUfEYkkUQq1JMdzsX7kklRrTrUi1wMcDjmu1YYfATj7Y+pGpPEBXuoQIj8rR9mgCl4C9yqmF7xnVWxGVniNqtpVmXBvQ6iwni5YQ8a1jYrXtc2J13HvgkvqWxuva1sbr+P2S5ceKGyBwDv2DbrToe1u6BkAJV7xnVLUaq0sJB8pFqcUIPi3yuwxi4JuLr+P30f3OkPQ72aO0xYo3/EsmO3QO5qEF8S0qQH0UsKXv0brnl9+8M7jF174+DsfvPOl1au/RL5/9DsbNnwHL2pHR1NTRxMZhJtHktOOxLxErPF6YlLvpC9YP73x+4ofw+3xVdrHcDE0dQQCmCRgvt9b35xINDf1CDcRSfJ+pYl+Sf8YcurfmXP5F/kj6J82jNsrkWiEuhVlgFfyNkB3S5MUzLhoNiwSCYcxQ7Ui4J0Xh7fmqRbaPa1tzujxkBRlsEHy0/OM4pYLPb7g9O6BQJN6l9zQ0OGyCaZz0vMTbHOzXfQ7a2tsterTcqxeInODoemdktw+1SbVhKwtW9ffe8VKadK0OVuC3bWzyKm5LeddsWTeorWyY9IMtUFutdu5g+Rn533qkocdvLs2HmhU75br/MmWtD8zA3OP2t1ea636jEzqYxJZGAwFiDEd61oTsrRuW3/3pYNi3bS+Rd+GjOfVpAPNd6y64Gsz1GaZleWIPoYL/v9mTeQBENVEguiF1aC4YeXxFETw6QyPfn0m9g8IrMFAvKM1EI11DARnbqibHk/Iojy5rSdgCyZi06y8sS024PeuO4MfwQ5Y9yKRZCqyYaF30vzeHlmUprR21tR0t0yz8KZY66zWuGvxVQB/36kP+K38t2Hu6NQ9SFJfw0AdpqPEK2qTMpf2VCqJwqPoJezTL824b8akoL+x03nhh+oNo5e77psxg9Q5LzebIKD+fsY34f2MtB9fk9v5b8PT6tYrgv4kRPwd0q9z3gdJSJ0653KjCYPwCaR5aUY63eW48O/kdo33yxX9wCiMv2QTrk8eGSI6Ag6moG9t2P/F7GRNlDjl0gw7pJ5aOXXqyqn8SENnXBmbSwUYLyqJjv3UmY1nKr4t80no0faXsaIEiF/BRaIBnItSce4OUif7W6Vm9T9H1X9Vj71BEm+RdmIJQST/ZfVdudUvh9S/qqNvqT98g9SQ3lHibZY0mRVHooyDN/FHmTgzjdozKw28NwQ0hwN6BCoPKaEk3YtKwNhwRLXuk076CGoZNXDQcRwZvreTZY9EZi+d0s4+ztv8iei04JQl6ZbDD2eHV7X4uHuFVfPrOmcs6m6Kr7hssr+1VZFcEZ/PdJkn1hOs8SXS/NFFgqt94PIZzZ3tdaL6Q5vo6piSzdy737pwsX1VyxUrF15iJ4uNkq+rbyg1Z+O8VsNC1UmcvORPRfxtPrfRwL2p/oA1eZp6Z/aGffoewaXcA/xBlKlQLfhQL/oPgBGP3qsA7IQS8qDVNswHKRSheDUvA3Q7MZoRcJMxlEygujn1QdyzfPfq3dEp/bXh5e5YXW2Ngfvza0ZF6UgFL/E0fTq4LBlvTE2qb/KuuzYSXVnjTfM1osvqMHVbm9950quIZlbqaL6YP7jk3kUtA0GnX2nvq53f3WoSsvEdDRnULgo2fN7lNZJgI8/VWi33c3bBZnGY05+dm+3qc7fNmj4YGKLj2nfqFP+g7jdDlxEV5XsJQZP6hYrS1l0VQr4c69Xueixp90gnZPmE5OF22j+SYEWHlZ0K/Hgsh/Ztsbh6h2DNRlvv6jJh9XaJaHCZDiUDKNTMkvb8vsqCyf3ZNdSmO0fa0Y4baJTtpbKzuVzeeSI7fCKr2Z0WypapnXJ4gnoWy3PoUIlIQ1TXdqhQJIXp9Wx5fYdpeWh2TY5D+YVyKd0jw3iumwi/BC3cEy4o83QlZnW79MrCgCjbhWXBlRZVVZZv4rIKpXC01HFlHdHLoeWVl6UVc/J5uGm6CViW5mulYMk+HqNYr0AyUPivLg2oMs2MPqtuhHyRyiwvNJej1Br+fcLyoAyu8D9B7bgmzUqfFobF5nKnK4+t8MPJkI/xHUNWk117jugWF+xazTAALQn6+UE9lhoI5ApGA/iuJOsrlNP28SVVuBVajXmircLel46w2bJS1Q0Ft0KDuikDFL/3pYrid1Q4FvofwRIo4R9h2ftSwc6jHAMqLcCql8YPHtlzGoByNXYN6v8hXnRaOhUvx0sVLCexwupGDR4NOYC7PePa5keIPACnuAdD7dEadRuTIiS6Lb7uskb381My5yjzF8lGCjBRqdwrWJCagfB3yCy7XT1i92hbcZ5Ci1FJkgYMDf6n+jspIsHFjJrTOdzSMuOa9DbDcj/nH9N9bIoGVgzHPWIQuFuYtaMRaq8eCKI0gEF6lPOZjBz3EEvaaxwSUT9U/8JbJZPJJLBLolH1La/RbF9AbC8JJjv/mMnssKjLRBJyqj9QXxNko0Ux/X79epfiXkm6fmKwF/en1HLc6LxloXWKvGa5rVCVL83VuiPcDEX/K5pTXOxHfx6HHB0t2FI0qI2rCZFTrvPWU67zVuS/kTsLnc7IKhFg30e4FOkqNSfH5PtkmUy6Cpiv/36k2sbqCeCFNa+URpoY0sZoYmCgCr3qgZz6s8I0gP1bYiR+D79H56NOz0EVWCTy2/fffvSCCx59W7uRV9995eqrX8GLesOXNm360iZ+T/El3uZqL+FyzSZ8XxpTiI/G0nkT4zznFZ0t4ipMz5v4q9ssqbdKUZt6u82knPCrt6PZwsnn0XySVnyPR1ZXAn72yx48bWJsu7apnI3Hy8bygUK5Js32qcytapqgmn95uexccj205vGgJ+euOeG2SORmKZr/qKzcx9SFctMJdwMUFZDJITs7dnOp1EKZCxg304Cevyfya+vlKqv6aXK1qIj3imL+L6hL+yvUlFfE0VKZ7E8gBY3M/8VoJCFgizH1W6VyC76nH6b7jiibYVxUmVIEspry/LgZIlCeP11Z4zs/AwvVwtGFEut5S1JY4lfyT0N/evOLo+rUEgjcqc9IkGpQbv3iW7Co5b+KgjvpzYdH85PLcc4X21ouwEGl/S4qnUAvoSlXUUhR1eKr2VWFTB+GMl6FsiQsVD1R3urlAAIoSn7JQkmiVVCHSpCwDH/qPepXQ0Db77CJOAImohB+RPWr31ev5g/kE+zTa4lbvZo8xdWPffQu9yJTPCNB66s+zXoJt/0L6hSoCuBIoK8fnBGG87OoRckJpLqyWe4YbpGi50g0+3I3UD85Oa0fzubfoXxPLbW3FDWzigmyJeM0tQkax7PqTy80+UxfUHPlBZIRVNQ+v0xRm8REKPoLmNr0+Uo48v9GFbXPKylqQ2IKm00QddgyWGMROCTxdLB9nCY8P7j2DjlsV/+mfr0C0r/NkeXbbpPlOTBBwT0mVz1zx9S/wJecBF9Wgv3p032iP2v4VSgfgW2G+HUEdEXU6iq4CtpLJfIN9XQG8dwa1VoO8XC2SrPDDyCOQptXgbcPvlAgBfxBoGwftQKeKFrNTASPt3pGGqDt/QRasn2kri+H6L80MJRsmVYJrAKyDItpJUy3/15WYIJqcJ9Q5N/LFJ4c3dc1URpWl9hW6mu50MUIelg4ucTPf15zs5DFo1c0VSp1tKB9jkwIyuM45kb+IP8gHed+6jO3v0KbIknzLy636E8KPTdCuUpB0wLo9JKnAO6pv0vS31EtBha/fJemkgLVVnd8KCk4qBTpQ5m7FbifBKrPJcq0pZAFVG/XbOFz+Tcq2MLrcmV28Nmi/OHskh82bau0k8eWCaPijQPWQ5lUvslwVCfHkXBMIehqUgtDNLeauH1huvZTbYmw+luPjyWoNGEuxRLR7LK5fSyXFUyK7PURQv2v8D3XOt2NJ6liBbmPGOsakw1kbeOs+31Wm5qpH+iJWSzqdPr2O7zc2TmtnrzCig6bBd/vgQmzOlz0STWIlmZEQfupogOZFHUZ7EkUnMn0RrpIMqAgHRJAOjIJ3yGw1I/MAp9q9S3Q/clADNm1wEeO+xbwg5OIYHZLY3ehG5lJk2xhco+6JWybpEVz2wrR6hZyD0QXZbeDVB+onmlimpkWprdAs4WEZDSQppsDlcdCBJJESIYFuAtUnC4GIF2C3Uu2Kv7L1bdz6FxtqxpG4TqQOqOUNAJ2HLvPWA2GgDy4O4vaDrtyl6P+1fAll+SyFcQ28GHqh7fvvf37udylf0fNwhzgz87Y+cf5x9GnF6ygHu18sAbipWeF0YPBgp2GaKeQduxxdEr3SgbH1kvH7tvqSLhedomOvZyts2dw8acu3dY/f+ucuMtCuP/e4zC4XnH3OLZ8ZuxTWxy8dJfU5dhDeKPSlJy5pn/+7u3XrJhmr9C5CuleGflGQocKnlAUaRKp0BAHV0ZwUt9VCqk6zYOgRIuMfePJzdmBdpPJ7/6B23+f+sp9NMDZevovvfYHG5dGPISQq1DojqNckchVrCcCYz/Q0hI0m3NKDRfkgsrnamo+p0CAq1FyvC3a3Nak/s5VX282x9Ufy3E39VAx6o7LpCvO2wK+ch9jNqpJCutcIOooKnYWtDK8gTRVYygRQfwgzKM5+jP2jOZdx3r32Py7rQUPOzAnoRs95NvRAR0qLGU11Taqu1bUYSzMcWjMEir067JQQHfIrLBHsrgv00/Wavd8HRLMEEYFSW3HCSNQehnrHztKqHcDyo4VfZ6gPKCR+gufwA8GegxUEo4A+gd0BASHiH6jYMLIsUdQJTs/C641KN4oCHWolCMLlMfIdtWKScjx7SM5LD9HnfmhrGI0S139UWfUnxgOXdJFW+AMcGjKr6eHAttHF5sUoeArYKDcxMSYcKA/xUDhPiEOEAPafSIUFArN0r24ynI91EPARDXvIDYyvqZaWeroBOUABQA/E+DXC7PWafDLQY2oiwpUEyj4RQtVlUp1GrM7In2p2A7VuiOW6otMiGOo5Mrp05ejVuTy6dNX/k/7mybZQ0nUmfrbx3U4KueDnlHm5wdh8FFeKnoaKKh/TK18StOPhwG9Xo5mqXAxvw/79YQwwDR+nAKQQ4izVXioB84qcppWB7IqjU45z4CE17OvF1Dw+oTFqxtz8dxwtogBnF9MjIl/in+K8s3hM9laIn0TiCbTAXL0T798bPXqx36p3chrv0O+GC9Xaj48Ecv8U8UEeBvUEsDlTepiU5OvlpeNGvpnKF0RvUooWhIjnx6GeBapXCQYTw9DNg6/OC3gZjp76oNTj9Kz6Jqobxb9NDqc08vcKReOpcsQV2K8InXFaXW3aI6Ofr1k48rp7CX7rx+v1UKPsfvzQU0Kc83i2VdILmd2/yX55zT9luN2+Cu4nKfwPcK/CvDVU+pHh8+LaldIf1fA5h3ndT6Fln9/W/9Ce1vndfvJtnPVO2xhm3qbafHVCN1X363UXHq9xuVD8OSD29Z8pZ5cZrern9cAdGW/uib/ud+VK0L9a42r6C90kL8KzxwLQw9NkIQJL0ASU8M+VG0KsUdgdvpgP/6NqqP0/gHZFUfGEijZLHpiIgvV5/Bltrj8Qd7XQd5p4P+7tJo30NMO6VGBwahSPMYiaaBYoLY6uEnciyhhh1Z/vvacG/rjpsvnpzs0B1Id6fmX8119l88XnOxe/uGrzzHcdu7UtY3+2vmXN5zUyj3ZcPl8p1sZSs6/nGXtwrV7Ka0XZdz83fwjjINpZWYw85lL8BRK4nGyIir2RiOsEyipuEcIakpGjWgBjLiHWOgj0Yi34gW1kKPxHt2Na5q+lwg1RdRSpFDNzosb44YJXnAfoEOpZW//6u1lhYA6leevezbI26zNHO811M2dc5HFxpk4i1jPC0s21/BWW5DnPQbn2X1WK43/aM2n18DfSoybbNHijFpamzXI31eRibGUOxSu/lT96YZlq1Yt20DaSBuG6knw2eusHs5EPBfNmVvHKdaQzcDfz9ZsXmLDWGXy2U5OsYSsIn8CS12jQIyD12KKqZrLPy7mSPdICmd6WGHG8NDZkkHuE4h9TU8FpmUO/VjC/EinToFyoNDz2p9XD6g78WgQdPG7Z3R0T/Z5dTM9lsL8Ktek7szl2L+gQwGgwkZHc2g5Su7NvVqwGy2Ua4KSXUwt1X4PaM5paaEu6jQ5zVFyNabxvUksVt2T/4VeamYPlLtffdQsk+2sUTY/zDXl/05W53/Bz9UK3p7LjapZ2ZxOm+UlZXrL3HHGqO8+wVroDaCTTnTxitMxmiAAYQzVJQH+nj3oIHnPaN6Zq6sNSLjBl8tKgVr2mj/9CWi9dnKca8rBQBsd5R1tzVlgrl5pbnPw6kZclCr2CHxMnHohLz+3KRQokzALyeIKFU1TNCiayJdoHvDYe7K6mZLm8S3uJ9dojuaJ62/qN/tjQxnSnhnKPw+LNrLi8ZKyJ3x1YhiI1aNAtP6NzCGzYv3DmaGh/LvQZnt0evgIhTFV0kE/PYxAnOHhCQUZdCWY5JWJwMzlAGl1mpNbDU7yyGnhRMILsYhH3VRAijrPcBU8/Cj1Y9NY6cnGVW0CjTLaz7E3epvaT/LtTV72Rs+0WVVmd0dz/MGTI5F0OsIviaqDlbbO5X6xT3PeXbXHRtf/z+fdka+eKPr8KF7IF4vBsT9MFPuPJMBTBMq9hQxXelQ+bewnf18ap4Ib+mSMrtDU5zqlD8QANa5MBGh/OwOvSDfcV2d66mfEWsbGWmIz6nsyZDWQSmqmxDneYyvjHPmRXHZxeueyRGLZzvRioKnGto9nIPkibAJA16adcOZRQr1iAP3bUyBR7T4RgAWTKxhkCYFwshq+7iV9r0whk50cmRcTg4fy5x4OmmNkHndIA2+YuMbmE9dwGYB4KFTsvnDE6Ah47r/fE3AYI+oXADpkdlENcZ8OZEEf8FFGZNxMs6ZLpG3SUFLL7Q2kcFU/A/Jsw+vWDa/7emewLaoeibaF1B9qUNnuqWK3+UfXYVL1v/omD15xxeDkPnXTOKSVcCbDGtOu0YQNpGAP7U1HU58UrqGu8xIbHtkQ3LVhb7Dx46ET3Ffcm1q0YcOizNmf3bC3VjWfAcpSv3MyTlgJ23FHQgmgvk+gk8pL0mcCDOn08MDAQlf+/SlTZ1z12fnqntOhbOTL9/ZdevbAPN+yby1f/uUtC/ixm8ZBo59LTXEW060hGrTDplNprWd58fwB/b/E27BdS/s7U+rGVCeQ46nzaw9QccnmZerGZZs3Yw9aVHt+Kh6HN4ti6lxIhT/wahnZtWwzlY9QHQ2c79C+dxzvVDKy8GqKWQERO9YAKbpsDUTLdWV5dE8PVPjvj9pqw7ah/PFVtkit7aj6G5xY9mfJrCz1j1e0BcnPol4UjtrCdbahIVtd2HaURujnFJR8CuOuUUfhrGhgKKgjCYNSvCc1WKlEp8wHUaAYynFNyzZn+2MnYv36dbMDBTonl/T/ma5IKAyEGz+4eRnVtaX6tss2o34u8mWorFtuFgm4A6qK/yp/gLEBVat5WnPDdKA574ubuFJ/IUfZ/Y2Nt6mN+ZNNTSTaeI56gKwkXerTe9DDHUw8/H35FY3nNN7GGuBKWhrV9ep+0k1WjNWVaHkW1yA+QHWNu8rtBw2a5YXuE40rs7/GA+j09V3hA98yRnFPOGr8ltGlsFdD/7tRce3LH6Trcneuiy7K7J3khKu+3qUaXPWaX7T6/Kfj9BX2eZq2XAcZT79u1ClJzUtHUqfqSMWBcZS43Ena0cUGLgpkKxB1QM+0Fxz10wgg6r5rltnFpH05pepUq3Y2HfYqeKRntmUFNz+XmcOs1H31U6cC6RTVLfCg7RNBF1UF2/wBgu0fFQtPEU1sSg3VcNsR7dWq3af87tUFn1l3ltXpaJxpNvtcZkH2WmMst3JqRpxUH+WC0E1qOGtP66s1MYv+VLu8/XFXvV/ZbunYYBeVN64ls0ur6NzpV9xzlmQwB5qC4Tq70WC0tk8dWJXeHvkD0h9zJOM0vD86/1NJMaIAolctvlByferCsqOKDKceOfUu1PsmoFCamV5mCrMUOCi6V6FJosMF22AcrKJgQDVhfYh6tepp/lYgvnCEAbJQ1L0rOpajEmRcasMiPfxhgGoVo4rwreQpV6fUJHH2e8fa1s2c13Apl1b89a58ozdoap2sjgLN9uISl7P1DrulyeIkt0zr6JjWocoPOZsaXPb6jtqBblsgsaRre2xHi4nELm0MhG1+x1SXwLpFi53b+aHRYo/IrbZtuWAKu5cSEXfybnnmUCaXGTpQr0xK2O2WWY76f+nAjNVf7nCZHU5XqIkTnpt6VtvsFlPXg1031g/VRdpkkyVpD7jnmax88QwDvg/66NnMRdRXTcGTmQc3cuINwN5IQqi0yzb+YFVHuVqI5s4ADfg5oE4ybDLd28mFSFmYvRoomsWXEdLU2Wl3GJy93ZNb/d5gqmNaqJZSO1l6PVRy0nZIj/45EetjLguh1rLqR+SK0hO6NrsqcNX8zoUdjQYDJ7tb4os6+i+Y0qpY2AWlnLRDWdGFTfGY1gV0zNAtJ7pdo24se0D88AwLY/gZmE9iuP4V5v7CSR/RThaHLh+UeBkXwU6BC7lGOevK65udTv+tS/PfW7qj3ljTcj3b9OkbV85t8xsMj7Ddj7DGpthZKwKPvso/c/1K9aLE12fMWLV1y1D9ua8lyJdWXr/bG+noCFutf/mLILe39ITUV4igr3876fpX5g2zeB52sWnIL4fXHlgeUzOx5QfIvJQyrKQE9wHUqVq+PEaOrz0wVvNbJZVSfsuMzxN4l9PkedFzw9V5Dj+nzpgoT4ZxCxJfC5RWLc74YVHxKlExCYt0JAOMatREhHBSCAtSfod6x6Ls8HCWECLwXZ9nd5Dz1T24JUdWs6fU3++fcnT49Qe+kBs+wdsMZgPXMp3U5S958snPP/EE7bvkOPCuTUDTUQ/UzirLhML9yPahoe1D5Fj5jWsaoveyP00PehdUAHk/seDVWsvDWXXXsyn/4wfpXc2V3/Qxli3jl/5hj/83avSCfpTNxOEKLmTjxOEKuxgNlsQn0xgct724mhynupNW1Ph6o3RYS3/+2TJrzLlkFz+ip3qCHKf6eqW02QJLjBYuuj4sobhCWqa/YHGEHpcnumuWSOhxeaL7sOakNR6vvmo+YcfFA8UFXEPZf9UjyudIOyNwx/i90DdsujS/FX2UAwvWSVK4NxaMhAGw3oowp/uc8CTi7D2rBgZWwb/60faR7SPsEbjkXy4G0XaqhXPwe2cePjxjxuHD6ssQuR1fq6PF0E+o2t1nePTn8TUmxz/A3crMoCc7egESuoTHYc7mYdg6etORoOhR7BBGD+qJopELrl4S6cJNRtEAsLP/OdvnJq0Wo0GolY2Et9VFB2Kf+4bZvVyxfOMz3WdFfSIryj6DwWghre7aQbdiDrkTL3A3vNDuDpk93HqXwam+bWmUJZfNn5ozKV5Pmmq8PF/jVY+2Tlk2M2RzSXKjmbQ4RZcQavEYrN/9rlXwtIQqzxQNMzPPfHYLvuPoO9TbT8bpGw5CQPGd+SyX/Cyf0Vxjd2R9NmsunnXYa8xGHzn+sSfM5J0y0DZEXWWxkXjcR75KBLNLHi7XvX2G8VOrf4Ykg0AMdBESIpo7MgAfyakA6rkqpI6UjNs0px7cMV+D5BF49Tez1VGnYmq0WIijp985m4Sn2gJR9b07riPPFo97OYbUZbxJCpot7H/lpZBicglCPN7WOfJkcHqc3ElWqvvz/1E6bIQrG+tz6WkM1SM9FBTR7FSs8KyBBytSmNEoquJNFN5EQyTiCrnKDx1h58yxCepPHU5nxGoxEQeeOZi2m80DxNxncVhr6BmEfUarxejw+WSiHhWk19bSY7aKR5MsteblJpfTLtjimBouXsm3d3djjYM+wEW0El9dM/ueVRWIsXwe43R7SgbVZqrnqoJ1X/kuF7pcgf8duv4q6vayV5U9zMV91GxO59UUjW8rHV6u799WzKMT7umRCXbYUKM+foaCcwgaoqZUtmodV3p+X7akb4dnU9B9La38RPFUG2SCC90tVA4XwEFhyOpZZrUCsgWYHsczLFBBVGNtstoN1bw0Z+O4fYIbvZVt4EUcJEKOhHeincWqONw+q6w5Go+WGOSR7LhKV+KBqbBPpfUvOf9QqkpDyVhBeyyZQGMsdA5FBUqvFMtUyGq9vjnsAJU4UcrxldP1CCaofyDkSAifoP5QwWx+SyUGxp75BzGAvtG7uQ38LehlyEQMeh0TeE6Bm7tYdXqdkt0uOb3kfYlNwmOdDyacOq/qlFo1v+PTmTi3E/glC9W11b34A22zmLzvb231Q0L2Bgg60OTW4YdstO+YOJnO38TtpH7zy9ymokWyA79qlVSn38HtpFlImFnhu3b4boNWXklOXV0Iwo7lQ1hrZyPFcwtjwFP7iEKSHSSJw509kh8kj6pr+H1jR7km9vcvqN9657vffefkv+fKxge1X+7RdjYUPIESN7gTvRkB/RMYtEkaVkdHApmdBPpnKmz0n1xSWFOyVIuLrinZwpoCRe6kyiVZoHX088F+UX4+WKS4iBTP0IWxGtZgOdMaV4KTayqHQF/VihBwTbgDXTCmKoOBJeNhwJMzEVjtjIFLuU38fPR7hqNG1JS7g/qRCuy3vmQ3W9Vu8qbVbP+SzazGRJH83MzP90Ck2m31mMjP8TiLn5uwD2Ugr2PFvPQjB5BnSJvQxGQZZEB+LopqzGzDbMmbkAPkZVJjeO5FzOSBKCgJze2ZS4Gemc9twrwY6u9H61iUQTcRvtdT9RW3tRxAWwFs2tcuJRnI6xjmBdWjbgFNRHMHiF1uHYBfUR/ut5Ug2jXAaT96+9RH/FToRwIzGbKmVJ1AZQnoabSB1yyIg7ByAridHApPMjyw0OiV6RjSbCuzwLAvFizBliWJua1tsuAgvNPbmljYbpt8lkWam7b3XZiOiKJskMOtmfScnsbPW208knwjuXrXK4Q1iKIgNyYXXDVT9C2Ye/78GQ5BEEXfFdde2RwauOysdJNL5AzCy84ard/nGAVN8alecnFdgu5Gbd5DJTL+hHZK0vApVy3OfU8XTSJg1TlssivsPYUlIqvn66PzrVTymCc4wgF6SDNR0pDf+9Gp+VnsUH5WtpHYsuhOaey8zdwLN47V8MTbm78g687+P3cx6tcAeNpjYGRgYGBk8s0/zBIfz2/zlUGeZQNQhOFCWfF0GP0/8P8c1jusIkAuBwMTSBQAYwQM6HjaY2BkYGAV+d8KJgP/XWG9wwAUQQGLAYqPBl942n1TvUoDQRCe1VM8kWARjNrZGIurBAsRBIuA2vkAFsJiKTYW4guIjT5ARMgTxCLoA1hcb5OgDyGHrY7f7M65e8fpLF++2W/nZ2eTmGfaIJi5I0qGDlZZcD51QzTTJirZPAI9JIwVA+wT8L5nOdMaV0AuMJ+icRHq8of6LSD18fzq8ds7xjpwBnQiSI9V5QVl6NwPvgM15NXn/AtWZyj3W0HjEXitOc/dIdbetPdFTZ+P6t+X7xU0/k6GJtOe1/B3arN0/pmz1J4UZc+D6ExwjD7vioeGd5HvhvU+R+DZcGZ6YBPNfAi0G97iBPwFXqph2cW8+D7kjMfwtinHb6kLb6Wygk3cZytSEoptGrlScdHtLPeri1JKueACMZfU1ViJG1Sq5E43dIt7SZZFl1zuRhb/GOs44xFVDbrJzB5tYs35OmaXTrEmkv0DajnMWQB42mNgYNCCwk0MLxheMPrhgUuY2JiUmOqY2pjWMD1hdmPOY+5hPsLCwWLEksSyiOUOawzrLrYiti/sCuxJ7Kc45DiSOPZxmnG2cG7jvMelweXDNYXrEbcBdxf3KR4OngheLd443g18fHwZfFv4NfiX8T8TEBIIEZggsEpQS7BMcJsQl5CFUI3QAWEp4RLhCyJaIldEbURXiJ4RYxEzE0sQ2yD2TzxIfJkEk4SeRJbENIkNEg8k/klqSGZITpE8InlL8p2UmVSG1A6pb9Jx0ltkjGSmyDySlZF1kc2RnSK7R/aZnJ5cmdwB+ST5SwpuCvsUjRTLFHcoOShNU9qhzKespGyhXKV8SPmBCpOKgUqcyjSVR6omqgmqe9RE1OrUnqkHqO9R/6FholGgsUZzgeYZLTUtL60WbS7tKh0OnQydXTpvdGV0O3S/6Gnopekt0ruhz6fvpl+nv0n/h4GdQYvBJUMhwwTDdYYvjFSM4oxmGd0zVjK2M84w3mYiYZJgssLkkqmO6TzTF2Z2ZjVmd8ylzP3MJ5lfsRCwcLJoszhhyWXpZdlhecZKxirHapbVPesF1ndsJGwCbBbZ/LA1sn1jZ2XXY3fFXsM+z36V/S8HD4cGh2OOTI51ThJOK5zeOUs4OzmXOS9wPuUi4JLgss7lm2uU6zY3NrcSty1u39zN3Mvct7l/8xDzMPLw88jyaPM44ynkaeEZ59niucqLyUvPKwgAn3OqOQAAAQAAARcApwARAAAAAAACAAAAAQABAAAAQAAuAAAAAHjarZK9TgJBEMf/d6CRaAyRhMLqCgsbL4ciglTGRPEjSiSKlnLycXJ86CEniU/hM9jYWPgIFkYfwd6nsDD+d1mBIIUx3mZnfzs3MzszuwDCeIYG8UUwQxmAFgxxPeeuyxrmcaNYxzTuFAewi0fFQSTxqXgM11pC8TgS2oPiCUS1d8Uh8ofiSczpYcVT5LjiCPlY8Qui+ncOr7D02y6/BTCrP/m+b5bdTrPi2I26Z9qNGtbRQBMdXMJBGRW0YOCecxEWYoiTCvxrYBunqHPdoX2bLOyrMKlZg8thDETw5K7Itci1TXlGy0124QRZZLDFU/exhxztMozlosTpMH6ZPge0L+OKGnFKjJ4WRwppHPL0PP3SI2P9jLQwFOu3GRhDfkeyDo//G7IHgzllZQxLdquvrdCyBVvat3seJlYo06gxapUxhU2JWnFygR03sSxnEkvcpf5Y5eibGq315TDp7fKWm8zbUVl71Aqq/ZtNnlkWmLnQtno9ycvXYbA6W2pF3aKfCayyC0Ja7Fr/PW70/HO4YM0OKxFvzf0C1MyPjwAAeNpt1VWUU2cYRuHsgxenQt1d8/3JOUnqAyR1d/cCLQVKO22pu7tQd3d3d3d3d3cXmGzumrWy3pWLs/NdPDMpZaWu1783l1Lpf14MnfzO6FbqVupfGkD30iR60JNe9KYP09CXfvRnAAMZxGCGMG3pW6ZjemZgKDMyEzMzC7MyG7MzB3MyF3MzD/MyH/OzAAuyEAuzCIuyGIuzBGWCRIUqOQU16jRYkqVYmmVYluVYng6GMZwRNGmxAiuyEiuzCquyGquzBmuyFmuzDuuyHuuzARuyERuzCZuyGZuzBVuyFVuzDduyHdszklGMZgd2ZAw7MZZxjGdnJrALu9LJbuzOHkxkT/Zib/ZhX/Zjfw7gQA7iYA7hUA7jcI7gSI7iaI7hWI7jeE7gRE7iZE5hEqdyGqdzBmdyFmdzDudyHudzARdyERdzCZdyGZdzBVdyFVdzDddyHddzAzdyEzdzC7dyG7dzB3dyF3dzD/dyH/fzAA/yEA/zCI/yGI/zBE/yFE/zDM/yHM/zAi/yEi/zCq/yGq/zBm/yFm/zDu/yHu/zAR/yER/zCZ/yGZ/zBV/yFV/zDd/yHd/zAz/yEz/zC7/yG7/zB3/yF3/zD/9mpYwsy7pl3bMeWc+sV9Y765NNk/XN+mX9swHZwGxQNjgb0nPkmInjR0V7Uq/OsaPL5Y7ylE3l8tQNN7kVt+rmbuHW3LrbcDvam1rtzVvdm50TxrU/DBvRtZUY1rV5a3jXFn550Wo/XDNWK3dFmh7X9LimxzU9qulRTY9qelTTo5rlKLt2wk7YiaprL+yFvbAX9pK9ZC/ZS/aSvWQv2Uv2kr1kr2KvYq9ir2KvYq9ir2KvYq9ir2Kvaq9qr2qvaq9qr2qvaq9qr2qvai+3l9vL7eX2cnu5vdxebi+3l9sr7BV2CjuFncJOYaewU9gp7NTs1LyrZq9mr2avZq9mr2avZq9mr26vbq9ur26vbq9ur26vbq9ur26vYa9hr2GvYa9hr2GvYa/R7oXuQ/eh+2j/UU7e3C3cqc/V3fYdof/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D92H7kP3ofvQfeg+dB+6D92H7kP3ofvQfRT29B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6D/2H/kP/of/Qf+g/9B/6j6nuG3Ya7U5q/0hN3nCTW3Grbu4Wrs/rP+k/6T/pP+k/6T/pP+k+6T7pPek86TzpPOk86TzpOuk66TrpOuk66TrpOlWmPu/36zrpOuk66TrpOuk66TrpOvl/Pek76TvpO+k76TvpO+k76TvpO+k76TvpO7V9t+qtVs/OaOURU6bo6PgPt6rZbwAAAAABVFDDFwAA\"},function(e,t){e.exports=\"data:application/x-font-ttf;base64,AAEAAAAPAIAAAwBwRkZUTW0ql9wAAAD8AAAAHEdERUYBRAAEAAABGAAAACBPUy8yZ7lriQAAATgAAABgY21hcNqt44EAAAGYAAAGcmN2dCAAKAL4AAAIDAAAAARnYXNw//8AAwAACBAAAAAIZ2x5Zn1dwm8AAAgYAACUpGhlYWQFTS/YAACcvAAAADZoaGVhCkQEEQAAnPQAAAAkaG10eNLHIGAAAJ0YAAADdGxvY2Fv+5XOAACgjAAAAjBtYXhwAWoA2AAAorwAAAAgbmFtZbMsoJsAAKLcAAADonBvc3S6o+U1AACmgAAACtF3ZWJmwxhUUAAAsVQAAAAGAAAAAQAAAADMPaLPAAAAANB2gXUAAAAA0HZzlwABAAAADgAAABgAAAAAAAIAAQABARYAAQAEAAAAAgAAAAMEiwGQAAUABAMMAtAAAABaAwwC0AAAAaQAMgK4AAAAAAUAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAFVLV04AQAAg//8DwP8QAAAFFAB7AAAAAQAAAAAAAAAAAAAAIAABAAAABQAAAAMAAAAsAAAACgAAAdwAAQAAAAAEaAADAAEAAAAsAAMACgAAAdwABAGwAAAAaABAAAUAKAAgACsAoAClIAogLyBfIKwgvSISIxsl/CYBJvonCScP4APgCeAZ4CngOeBJ4FngYOBp4HngieCX4QnhGeEp4TnhRuFJ4VnhaeF54YnhleGZ4gbiCeIW4hniIeIn4jniSeJZ4mD4////AAAAIAAqAKAApSAAIC8gXyCsIL0iEiMbJfwmASb6JwknD+AB4AXgEOAg4DDgQOBQ4GDgYuBw4IDgkOEB4RDhIOEw4UDhSOFQ4WDhcOGA4ZDhl+IA4gniEOIY4iHiI+Iw4kDiUOJg+P/////j/9r/Zv9i4Ajf5N+132nfWd4F3P3aHdoZ2SHZE9kOIB0gHCAWIBAgCiAEH/4f+B/3H/Ef6x/lH3wfdh9wH2ofZB9jH10fVx9RH0sfRR9EHt4e3B7WHtUezh7NHsUevx65HrMIFQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAACjAAAAAAAAAA1AAAAIAAAACAAAAADAAAAKgAAACsAAAAEAAAAoAAAAKAAAAAGAAAApQAAAKUAAAAHAAAgAAAAIAoAAAAIAAAgLwAAIC8AAAATAAAgXwAAIF8AAAAUAAAgrAAAIKwAAAAVAAAgvQAAIL0AAAAWAAAiEgAAIhIAAAAXAAAjGwAAIxsAAAAYAAAl/AAAJfwAAAAZAAAmAQAAJgEAAAAaAAAm+gAAJvoAAAAbAAAnCQAAJwkAAAAcAAAnDwAAJw8AAAAdAADgAQAA4AMAAAAeAADgBQAA4AkAAAAhAADgEAAA4BkAAAAmAADgIAAA4CkAAAAwAADgMAAA4DkAAAA6AADgQAAA4EkAAABEAADgUAAA4FkAAABOAADgYAAA4GAAAABYAADgYgAA4GkAAABZAADgcAAA4HkAAABhAADggAAA4IkAAABrAADgkAAA4JcAAAB1AADhAQAA4QkAAAB9AADhEAAA4RkAAACGAADhIAAA4SkAAACQAADhMAAA4TkAAACaAADhQAAA4UYAAACkAADhSAAA4UkAAACrAADhUAAA4VkAAACtAADhYAAA4WkAAAC3AADhcAAA4XkAAADBAADhgAAA4YkAAADLAADhkAAA4ZUAAADVAADhlwAA4ZkAAADbAADiAAAA4gYAAADeAADiCQAA4gkAAADlAADiEAAA4hYAAADmAADiGAAA4hkAAADtAADiIQAA4iEAAADvAADiIwAA4icAAADwAADiMAAA4jkAAAD1AADiQAAA4kkAAAD/AADiUAAA4lkAAAEJAADiYAAA4mAAAAETAAD4/wAA+P8AAAEUAAH1EQAB9REAAAEVAAH2qgAB9qoAAAEWAAYCCgAAAAABAAABAAAAAAAAAAAAAAAAAAAAAQACAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAEAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAL4AAAAAf//AAIAAgAoAAABaAMgAAMABwAusQEALzyyBwQA7TKxBgXcPLIDAgDtMgCxAwAvPLIFBADtMrIHBgH8PLIBAgDtMjMRIRElMxEjKAFA/ujw8AMg/OAoAtAAAQBkAGQETARMAFsAAAEyFh8BHgEdATc+AR8BFgYPATMyFhcWFRQGDwEOASsBFx4BDwEGJi8BFRQGBwYjIiYvAS4BPQEHDgEvASY2PwEjIiYnJjU0Nj8BPgE7AScuAT8BNhYfATU0Njc2AlgPJgsLCg+eBxYIagcCB57gChECBgMCAQIRCuCeBwIHaggWB54PCikiDyYLCwoPngcWCGoHAgee4AoRAgYDAgECEQrgngcCB2oIFgeeDwopBEwDAgECEQrgngcCB2oIFgeeDwopIg8mCwsKD54HFghqBwIHnuAKEQIGAwIBAhEK4J4HAgdqCBYHng8KKSIPJgsLCg+eBxYIagcCB57gChECBgAAAAABAAAAAARMBEwAIwAAATMyFhURITIWHQEUBiMhERQGKwEiJjURISImPQE0NjMhETQ2AcLIFR0BXhUdHRX+oh0VyBUd/qIVHR0VAV4dBEwdFf6iHRXIFR3+ohUdHRUBXh0VyBUdAV4VHQAAAAABAHAAAARABEwARQAAATMyFgcBBgchMhYPAQ4BKwEVITIWDwEOASsBFRQGKwEiJj0BISImPwE+ATsBNSEiJj8BPgE7ASYnASY2OwEyHwEWMj8BNgM5+goFCP6UBgUBDAoGBngGGAp9ARMKBgZ4BhgKfQ8LlAsP/u0KBgZ4BhgKff7tCgYGeAYYCnYFBv6UCAUK+hkSpAgUCKQSBEwKCP6UBgwMCKAIDGQMCKAIDK4LDw8LrgwIoAgMZAwIoAgMDAYBbAgKEqQICKQSAAABAGQABQSMBK4AOwAAATIXFhcjNC4DIyIOAwchByEGFSEHIR4EMzI+AzUzBgcGIyInLgEnIzczNjcjNzM+ATc2AujycDwGtSM0QDkXEys4MjAPAXtk/tQGAZZk/tQJMDlCNBUWOUA0I64eYmunznYkQgzZZHABBdpkhhQ+H3UErr1oaS1LMCEPCx4uTzJkMjJkSnRCKw8PIjBKK6trdZ4wqndkLzVkV4UljQAAAgB7AAAETASwAD4ARwAAASEyHgUVHAEVFA4FKwEHITIWDwEOASsBFRQGKwEiJj0BISImPwE+ATsBNSEiJj8BPgE7ARE0NhcRMzI2NTQmIwGsAV5DakIwFgwBAQwWMEJqQ7ICASAKBgZ4BhgKigsKlQoP/vUKBgZ4BhgKdf71CgYGeAYYCnUPtstALS1ABLAaJD8yTyokCwsLJCpQMkAlGmQMCKAIDK8LDg8KrwwIoAgMZAwIoAgMAdsKD8j+1EJWVEAAAAEAyAGQBEwCvAAPAAATITIWHQEUBiMhIiY9ATQ2+gMgFR0dFfzgFR0dArwdFcgVHR0VyBUdAAAAAgDIAAAD6ASwACUAQQAAARUUBisBFRQGBx4BHQEzMhYdASE1NDY7ATU0NjcuAT0BIyImPQEXFRQWFx4BFAYHDgEdASE1NCYnLgE0Njc+AT0BA+gdFTJjUVFjMhUd/OAdFTJjUVFjMhUdyEE3HCAgHDdBAZBBNxwgIBw3QQSwlhUdZFuVIyOVW5YdFZaWFR2WW5UjI5VbZB0VlshkPGMYDDI8MgwYYzyWljxjGAwyPDIMGGM8ZAAAAAEAAAAAAAAAAAAAAAAxAAAB//IBLATCBEEAFgAAATIWFzYzMhYVFAYjISImNTQ2NyY1NDYB9261LCwueKqqeP0ST3FVQgLYBEF3YQ6teHmtclBFaw4MGZnXAAAAAgAAAGQEsASvABoAHgAAAB4BDwEBMzIWHQEhNTQ2OwEBJyY+ARYfATc2AyEnAwL2IAkKiAHTHhQe+1AeFB4B1IcKCSAkCm9wCXoBebbDBLMTIxC7/RYlFSoqFSUC6rcQJBQJEJSWEPwecAIWAAAAAAQAAABkBLAETAALABcAIwA3AAATITIWBwEGIicBJjYXARYUBwEGJjURNDYJATYWFREUBicBJjQHARYGIyEiJjcBNjIfARYyPwE2MhkEfgoFCP3MCBQI/cwIBQMBCAgI/vgICgoDjAEICAoKCP74CFwBbAgFCvuCCgUIAWwIFAikCBQIpAgUBEwKCP3JCAgCNwgK2v74CBQI/vgIBQoCJgoF/vABCAgFCv3aCgUIAQgIFID+lAgKCggBbAgIpAgIpAgAAAAD//D/8AS6BLoACQANABAAAAAyHwEWFA8BJzcTAScJAQUTA+AmDpkNDWPWXyL9mdYCZv4f/rNuBLoNmQ4mDlzWYP50/ZrWAmb8anABTwAAAAEAAAAABLAEsAAPAAABETMyFh0BITU0NjsBEQEhArz6FR384B0V+v4MBLACiv3aHRUyMhUdAiYCJgAAAAEADgAIBEwEnAAfAAABJTYWFREUBgcGLgE2NzYXEQURFAYHBi4BNjc2FxE0NgFwAoUnMFNGT4gkV09IQv2oWEFPiCRXT0hCHQP5ow8eIvzBN1EXGSltchkYEAIJm/2iKmAVGilucRoYEQJ/JioAAAACAAn/+AS7BKcAHQApAAAAMh4CFQcXFAcBFgYPAQYiJwEGIycHIi4CND4BBCIOARQeATI+ATQmAZDItoNOAQFOARMXARY7GikT/u13jgUCZLaDTk6DAXKwlFZWlLCUVlYEp06DtmQCBY15/u4aJRg6FBQBEk0BAU6Dtsi2g1tWlLCUVlaUsJQAAQBkAFgErwREABkAAAE+Ah4CFRQOAwcuBDU0PgIeAQKJMHt4dVg2Q3mEqD4+p4V4Qzhadnh5A7VESAUtU3ZAOXmAf7JVVbJ/gHk5QHZTLQVIAAAAAf/TAF4EewSUABgAAAETNjIXEyEyFgcFExYGJyUFBiY3EyUmNjMBl4MHFQeBAaUVBhH+qoIHDxH+qf6qEQ8Hgv6lEQYUAyABYRMT/p8RDPn+bxQLDPb3DAsUAZD7DBEAAv/TAF4EewSUABgAIgAAARM2MhcTITIWBwUTFgYnJQUGJjcTJSY2MwUjFwc3Fyc3IycBl4MHFQeBAaUVBhH+qoIHDxH+qf6qEQ8Hgv6lEQYUAfPwxUrBw0rA6k4DIAFhExP+nxEM+f5vFAsM9vcMCxQBkPsMEWSO4ouM5YzTAAABAAAAAASwBLAAJgAAATIWHQEUBiMVFBYXBR4BHQEUBiMhIiY9ATQ2NyU+AT0BIiY9ATQ2Alh8sD4mDAkBZgkMDwr7ggoPDAkBZgkMJj6wBLCwfPouaEsKFwbmBRcKXQoPDwpdChcF5gYXCktoLvp8sAAAAA0AAAAABLAETAAPABMAIwAnACsALwAzADcARwBLAE8AUwBXAAATITIWFREUBiMhIiY1ETQ2FxUzNSkBIgYVERQWMyEyNjURNCYzFTM1BRUzNSEVMzUFFTM1IRUzNQchIgYVERQWMyEyNjURNCYFFTM1IRUzNQUVMzUhFTM1GQR+Cg8PCvuCCg8PVWQCo/3aCg8PCgImCg8Pc2T8GGQDIGT8GGQDIGTh/doKDw8KAiYKDw/872QDIGT8GGQDIGQETA8K++YKDw8KBBoKD2RkZA8K/qIKDw8KAV4KD2RkyGRkZGTIZGRkZGQPCv6iCg8PCgFeCg9kZGRkZMhkZGRkAAAEAAAAAARMBEwADwAfAC8APwAAEyEyFhURFAYjISImNRE0NikBMhYVERQGIyEiJjURNDYBITIWFREUBiMhIiY1ETQ2KQEyFhURFAYjISImNRE0NjIBkBUdHRX+cBUdHQJtAZAVHR0V/nAVHR39vQGQFR0dFf5wFR0dAm0BkBUdHRX+cBUdHQRMHRX+cBUdHRUBkBUdHRX+cBUdHRUBkBUd/agdFf5wFR0dFQGQFR0dFf5wFR0dFQGQFR0AAAkAAAAABEwETAAPAB8ALwA/AE8AXwBvAH8AjwAAEzMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYhMzIWHQEUBisBIiY9ATQ2ATMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYhMzIWHQEUBisBIiY9ATQ2ATMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYhMzIWHQEUBisBIiY9ATQ2MsgVHR0VyBUdHQGlyBUdHRXIFR0dAaXIFR0dFcgVHR389cgVHR0VyBUdHQGlyBUdHRXIFR0dAaXIFR0dFcgVHR389cgVHR0VyBUdHQGlyBUdHRXIFR0dAaXIFR0dFcgVHR0ETB0VyBUdHRXIFR0dFcgVHR0VyBUdHRXIFR0dFcgVHf5wHRXIFR0dFcgVHR0VyBUdHRXIFR0dFcgVHR0VyBUd/nAdFcgVHR0VyBUdHRXIFR0dFcgVHR0VyBUdHRXIFR0ABgAAAAAEsARMAA8AHwAvAD8ATwBfAAATMzIWHQEUBisBIiY9ATQ2KQEyFh0BFAYjISImPQE0NgEzMhYdARQGKwEiJj0BNDYpATIWHQEUBiMhIiY9ATQ2ATMyFh0BFAYrASImPQE0NikBMhYdARQGIyEiJj0BNDYyyBUdHRXIFR0dAaUCvBUdHRX9RBUdHf6FyBUdHRXIFR0dAaUCvBUdHRX9RBUdHf6FyBUdHRXIFR0dAaUCvBUdHRX9RBUdHQRMHRXIFR0dFcgVHR0VyBUdHRXIFR3+cB0VyBUdHRXIFR0dFcgVHR0VyBUd/nAdFcgVHR0VyBUdHRXIFR0dFcgVHQAAAAABACYALAToBCAAFwAACQE2Mh8BFhQHAQYiJwEmND8BNjIfARYyAdECOwgUB7EICPzxBxUH/oAICLEHFAirBxYB3QI7CAixBxQI/PAICAGACBQHsQgIqwcAAQBuAG4EQgRCACMAAAEXFhQHCQEWFA8BBiInCQEGIi8BJjQ3CQEmND8BNjIXCQE2MgOIsggI/vUBCwgIsggVB/70/vQHFQiyCAgBC/71CAiyCBUHAQwBDAcVBDuzCBUH/vT+9AcVCLIICAEL/vUICLIIFQcBDAEMBxUIsggI/vUBDAcAAwAX/+sExQSZABkAJQBJAAAAMh4CFRQHARYUDwEGIicBBiMiLgI0PgEEIg4BFB4BMj4BNCYFMzIWHQEzMhYdARQGKwEVFAYrASImPQEjIiY9ATQ2OwE1NDYBmcSzgk1OASwICG0HFQj+1HeOYrSBTU2BAW+zmFhYmLOZWFj+vJYKD0sKDw8KSw8KlgoPSwoPDwpLDwSZTYKzYo15/tUIFQhsCAgBK01NgbTEs4JNWJmzmFhYmLOZIw8KSw8KlgoPSwoPDwpLDwqWCg9LCg8AAAMAF//rBMUEmQAZACUANQAAADIeAhUUBwEWFA8BBiInAQYjIi4CND4BBCIOARQeATI+ATQmBSEyFh0BFAYjISImPQE0NgGZxLOCTU4BLAgIbQcVCP7Ud45itIFNTYEBb7OYWFiYs5lYWP5YAV4KDw8K/qIKDw8EmU2Cs2KNef7VCBUIbAgIAStNTYG0xLOCTViZs5hYWJizmYcPCpYKDw8KlgoPAAAAAAIAFwAXBJkEsAAPAC0AAAEzMhYVERQGKwEiJjURNDYFNRYSFRQOAiIuAjU0EjcVDgEVFB4BMj4BNTQmAiZkFR0dFWQVHR0BD6fSW5vW6tabW9KnZ3xyxejFcnwEsB0V/nAVHR0VAZAVHeGmPv7ZuHXWm1tbm9Z1uAEnPqY3yHh0xXJyxXR4yAAEAGQAAASwBLAADwAfAC8APwAAATMyFhURFAYrASImNRE0NgEzMhYVERQGKwEiJjURNDYBMzIWFREUBisBIiY1ETQ2BTMyFh0BFAYrASImPQE0NgQBlgoPDwqWCg8P/t6WCg8PCpYKDw/+3pYKDw8KlgoPD/7elgoPDwqWCg8PBLAPCvuCCg8PCgR+Cg/+cA8K/RIKDw8KAu4KD/7UDwr+PgoPDwoBwgoPyA8K+goPDwr6Cg8AAAAAAgAaABsElgSWAEcATwAAATIfAhYfATcWFwcXFh8CFhUUDwIGDwEXBgcnBwYPAgYjIi8CJi8BByYnNycmLwImNTQ/AjY/ASc2Nxc3Nj8CNhIiBhQWMjY0AlghKSYFMS0Fhj0rUAMZDgGYBQWYAQ8YA1AwOIYFLDIFJisfISkmBTEtBYY8LFADGQ0ClwYGlwINGQNQLzqFBS0xBSYreLJ+frJ+BJYFmAEOGQJQMDmGBSwxBiYrHiIoJgYxLAWGPSxRAxkOApcFBZcCDhkDUTA5hgUtMAYmKiAhKCYGMC0Fhj0sUAIZDgGYBf6ZfrF+frEABwBkAAAEsAUUABMAFwAhACUAKQAtADEAAAEhMhYdASEyFh0BITU0NjMhNTQ2FxUhNQERFAYjISImNREXETMRMxEzETMRMxEzETMRAfQBLCk7ARMKD/u0DwoBEzspASwBLDsp/UQpO2RkZGRkZGRkBRQ7KWQPCktLCg9kKTtkZGT+1PzgKTs7KQMgZP1EArz9RAK8/UQCvP1EArwAAQAMAAAFCATRAB8AABMBNjIXARYGKwERFAYrASImNREhERQGKwEiJjURIyImEgJsCBUHAmAIBQqvDwr6Cg/+1A8K+goPrwoFAmoCYAcH/aAICv3BCg8PCgF3/okKDw8KAj8KAAIAZAAAA+gEsAARABcAAAERFBYzIREUBiMhIiY1ETQ2MwEjIiY9AQJYOykBLB0V/OAVHR0VA1L6FR0EsP5wKTv9dhUdHRUETBUd/nAdFfoAAwAXABcEmQSZAA8AGwAwAAAAMh4CFA4CIi4CND4BBCIOARQeATI+ATQmBTMyFhURMzIWHQEUBisBIiY1ETQ2AePq1ptbW5vW6tabW1ubAb/oxXJyxejFcnL+fDIKD68KDw8K+goPDwSZW5vW6tabW1ub1urWmztyxejFcnLF6MUNDwr+7Q8KMgoPDwoBXgoPAAAAAAL/nAAABRQEsAALAA8AACkBAyMDIQEzAzMDMwEDMwMFFP3mKfIp/eYBr9EVohTQ/p4b4BsBkP5wBLD+1AEs/nD+1AEsAAAAAAIAZAAABLAEsAAVAC8AAAEzMhYVETMyFgcBBiInASY2OwERNDYBMzIWFREUBiMhIiY1ETQ2OwEyFh0BITU0NgImyBUdvxQLDf65DSYN/rkNCxS/HQJUMgoPDwr75goPDwoyCg8DhA8EsB0V/j4XEP5wEBABkBAXAcIVHfzgDwr+ogoPDwoBXgoPDwqvrwoPAAMAFwAXBJkEmQAPABsAMQAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JgUzMhYVETMyFgcDBiInAyY2OwERNDYB4+rWm1tbm9bq1ptbW5sBv+jFcnLF6MVycv58lgoPiRUKDd8NJg3fDQoViQ8EmVub1urWm1tbm9bq1ps7csXoxXJyxejFDQ8K/u0XEP7tEBABExAXARMKDwAAAAMAFwAXBJkEmQAPABsAMQAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JiUTFgYrAREUBisBIiY1ESMiJjcTNjIB4+rWm1tbm9bq1ptbW5sBv+jFcnLF6MVycv7n3w0KFYkPCpYKD4kVCg3fDSYEmVub1urWm1tbm9bq1ps7csXoxXJyxejFAf7tEBf+7QoPDwoBExcQARMQAAAAAAIAAAAABLAEsAAZADkAABMhMhYXExYVERQGBwYjISImJyY1EzQ3Ez4BBSEiBgcDBhY7ATIWHwEeATsBMjY/AT4BOwEyNicDLgHhAu4KEwO6BwgFDBn7tAweAgYBB7kDEwKX/dQKEgJXAgwKlgoTAiYCEwr6ChMCJgITCpYKDAJXAhIEsA4K/XQYGf5XDB4CBggEDRkBqRkYAowKDsgOC/4+Cw4OCpgKDg4KmAoODgsBwgsOAAMAFwAXBJkEmQAPABsAJwAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JgUXFhQPAQYmNRE0NgHj6tabW1ub1urWm1tbmwG/6MVycsXoxXJy/ov9ERH9EBgYBJlbm9bq1ptbW5vW6tabO3LF6MVycsXoxV2+DCQMvgwLFQGQFQsAAQAXABcEmQSwACgAAAE3NhYVERQGIyEiJj8BJiMiDgEUHgEyPgE1MxQOAiIuAjQ+AjMyA7OHBwsPCv6WCwQHhW2BdMVycsXoxXKWW5vW6tabW1ub1nXABCSHBwQL/pYKDwsHhUxyxejFcnLFdHXWm1tbm9bq1ptbAAAAAAIAFwABBJkEsAAaADUAAAE3NhYVERQGIyEiJj8BJiMiDgEVIzQ+AjMyEzMUDgIjIicHBiY1ETQ2MyEyFg8BFjMyPgEDs4cHCw8L/pcLBAeGboF0xXKWW5vWdcDrllub1nXAnIYHCw8LAWgKBQiFboJ0xXIEJIcHBAv+lwsPCweGS3LFdHXWm1v9v3XWm1t2hggFCgFoCw8LB4VMcsUAAAAKAGQAAASwBLAADwAfAC8APwBPAF8AbwB/AI8AnwAAEyEyFhURFAYjISImNRE0NgUhIgYVERQWMyEyNjURNCYFMzIWHQEUBisBIiY9ATQ2MyEyFh0BFAYjISImPQE0NgczMhYdARQGKwEiJj0BNDYzITIWHQEUBiMhIiY9ATQ2BzMyFh0BFAYrASImPQE0NjMhMhYdARQGIyEiJj0BNDYHMzIWHQEUBisBIiY9ATQ2MyEyFh0BFAYjISImPQE0Nn0EGgoPDwr75goPDwPA/K4KDw8KA1IKDw/9CDIKDw8KMgoPD9IBwgoPDwr+PgoPD74yCg8PCjIKDw/SAcIKDw8K/j4KDw++MgoPDwoyCg8P0gHCCg8PCv4+Cg8PvjIKDw8KMgoPD9IBwgoPDwr+PgoPDwSwDwr7ggoPDwoEfgoPyA8K/K4KDw8KA1IKD2QPCjIKDw8KMgoPDwoyCg8PCjIKD8gPCjIKDw8KMgoPDwoyCg8PCjIKD8gPCjIKDw8KMgoPDwoyCg8PCjIKD8gPCjIKDw8KMgoPDwoyCg8PCjIKDwAAAAACAAAAAARMBLAAGQAjAAABNTQmIyEiBh0BIyIGFREUFjMhMjY1ETQmIyE1NDY7ATIWHQEDhHVT/tRSdmQpOzspA4QpOzsp/ageFMgUHgMgyFN1dlLIOyn9qCk7OykCWCk7lhUdHRWWAAIAZAAABEwETAAJADcAABMzMhYVESMRNDYFMhcWFREUBw4DIyIuAScuAiMiBwYjIicmNRE+ATc2HgMXHgIzMjc2fTIKD2QPA8AEBRADIUNAMRwaPyonKSxHHlVLBwgGBQ4WeDsXKC4TOQQpLUUdZ1AHBEwPCvvNBDMKDzACBhH+WwYGO1AkDQ0ODg8PDzkFAwcPAbY3VwMCAwsGFAEODg5XCAAAAwAAAAAEsASXACEAMQBBAAAAMh4CFREUBisBIiY1ETQuASAOARURFAYrASImNRE0PgEDMzIWFREUBisBIiY1ETQ2ITMyFhURFAYrASImNRE0NgHk6N6jYw8KMgoPjeT++uSNDwoyCg9joyqgCAwMCKAIDAwCYKAIDAwIoAgMDASXY6PedP7UCg8PCgEsf9FyctF//tQKDw8KASx03qP9wAwI/jQIDAwIAcwIDAwI/jQIDAwIAcwIDAAAAAACAAAA0wRHA90AFQA5AAABJTYWFREUBiclJisBIiY1ETQ2OwEyBTc2Mh8BFhQPARcWFA8BBiIvAQcGIi8BJjQ/AScmND8BNjIXAUEBAgkMDAn+/hUZ+goPDwr6GQJYeAcUByIHB3h4BwciBxQHeHgHFAciBwd3dwcHIgcUBwMurAYHCv0SCgcGrA4PCgFeCg+EeAcHIgcUB3h4BxQHIgcHd3cHByIHFAd4eAcUByIICAAAAAACAAAA0wNyA90AFQAvAAABJTYWFREUBiclJisBIiY1ETQ2OwEyJTMWFxYVFAcGDwEiLwEuATc2NTQnJjY/ATYBQQECCQwMCf7+FRn6Cg8PCvoZAdIECgZgWgYLAwkHHQcDBkhOBgMIHQcDLqwGBwr9EgoHBqwODwoBXgoPZAEJgaGafwkBAQYXBxMIZ36EaggUBxYFAAAAAAMAAADEBGID7AAbADEASwAAATMWFxYVFAYHBgcjIi8BLgE3NjU0JicmNj8BNgUlNhYVERQGJyUmKwEiJjURNDY7ATIlMxYXFhUUBwYPASIvAS4BNzY1NCcmNj8BNgPHAwsGh0RABwoDCQcqCAIGbzs3BgIJKgf9ggECCQwMCf7+FRn6Cg8PCvoZAdIECgZgWgYLAwkHHQcDBkhOBgMIHQcD7AEJs9lpy1QJAQYiBhQIlrJarEcJFAYhBb6sBgcK/RIKBwasDg8KAV4KD2QBCYGhmn8JAQEGFwcTCGd+hGoIFQYWBQAAAAANAAAAAASwBLAACQAVABkAHQAhACUALQA7AD8AQwBHAEsATwAAATMVIxUhFSMRIQEjFTMVIREjESM1IQURIREhESERBSM1MwUjNTMBMxEhETM1MwEzFSMVIzUjNTM1IzUhBREhEQcjNTMFIzUzASM1MwUhNSEB9GRk/nBkAfQCvMjI/tTIZAJY+7QBLAGQASz84GRkArxkZP1EyP4MyGQB9MhkyGRkyAEs/UQBLGRkZAOEZGT+DGRkAfT+1AEsA4RkZGQCWP4MZMgBLAEsyGT+1AEs/tQBLMhkZGT+DP4MAfRk/tRkZGRkyGTI/tQBLMhkZGT+1GRkZAAAAAAJAAAAAASwBLAAAwAHAAsADwATABcAGwAfACMAADcjETMTIxEzASMRMxMjETMBIxEzASE1IRcjNTMXIzUzBSM1M2RkZMhkZAGQyMjIZGQBLMjI/OD+1AEsyGRkyGRkASzIyMgD6PwYA+j8GAPo/BgD6PwYA+j7UGRkW1tbW1sAAAIAAAAKBKYEsAANABUAAAkBFhQHAQYiJwETNDYzBCYiBhQWMjYB9AKqCAj+MAgUCP1WAQ8KAUM7Uzs7UzsEsP1WCBQI/jAICAKqAdsKD807O1Q7OwAAAAADAAAACgXSBLAADQAZACEAAAkBFhQHAQYiJwETNDYzIQEWFAcBBiIvAQkBBCYiBhQWMjYB9AKqCAj+MAgUCP1WAQ8KAwYCqggI/jAIFAg4Aaj9RP7TO1M7O1M7BLD9VggUCP4wCAgCqgHbCg/9VggUCP4wCAg4AaoCvM07O1Q7OwAAAAABAGQAAASwBLAAJgAAASEyFREUDwEGJjURNCYjISIPAQYWMyEyFhURFAYjISImNRE0PwE2ASwDOUsSQAgKDwr9RBkSQAgFCgK8Cg8PCvyuCg8SixIEsEv8fBkSQAgFCgO2Cg8SQAgKDwr8SgoPDwoDzxkSixIAAAABAMj//wRMBLAACgAAEyEyFhURCQERNDb6AyAVHf4+/j4dBLAdFfuCAbz+QwR/FR0AAAAAAwAAAAAEsASwABUARQBVAAABISIGBwMGHwEeATMhMjY/ATYnAy4BASMiBg8BDgEjISImLwEuASsBIgYVERQWOwEyNj0BNDYzITIWHQEUFjsBMjY1ETQmASEiBg8BBhYzITI2LwEuAQM2/kQLEAFOBw45BhcKAcIKFwY+DgdTARABVpYKFgROBBYK/doKFgROBBYKlgoPDwqWCg8PCgLuCg8PCpYKDw/+sf4MChMCJgILCgJYCgsCJgITBLAPCv7TGBVsCQwMCWwVGAEtCg/+cA0JnAkNDQmcCQ0PCv12Cg8PCpYKDw8KlgoPDwoCigoP/agOCpgKDg4KmAoOAAAAAAQAAABkBLAETAAdACEAKQAxAAABMzIeAh8BMzIWFREUBiMhIiY1ETQ2OwE+BAEVMzUEIgYUFjI2NCQyFhQGIiY0AfTIOF00JAcGlik7Oyn8GCk7OymWAgknM10ByGT+z76Hh76H/u9WPDxWPARMKTs7FRQ7Kf2oKTs7KQJYKTsIG0U1K/7UZGRGh76Hh74IPFY8PFYAAAAAAgA1AAAEsASvACAAIwAACQEWFx4BHwEVITUyNi8BIQYHBh4CMxUhNTY3PgE/AQEDIQMCqQGBFCgSJQkK/l81LBFS/nk6IgsJKjIe/pM4HAwaBwcBj6wBVKIEr/waMioTFQECQkJXLd6RWSIuHAxCQhgcDCUNDQPu/VoByQAAAAADAGQAAAPwBLAAJwAyADsAAAEeBhUUDgMjITU+ATURNC4EJzUFMh4CFRQOAgclMzI2NTQuAisBETMyNjU0JisBAvEFEzUwOyodN1htbDD+DCk7AQYLFyEaAdc5dWM+Hy0tEP6Pi05pESpTPnbYUFJ9Xp8CgQEHGB0zOlIuQ3VONxpZBzMoAzsYFBwLEAkHRwEpSXNDM1s6KwkxYUopOzQb/K5lUFqBAAABAMgAAANvBLAAGQAAARcOAQcDBhYXFSE1NjcTNjQuBCcmJzUDbQJTQgeECSxK/gy6Dq0DAw8MHxUXDQYEsDkTNSj8uTEoBmFhEFIDQBEaExAJCwYHAwI5AAAAAAL/tQAABRQEsAAlAC8AAAEjNC4FKwERFBYfARUhNTI+AzURIyIOBRUjESEFIxEzByczESM3BRQyCAsZEyYYGcgyGRn+cAQOIhoWyBkYJhMZCwgyA+j7m0tLfX1LS30DhBUgFQ4IAwH8rhYZAQJkZAEFCRUOA1IBAwgOFSAVASzI/OCnpwMgpwACACH/tQSPBLAAJQAvAAABIzQuBSsBERQWHwEVITUyPgM1ESMiDgUVIxEhEwc1IRUnNxUhNQRMMggLGRMmGBnIMhkZ/nAEDiIaFsgZGCYTGQsIMgPoQ6f84KenAyADhBUgFQ4IAwH9dhYZAQJkZAEFCRUOAooBAwgOFSAVASz7gn1LS319S0sABAAAAAAEsARMAA8AHwAvAD8AABMhMhYdARQGIyEiJj0BNDYTITIWHQEUBiMhIiY9ATQ2EyEyFh0BFAYjISImPQE0NhMhMhYdARQGIyEiJj0BNDYyAlgVHR0V/agVHR0VA+gVHR0V/BgVHR0VAyAVHR0V/OAVHR0VBEwVHR0V+7QVHR0ETB0VZBUdHRVkFR3+1B0VZBUdHRVkFR3+1B0VZBUdHRVkFR3+1B0VZBUdHRVkFR0ABAAAAAAEsARMAA8AHwAvAD8AABMhMhYdARQGIyEiJj0BNDYDITIWHQEUBiMhIiY9ATQ2EyEyFh0BFAYjISImPQE0NgMhMhYdARQGIyEiJj0BNDb6ArwVHR0V/UQVHR2zBEwVHR0V+7QVHR3dArwVHR0V/UQVHR2zBEwVHR0V+7QVHR0ETB0VZBUdHRVkFR3+1B0VZBUdHRVkFR3+1B0VZBUdHRVkFR3+1B0VZBUdHRVkFR0ABAAAAAAEsARMAA8AHwAvAD8AAAE1NDYzITIWHQEUBiMhIiYBNTQ2MyEyFh0BFAYjISImEzU0NjMhMhYdARQGIyEiJgE1NDYzITIWHQEUBiMhIiYB9B0VAlgVHR0V/agVHf5wHRUD6BUdHRX8GBUdyB0VAyAVHR0V/OAVHf7UHRUETBUdHRX7tBUdA7ZkFR0dFWQVHR3+6WQVHR0VZBUdHf7pZBUdHRVkFR0d/ulkFR0dFWQVHR0AAAQAAAAABLAETAAPAB8ALwA/AAATITIWHQEUBiMhIiY9ATQ2EyEyFh0BFAYjISImPQE0NhMhMhYdARQGIyEiJj0BNDYTITIWHQEUBiMhIiY9ATQ2MgRMFR0dFfu0FR0dFQRMFR0dFfu0FR0dFQRMFR0dFfu0FR0dFQRMFR0dFfu0FR0dBEwdFWQVHR0VZBUd/tQdFWQVHR0VZBUd/tQdFWQVHR0VZBUd/tQdFWQVHR0VZBUdAAgAAAAABLAETAAPAB8ALwA/AE8AXwBvAH8AABMzMhYdARQGKwEiJj0BNDYpATIWHQEUBiMhIiY9ATQ2ATMyFh0BFAYrASImPQE0NikBMhYdARQGIyEiJj0BNDYBMzIWHQEUBisBIiY9ATQ2KQEyFh0BFAYjISImPQE0NgEzMhYdARQGKwEiJj0BNDYpATIWHQEUBiMhIiY9ATQ2MmQVHR0VZBUdHQFBAyAVHR0V/OAVHR3+6WQVHR0VZBUdHQFBAyAVHR0V/OAVHR3+6WQVHR0VZBUdHQFBAyAVHR0V/OAVHR3+6WQVHR0VZBUdHQFBAyAVHR0V/OAVHR0ETB0VZBUdHRVkFR0dFWQVHR0VZBUd/tQdFWQVHR0VZBUdHRVkFR0dFWQVHf7UHRVkFR0dFWQVHR0VZBUdHRVkFR3+1B0VZBUdHRVkFR0dFWQVHR0VZBUdAAAG/5wAAASwBEwAAwATACMAKgA6AEoAACEjETsCMhYdARQGKwEiJj0BNDYTITIWHQEUBiMhIiY9ATQ2BQc1IzUzNQUhMhYdARQGIyEiJj0BNDYTITIWHQEUBiMhIiY9ATQ2AZBkZJZkFR0dFWQVHR0VAfQVHR0V/gwVHR3++qfIyAHCASwVHR0V/tQVHR0VAlgVHR0V/agVHR0ETB0VZBUdHRVkFR3+1B0VZBUdHRVkFR36fUtkS68dFWQVHR0VZBUd/tQdFWQVHR0VZBUdAAAABgAAAAAFFARMAA8AEwAjACoAOgBKAAATMzIWHQEUBisBIiY9ATQ2ASMRMwEhMhYdARQGIyEiJj0BNDYFMxUjFSc3BSEyFh0BFAYjISImPQE0NhMhMhYdARQGIyEiJj0BNDYyZBUdHRVkFR0dA2dkZPyuAfQVHR0V/gwVHR0EL8jIp6f75gEsFR0dFf7UFR0dFQJYFR0dFf2oFR0dBEwdFWQVHR0VZBUd+7QETP7UHRVkFR0dFWQVHchkS319rx0VZBUdHRVkFR3+1B0VZBUdHRVkFR0AAAAAAgAAAMgEsAPoAA8AEgAAEyEyFhURFAYjISImNRE0NgkCSwLuHywsH/0SHywsBIT+1AEsA+gsH/12HywsHwKKHyz9RAEsASwAAwAAAAAEsARMAA8AFwAfAAATITIWFREUBiMhIiY1ETQ2FxE3BScBExEEMhYUBiImNCwEWBIaGhL7qBIaGkr3ASpKASXs/NJwTk5wTgRMGhL8DBIaGhID9BIaZP0ftoOcAT7+4AH0dE5vT09vAAAAAAIA2wAFBDYEkQAWAB4AAAEyHgEVFAcOAQ8BLgQnJjU0PgIWIgYUFjI2NAKIdcZzRkWyNjYJIV5YbSk8RHOft7eCgreCBJF4ynVzj23pPz4IIWZomEiEdVijeUjDgriBgbgAAAACABcAFwSZBJkADwAXAAAAMh4CFA4CIi4CND4BAREiDgEUHgEB4+rWm1tbm9bq1ptbW5sBS3TFcnLFBJlbm9bq1ptbW5vW6tab/G8DVnLF6MVyAAACAHUAAwPfBQ8AGgA1AAABHgYVFA4DBy4DNTQ+BQMOAhceBBcWNj8BNiYnLgInJjc2IyYCKhVJT1dOPiUzVnB9P1SbfEokP0xXUEm8FykoAwEbITEcExUWAgYCCQkFEikMGiACCAgFD0iPdXdzdYdFR4BeRiYEBTpjl1lFh3ZzeHaQ/f4hS4I6JUEnIw4IBwwQIgoYBwQQQSlZtgsBAAAAAwAAAAAEywRsAAwAKgAvAAABNz4CHgEXHgEPAiUhMhcHISIGFREUFjMhMjY9ATcRFAYjISImNRE0NgkBBzcBA+hsAgYUFR0OFgoFBmz9BQGQMje7/pApOzspAfQpO8i7o/5wpbm5Azj+lqE3AWMD9XMBAgIEDw4WKgsKc8gNuzsp/gwpOzsptsj+tKW5uaUBkKW5/tf+ljKqAWMAAgAAAAAEkwRMABsANgAAASEGByMiBhURFBYzITI2NTcVFAYjISImNRE0NgUBFhQHAQYmJzUmDgMHPgY3NT4BAV4BaaQ0wyk7OykB9Ck7yLml/nClubkCfwFTCAj+rAcLARo5ZFRYGgouOUlARioTAQsETJI2Oyn+DCk7OymZZ6W5uaUBkKW5G/7TBxUH/s4GBAnLAQINFjAhO2JBNB0UBwHSCgUAAAAAAgAAAAAEnQRMAB0ANQAAASEyFwchIgYVERQWMyEyNj0BNxUUBiMhIiY1ETQ2CQE2Mh8BFhQHAQYiLwEmND8BNjIfARYyAV4BXjxDsv6jKTs7KQH0KTvIuaX+cKW5uQHKAYsHFQdlBwf97QcVB/gHB2UHFQdvCBQETBexOyn+DCk7OylFyNulubmlAZCluf4zAYsHB2UHFQf97AcH+AcVB2UHB28HAAAAAQAKAAoEpgSmADsAAAkBNjIXARYGKwEVMzU0NhcBFhQHAQYmPQEjFTMyFgcBBiInASY2OwE1IxUUBicBJjQ3ATYWHQEzNSMiJgE+AQgIFAgBBAcFCqrICggBCAgI/vgICsiqCgUH/vwIFAj++AgFCq/ICgj++AgIAQgICsivCgUDlgEICAj++AgKyK0KBAf+/AcVB/73BwQKrcgKCP74CAgBCAgKyK0KBAcBCQcVBwEEBwQKrcgKAAEAyAAAA4QETAAZAAATMzIWFREBNhYVERQGJwERFAYrASImNRE0NvpkFR0B0A8VFQ/+MB0VZBUdHQRMHRX+SgHFDggV/BgVCA4Bxf5KFR0dFQPoFR0AAAABAAAAAASwBEwAIwAAEzMyFhURATYWFREBNhYVERQGJwERFAYnAREUBisBIiY1ETQ2MmQVHQHQDxUB0A8VFQ/+MBUP/jAdFWQVHR0ETB0V/koBxQ4IFf5KAcUOCBX8GBUIDgHF/koVCA4Bxf5KFR0dFQPoFR0AAAABAJ0AGQSwBDMAFQAAAREUBicBERQGJwEmNDcBNhYVEQE2FgSwFQ/+MBUP/hQPDwHsDxUB0A8VBBr8GBUIDgHF/koVCA4B4A4qDgHgDggV/koBxQ4IAAAAAQDIABYEMwQ2AAsAABMBFhQHAQYmNRE0NvMDLhIS/NISGRkEMv4OCx4L/g4LDhUD6BUOAAIAyABkA4QD6AAPAB8AABMzMhYVERQGKwEiJjURNDYhMzIWFREUBisBIiY1ETQ2+sgVHR0VyBUdHQGlyBUdHRXIFR0dA+gdFfzgFR0dFQMgFR0dFfzgFR0dFQMgFR0AAAEAyABkBEwD6AAPAAABERQGIyEiJjURNDYzITIWBEwdFfzgFR0dFQMgFR0DtvzgFR0dFQMgFR0dAAAAAAEAAAAZBBMEMwAVAAABETQ2FwEWFAcBBiY1EQEGJjURNDYXAfQVDwHsDw/+FA8V/jAPFRUPAmQBthUIDv4gDioO/iAOCBUBtv47DggVA+gVCA4AAAH//gACBLMETwAjAAABNzIWFRMUBiMHIiY1AwEGJjUDAQYmNQM0NhcBAzQ2FwEDNDYEGGQUHgUdFWQVHQL+MQ4VAv4yDxUFFQ8B0gIVDwHSAh0ETgEdFfwYFR0BHRUBtf46DwkVAbX+OQ4JFAPoFQkP/j4BthQJDv49AbYVHQAAAQEsAAAD6ARMABkAAAEzMhYVERQGKwEiJjURAQYmNRE0NhcBETQ2A1JkFR0dFWQVHf4wDxUVDwHQHQRMHRX8GBUdHRUBtv47DggVA+gVCA7+OwG2FR0AAAIAZADIBLAESAALABsAAAkBFgYjISImNwE2MgEhMhYdARQGIyEiJj0BNDYCrgH1DwkW++4WCQ8B9Q8q/fcD6BUdHRX8GBUdHQQ5/eQPFhYPAhwP/UgdFWQVHR0VZBUdAAEAiP/8A3UESgAFAAAJAgcJAQN1/qABYMX92AIoA4T+n/6fxgIoAiYAAAAAAQE7//wEKARKAAUAAAkBJwkBNwQo/dnGAWH+n8YCI/3ZxgFhAWHGAAIAFwAXBJkEmQAPADMAAAAyHgIUDgIiLgI0PgEFIyIGHQEjIgYdARQWOwEVFBY7ATI2PQEzMjY9ATQmKwE1NCYB4+rWm1tbm9bq1ptbW5sBfWQVHZYVHR0Vlh0VZBUdlhUdHRWWHQSZW5vW6tabW1ub1urWm7odFZYdFWQVHZYVHR0Vlh0VZBUdlhUdAAAAAAIAFwAXBJkEmQAPAB8AAAAyHgIUDgIiLgI0PgEBISIGHQEUFjMhMjY9ATQmAePq1ptbW5vW6tabW1ubAkX+DBUdHRUB9BUdHQSZW5vW6tabW1ub1urWm/5+HRVkFR0dFWQVHQACABcAFwSZBJkADwAzAAAAMh4CFA4CIi4CND4BBCIPAScmIg8BBhQfAQcGFB8BFjI/ARcWMj8BNjQvATc2NC8BAePq1ptbW5vW6tabW1ubAeUZCXh4CRkJjQkJeHgJCY0JGQl4eAkZCY0JCXh4CQmNBJlbm9bq1ptbW5vW6tabrQl4eAkJjQkZCXh4CRkJjQkJeHgJCY0JGQl4eAkZCY0AAgAXABcEmQSZAA8AJAAAADIeAhQOAiIuAjQ+AQEnJiIPAQYUHwEWMjcBNjQvASYiBwHj6tabW1ub1urWm1tbmwEVVAcVCIsHB/IHFQcBdwcHiwcVBwSZW5vW6tabW1ub1urWm/4xVQcHiwgUCPEICAF3BxUIiwcHAAAAAAMAFwAXBJkEmQAPADsASwAAADIeAhQOAiIuAjQ+AQUiDgMVFDsBFjc+ATMyFhUUBgciDgUHBhY7ATI+AzU0LgMTIyIGHQEUFjsBMjY9ATQmAePq1ptbW5vW6tabW1ubAT8dPEIyIRSDHgUGHR8UFw4TARkOGhITDAIBDQ6tBx4oIxgiM0Q8OpYKDw8KlgoPDwSZW5vW6tabW1ub1urWm5ELHi9PMhkFEBQQFRIXFgcIBw4UHCoZCBEQKDhcNi9IKhsJ/eMPCpYKDw8KlgoPAAADABcAFwSZBJkADwAfAD4AAAAyHgIUDgIiLgI0PgEFIyIGHQEUFjsBMjY9ATQmAyMiBh0BFBY7ARUjIgYdARQWMyEyNj0BNCYrARE0JgHj6tabW1ub1urWm1tbmwGWlgoPDwqWCg8PCvoKDw8KS0sKDw8KAV4KDw8KSw8EmVub1urWm1tbm9bq1ptWDwqWCg8PCpYKD/7UDwoyCg/IDwoyCg8PCjIKDwETCg8AAgAAAAAEsASwAC8AXwAAATMyFh0BHgEXMzIWHQEUBisBDgEHFRQGKwEiJj0BLgEnIyImPQE0NjsBPgE3NTQ2ExUUBisBIiY9AQ4BBzMyFh0BFAYrAR4BFzU0NjsBMhYdAT4BNyMiJj0BNDY7AS4BAg2WCg9nlxvCCg8PCsIbl2cPCpYKD2eXG8IKDw8KwhuXZw+5DwqWCg9EZheoCg8PCqgXZkQPCpYKD0RmF6gKDw8KqBdmBLAPCsIbl2cPCpYKD2eXG8IKDw8KwhuXZw8KlgoPZ5cbwgoP/s2oCg8PCqgXZkQPCpYKD0RmF6gKDw8KqBdmRA8KlgoPRGYAAwAXABcEmQSZAA8AGwA/AAAAMh4CFA4CIi4CND4BBCIOARQeATI+ATQmBxcWFA8BFxYUDwEGIi8BBwYiLwEmND8BJyY0PwE2Mh8BNzYyAePq1ptbW5vW6tabW1ubAb/oxXJyxejFcnKaQAcHfHwHB0AHFQd8fAcVB0AHB3x8BwdABxUHfHwHFQSZW5vW6tabW1ub1urWmztyxejFcnLF6MVaQAcVB3x8BxUHQAcHfHwHB0AHFQd8fAcVB0AHB3x8BwAAAAMAFwAXBJkEmQAPABsAMAAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JgcXFhQHAQYiLwEmND8BNjIfATc2MgHj6tabW1ub1urWm1tbmwG/6MVycsXoxXJyg2oHB/7ACBQIyggIagcVB0/FBxUEmVub1urWm1tbm9bq1ps7csXoxXJyxejFfWoHFQf+vwcHywcVB2oICE/FBwAAAAMAFwAXBJkEmQAPABgAIQAAADIeAhQOAiIuAjQ+AQUiDgEVFBcBJhcBFjMyPgE1NAHj6tabW1ub1urWm1tbmwFLdMVyQQJLafX9uGhzdMVyBJlbm9bq1ptbW5vW6tabO3LFdHhpAktB0P24PnLFdHMAAAAAAQAXAFMEsAP5ABUAABMBNhYVESEyFh0BFAYjIREUBicBJjQnAgoQFwImFR0dFf3aFxD99hACRgGrDQoV/t0dFcgVHf7dFQoNAasNJgAAAAABAAAAUwSZA/kAFQAACQEWFAcBBiY1ESEiJj0BNDYzIRE0NgJ/AgoQEP32EBf92hUdHRUCJhcD8f5VDSYN/lUNChUBIx0VyBUdASMVCgAAAAEAtwAABF0EmQAVAAAJARYGIyERFAYrASImNREhIiY3ATYyAqoBqw0KFf7dHRXIFR3+3RUKDQGrDSYEif32EBf92hUdHRUCJhcQAgoQAAAAAQC3ABcEXQSwABUAAAEzMhYVESEyFgcBBiInASY2MyERNDYCJsgVHQEjFQoN/lUNJg3+VQ0KFQEjHQSwHRX92hcQ/fYQEAIKEBcCJhUdAAABAAAAtwSZBF0AFwAACQEWFAcBBiY1EQ4DBz4ENxE0NgJ/AgoQEP32EBdesKWBJAUsW4fHfhcEVf5VDSYN/lUNChUBIwIkRHVNabGdcUYHAQYVCgACAAAAAASwBLAAFQArAAABITIWFREUBi8BBwYiLwEmND8BJyY2ASEiJjURNDYfATc2Mh8BFhQPARcWBgNSASwVHRUOXvkIFAhqBwf5Xg4I/iH+1BUdFQ5e+QgUCGoHB/leDggEsB0V/tQVCA5e+QcHaggUCPleDhX7UB0VASwVCA5e+QcHaggUCPleDhUAAAACAEkASQRnBGcAFQArAAABFxYUDwEXFgYjISImNRE0Nh8BNzYyASEyFhURFAYvAQcGIi8BJjQ/AScmNgP2agcH+V4OCBX+1BUdFQ5e+QgU/QwBLBUdFQ5e+QgUCGoHB/leDggEYGoIFAj5Xg4VHRUBLBUIDl75B/3xHRX+1BUIDl75BwdqCBQI+V4OFQAAAAADABcAFwSZBJkADwAfAC8AAAAyHgIUDgIiLgI0PgEFIyIGFxMeATsBMjY3EzYmAyMiBh0BFBY7ATI2PQE0JgHj6tabW1ub1urWm1tbmwGz0BQYBDoEIxQ2FCMEOgQYMZYKDw8KlgoPDwSZW5vW6tabW1ub1urWm7odFP7SFB0dFAEuFB3+DA8KlgoPDwqWCg8AAAAABQAAAAAEsASwAEkAVQBhAGgAbwAAATIWHwEWHwEWFxY3Nj8BNjc2MzIWHwEWHwIeATsBMhYdARQGKwEiBh0BIREjESE1NCYrASImPQE0NjsBMjY1ND8BNjc+BAUHBhY7ATI2LwEuAQUnJgYPAQYWOwEyNhMhIiY1ESkBERQGIyERAQQJFAUFFhbEFQ8dCAsmxBYXERUXMA0NDgQZCAEPCj0KDw8KMgoP/nDI/nAPCjIKDw8KPQsOCRkFDgIGFRYfAp2mBwQK2woKAzMDEP41sQgQAzMDCgrnCwMe/okKDwGQAlgPCv6JBLAEAgIKDXYNCxUJDRZ2DQoHIREQFRh7LAkLDwoyCg8PCq8BLP7UrwoPDwoyCg8GBQQwgBkUAwgWEQ55ogcKDgqVCgSqnQcECo8KDgr8cg8KAXf+iQoPAZAAAAAAAgAAAAwErwSmACsASQAAATYWFQYCDgQuAScmByYOAQ8BBiY1NDc+ATc+AScuAT4BNz4GFyYGBw4BDwEOBAcOARY2Nz4CNz4DNz4BBI0IGgItQmxhi2KORDg9EQQRMxuZGhYqCFUYEyADCQIQOjEnUmFch3vAJQgdHyaiPT44XHRZUhcYDhItIRmKcVtGYWtbKRYEBKYDEwiy/t3IlVgxEQgLCwwBAQIbG5kYEyJAJghKFRE8Hzdff4U/M0o1JSMbL0QJGCYvcSEhHjZST2c1ODwEJygeW0AxJUBff1UyFAABAF0AHgRyBM8ATwAAAQ4BHgQXLgc+ATceAwYHDgQHBicmNzY3PgQuAScWDgMmJy4BJyY+BDcGHgM3PgEuAicmPgMCjScfCic4R0IgBBsKGAoQAwEJEg5gikggBhANPkpTPhZINx8SBgsNJysiCRZOQQoVNU1bYC9QZwICBAUWITsoCAYdJzIYHw8YIiYHDyJJYlkEz0OAZVxEOSQMBzgXOB42IzElKRIqg5Gnl0o3Z0c6IAYWCwYNAwQFIDhHXGF1OWiqb0sdBxUknF0XNTQ8PEUiNWNROBYJDS5AQVUhVZloUSkAAAAAA//cAGoE1ARGABsAPwBRAAAAMh4FFA4FIi4FND4EBSYGFxYVFAYiJjU0NzYmBwYHDgEXHgQyPgM3NiYnJgUHDgEXFhcWNj8BNiYnJicuAQIGpJ17bk85HBw6T257naKde25POhwcOU9uewIPDwYIGbD4sBcIBw5GWg0ECxYyWl+DiINfWjIWCwQMWv3/Iw8JCSU4EC0OIw4DDywtCyIERi1JXGJcSSpJXGJcSS0tSVxiXEkqSVxiXEncDwYTOT58sLB8OzcTBg9FcxAxEiRGXkQxMEVeRSQSMRF1HiQPLxJEMA0EDyIPJQ8sSRIEAAAABP/cAAAE1ASwABQAJwA7AEwAACEjNy4ENTQ+BTMyFzczEzceARUUDgMHNz4BNzYmJyYlBgcOARceBBc3LgE1NDc2JhcHDgEXFhcWNj8CJyYnLgECUJQfW6l2WSwcOU9ue51SPUEglCYvbIknUGqYUi5NdiYLBAw2/VFGWg0ECxIqSExoNSlrjxcIB3wjDwkJJTgQLQ4MFgMsLQsieBRhdHpiGxVJXGJcSS0Pef5StVXWNBpacm5jGq0xiD8SMRFGckVzEDESHjxRQTkNmhKnbjs3EwZwJA8vEkQwDQQPC1YELEkSBAAAAAP/ngAABRIEqwALABgAKAAAJwE2FhcBFgYjISImJSE1NDY7ATIWHQEhAQczMhYPAQ4BKwEiJi8BJjZaAoIUOBQCghUbJfryJRsBCgFZDwqWCg8BWf5DaNAUGAQ6BCMUNhQjBDoEGGQEKh8FIfvgIEdEhEsKDw8KSwLT3x0U/BQdHRT8FB0AAAABAGQAFQSwBLAAKAAAADIWFREBHgEdARQGJyURFh0BFAYvAQcGJj0BNDcRBQYmPQE0NjcBETQCTHxYAWsPFhgR/plkGhPNzRMaZP6ZERgWDwFrBLBYPv6t/rsOMRQpFA0M+f75XRRAFRAJgIAJEBVAFF0BB/kMDRQpFDEOAUUBUz4AAAARAAAAAARMBLAAHQAnACsALwAzADcAOwA/AEMARwBLAE8AUwBXAFsAXwBjAAABMzIWHQEzMhYdASE1NDY7ATU0NjsBMhYdASE1NDYBERQGIyEiJjURFxUzNTMVMzUzFTM1MxUzNTMVMzUFFTM1MxUzNTMVMzUzFTM1MxUzNQUVMzUzFTM1MxUzNTMVMzUzFTM1A1JkFR0yFR37tB0VMh0VZBUdAfQdAQ8dFfwYFR1kZGRkZGRkZGRk/HxkZGRkZGRkZGT8fGRkZGRkZGRkZASwHRUyHRWWlhUdMhUdHRUyMhUd/nD9EhUdHRUC7shkZGRkZGRkZGRkyGRkZGRkZGRkZGTIZGRkZGRkZGRkZAAAAAMAAAAZBXcElwAZACUANwAAARcWFA8BBiY9ASMBISImPQE0NjsBATM1NDYBBycjIiY9ATQ2MyEBFxYUDwEGJj0BIyc3FzM1NDYEb/kPD/kOFZ/9qP7dFR0dFdECWPEV/amNetEVHR0VASMDGvkPD/kOFfG1jXqfFQSN5g4qDuYOCBWW/agdFWQVHQJYlhUI/piNeh0VZBUd/k3mDioO5g4IFZa1jXqWFQgAAAABAAAAAASwBEwAEgAAEyEyFhURFAYjIQERIyImNRE0NmQD6Ck7Oyn9rP7QZCk7OwRMOyn9qCk7/tQBLDspAlgpOwAAAAMAZAAABEwEsAAJABMAPwAAEzMyFh0BITU0NiEzMhYdASE1NDYBERQOBSIuBTURIRUUFRwBHgYyPgYmNTQ9AZbIFR3+1B0C0cgVHf7UHQEPBhgoTGacwJxmTCgYBgEsAwcNFB8nNkI2Jx8TDwUFAQSwHRX6+hUdHRX6+hUd/nD+1ClJalZcPigoPlxWakkpASz6CRIVKyclIRsWEAgJEBccISUnKhURCPoAAAAB//8A1ARMA8IABQAAAQcJAScBBEzG/p/+n8UCJwGbxwFh/p/HAicAAQAAAO4ETQPcAAUAAAkCNwkBBE392v3ZxgFhAWEDFf3ZAifH/p8BYQAAAAAC/1EAZAVfA+gAFAApAAABITIWFREzMhYPAQYiLwEmNjsBESElFxYGKwERIRchIiY1ESMiJj8BNjIBlALqFR2WFQgO5g4qDuYOCBWW/oP+HOYOCBWWAYHX/RIVHZYVCA7mDioD6B0V/dkVDvkPD/kOFQGRuPkOFf5wyB0VAiYVDvkPAAABAAYAAASeBLAAMAAAEzMyFh8BITIWBwMOASMhFyEyFhQGKwEVFAYiJj0BIRUUBiImPQEjIiYvAQMjIiY0NjheERwEJgOAGB4FZAUsIf2HMAIXFR0dFTIdKh3+1B0qHR8SHQYFyTYUHh4EsBYQoiUY/iUVK8gdKh0yFR0dFTIyFR0dFTIUCQoDwR0qHQAAAAACAAAAAASwBEwACwAPAAABFSE1MzQ2MyEyFhUFIREhBLD7UMg7KQEsKTv9RASw+1AD6GRkKTs7Kcj84AACAAAAAAXcBEwADAAQAAATAxEzNDYzITIWFSEVBQEhAcjIyDspASwqOgH0ASz+1PtQASwDIP5wAlgpOzspyGT9RAK8AAEBRQAAA2sErwAbAAABFxYGKwERMzIWDwEGIi8BJjY7AREjIiY/ATYyAnvmDggVlpYVCA7mDioO5g4IFZaWFQgO5g4qBKD5DhX9pxUO+Q8P+Q4VAlkVDvkPAAAAAQABAUQErwNrABsAAAEXFhQPAQYmPQEhFRQGLwEmND8BNhYdASE1NDYDqPkODvkPFf2oFQ/5Dg75DxUCWBUDYOUPKQ/lDwkUl5cUCQ/lDykP5Q8JFZWVFQkAAAAEAAAAAASwBLAACQAZAB0AIQAAAQMuASMhIgYHAwUhIgYdARQWMyEyNj0BNCYFNTMVMzUzFQSRrAUkFP1gFCQFrAQt/BgpOzspA+gpOzv+q2RkZAGQAtwXLSgV/R1kOylkKTs7KWQpO8hkZGRkAAAAA/+cAGQEsARMAAsAIwAxAAAAMhYVERQGIiY1ETQDJSMTFgYjIisBIiYnAj0BNDU0PgE7ASUBFSIuAz0BND4CNwRpKh0dKh1k/V0mLwMRFQUCVBQdBDcCCwzIAqP8GAQOIhoWFR0dCwRMHRX8rhUdHRUDUhX8mcj+7BAIHBUBUQ76AgQQDw36/tT6AQsTKRwyGigUDAEAAAACAEoAAARmBLAALAA1AAABMzIWDwEeARcTFzMyFhQGBw4EIyIuBC8BLgE0NjsBNxM+ATcnJjYDFjMyNw4BIiYCKV4UEgYSU3oPP3YRExwaEggeZGqfTzl0XFU+LwwLEhocExF2Pw96UxIGEyQyNDUxDDdGOASwFRMlE39N/rmtHSkoBwQLHBYSCg4REg4FBAgoKR2tAUdNfhQgExr7vgYGMT09AAEAFAAUBJwEnAAXAAABNwcXBxcHFycHJwcnBzcnNyc3Jxc3FzcDIOBO6rS06k7gLZubLeBO6rS06k7gLZubA7JO4C2bmy3gTuq0tOpO4C2bmy3gTuq0tAADAAAAZASwBLAAIQAtAD0AAAEzMhYdAQchMhYdARQHAw4BKwEiJi8BIyImNRE0PwI+ARcPAREzFzMTNSE3NQEzMhYVERQGKwEiJjURNDYCijIoPBwBSCg8He4QLBf6B0YfHz0tNxSRYA0xG2SWZIjW+v4+Mv12ZBUdHRVkFR0dBLBRLJZ9USxkLR3+qBghMhkZJCcBkCQbxMYcKGTU1f6JZAF3feGv/tQdFf4MFR0dFQH0FR0AAAAAAwAAAAAEsARMACAAMAA8AAABMzIWFxMWHQEUBiMhFh0BFAYrASImLwImNRE0NjsBNgUzMhYVERQGKwEiJjURNDYhByMRHwEzNSchNQMCWPoXLBDuHTwo/rgcPCgyGzENYJEUNy09fP3pZBUdHRVkFR0dAl+IZJZkMjIBwvoETCEY/qgdLWQsUXYHlixRKBzGxBskAZAnJGRkHRX+DBUdHRUB9BUdZP6J1dSv4X0BdwADAAAAZAUOBE8AGwA3AEcAAAElNh8BHgEPASEyFhQGKwEDDgEjISImNRE0NjcXERchEz4BOwEyNiYjISoDLgQnJj8BJwUzMhYVERQGKwEiJjURNDYBZAFrHxZuDQEMVAEuVGxuVGqDBhsP/qoHphwOOmQBJYMGGw/LFRMSFv44AgoCCQMHAwUDAQwRklb9T2QVHR0VZBUdHQNp5hAWcA0mD3lMkE7+rRUoog0CDRElCkj+CVkBUxUoMjIBAgIDBQIZFrdT5B0V/gwVHR0VAfQVHQAAAAP/nABkBLAETwAdADYARgAAAQUeBBURFAYjISImJwMjIiY0NjMhJyY2PwE2BxcWBw4FKgIjIRUzMhYXEyE3ESUFMzIWFREUBisBIiY1ETQ2AdsBbgIIFBANrAf+qg8bBoNqVW1sVAEuVQsBDW4WSpIRDAIDBQMHAwkDCgH+Jd0PHAaCASZq/qoCUGQVHR0VZBUdHQRP5gEFEBEXC/3zDaIoFQFTTpBMeQ8mDXAWrrcWGQIFAwICAWQoFf6tWQH37OQdFf4MFR0dFQH0FR0AAAADAGEAAARMBQ4AGwA3AEcAAAAyFh0BBR4BFREUBiMhIiYvAQMmPwE+AR8BETQXNTQmBhURHAMOBAcGLwEHEyE3ESUuAQMhMhYdARQGIyEiJj0BNDYB3pBOAVMVKKIN/fMRJQoJ5hAWcA0mD3nGMjIBAgIDBQIZFrdT7AH3Wf6tFSiWAfQVHR0V/gwVHR0FDm5UaoMGGw/+qgemHA4OAWsfFm4NAQxUAS5U1ssVExIW/jgCCgIJAwcDBQMBDBGSVv6tZAElgwYb/QsdFWQVHR0VZBUdAAP//QAGA+gFFAAPAC0ASQAAASEyNj0BNCYjISIGHQEUFgEVFAYiJjURBwYmLwEmNxM+BDMhMhYVERQGBwEDFzc2Fx4FHAIVERQWNj0BNDY3JREnAV4B9BUdHRX+DBUdHQEPTpBMeQ8mDXAWEOYBBRARFwsCDQ2iKBX9iexTtxYZAgUDAgIBMjIoFQFTWQRMHRVkFR0dFWQVHfzmalRubFQBLlQMAQ1uFh8BawIIEw8Mpgf+qg8bBgHP/q1WkhEMAQMFAwcDCQIKAv44FhITFcsPGwaDASVkAAIAFgAWBJoEmgAPACUAAAAyHgIUDgIiLgI0PgEBJSYGHQEhIgYdARQWMyEVFBY3JTY0AeLs1ptbW5vW7NabW1ubAob+7RAX/u0KDw8KARMXEAETEASaW5vW7NabW1ub1uzWm/453w0KFYkPCpYKD4kVCg3fDSYAAAIAFgAWBJoEmgAPACUAAAAyHgIUDgIiLgI0PgENAQYUFwUWNj0BITI2PQE0JiMhNTQmAeLs1ptbW5vW7NabW1ubASX+7RAQARMQFwETCg8PCv7tFwSaW5vW7NabW1ub1uzWm+jfDSYN3w0KFYkPCpYKD4kVCgAAAAIAFgAWBJoEmgAPACUAAAAyHgIUDgIiLgI0PgEBAyYiBwMGFjsBERQWOwEyNjURMzI2AeLs1ptbW5vW7NabW1ubAkvfDSYN3w0KFYkPCpYKD4kVCgSaW5vW7NabW1ub1uzWm/5AARMQEP7tEBf+7QoPDwoBExcAAAIAFgAWBJoEmgAPACUAAAAyHgIUDgIiLgI0PgEFIyIGFREjIgYXExYyNxM2JisBETQmAeLs1ptbW5vW7NabW1ubAZeWCg+JFQoN3w0mDd8NChWJDwSaW5vW7NabW1ub1uzWm7sPCv7tFxD+7RAQARMQFwETCg8AAAMAGAAYBJgEmAAPAJYApgAAADIeAhQOAiIuAjQ+ASUOAwcGJgcOAQcGFgcOAQcGFgcUFgcyHgEXHgIXHgI3Fg4BFx4CFxQGFBcWNz4CNy4BJy4BJyIOAgcGJyY2NS4BJzYuAQYHBicmNzY3HgIXHgMfAT4CJyY+ATc+AzcmNzIWMjY3LgMnND4CJiceAT8BNi4CJwYHFB4BFS4CJz4BNxYyPgEB5OjVm1xcm9Xo1ZtcXJsBZA8rHDoKDz0PFD8DAxMBAzEFCRwGIgEMFhkHECIvCxU/OR0HFBkDDRQjEwcFaHUeISQDDTAMD0UREi4oLBAzDwQBBikEAQMLGhIXExMLBhAGKBsGBxYVEwYFAgsFAwMNFwQGCQcYFgYQCCARFwkKKiFBCwQCAQMDHzcLDAUdLDgNEiEQEgg/KhADGgMKEgoRBJhcm9Xo1ZtcXJvV6NWbEQwRBwkCAwYFBycPCxcHInIWInYcCUcYChQECA4QBAkuHgQPJioRFRscBAcSCgwCch0kPiAIAQcHEAsBAgsLIxcBMQENCQIPHxkCFBkdHB4QBgEBBwoMGBENBAMMJSAQEhYXDQ4qFBkKEhIDCQsXJxQiBgEOCQwHAQ0DBAUcJAwSCwRnETIoAwEJCwsLJQcKDBEAAAAAAQAAAAIErwSFABYAAAE2FwUXNxYGBw4BJwEGIi8BJjQ3ASY2AvSkjv79kfsGUE08hjv9rA8rD28PDwJYIk8EhVxliuh+WYcrIgsW/awQEG4PKxACV2XJAAYAAABgBLAErAAPABMAIwAnADcAOwAAEyEyFh0BFAYjISImPQE0NgUjFTMFITIWHQEUBiMhIiY9ATQ2BSEVIQUhMhYdARQGIyEiJj0BNDYFIRUhZAPoKTs7KfwYKTs7BBHIyPwYA+gpOzsp/BgpOzsEEf4MAfT8GAPoKTs7KfwYKTs7BBH+1AEsBKw7KWQpOzspZCk7ZGTIOylkKTs7KWQpO2RkyDspZCk7OylkKTtkZAAAAAIAZAAABEwEsAALABEAABMhMhYUBiMhIiY0NgERBxEBIZYDhBUdHRX8fBUdHQI7yP6iA4QEsB0qHR0qHf1E/tTIAfQB9AAAAAMAAABkBLAEsAAXABsAJQAAATMyFh0BITIWFREhNSMVIRE0NjMhNTQ2FxUzNQEVFAYjISImPQEB9MgpOwEsKTv+DMj+DDspASw7KcgB9Dsp/BgpOwSwOylkOyn+cGRkAZApO2QpO2RkZP1EyCk7OynIAAAABAAAAAAEsASwABUAKwBBAFcAABMhMhYPARcWFA8BBiIvAQcGJjURNDYpATIWFREUBi8BBwYiLwEmND8BJyY2ARcWFA8BFxYGIyEiJjURNDYfATc2MgU3NhYVERQGIyEiJj8BJyY0PwE2MhcyASwVCA5exwcHaggUCMdeDhUdAzUBLBUdFQ5exwgUCGoHB8deDgj+L2oHB8deDggV/tQVHRUOXscIFALLXg4VHRX+1BUIDl7HBwdqCBQIBLAVDl7HCBQIagcHx14OCBUBLBUdHRX+1BUIDl7HBwdqCBQIx14OFf0maggUCMdeDhUdFQEsFQgOXscHzl4OCBX+1BUdFQ5exwgUCGoHBwAAAAYAAAAABKgEqAAPABsAIwA7AEMASwAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JiQyFhQGIiY0JDIWFAYjIicHFhUUBiImNTQ2PwImNTQEMhYUBiImNCQyFhQGIiY0Advy3Z9fX5/d8t2gXl6gAcbgv29vv+C/b2/+LS0gIC0gAUwtICAWDg83ETNIMykfegEJ/octICAtIAIdLSAgLSAEqF+f3fLdoF5eoN3y3Z9Xb7/gv29vv+C/BiAtISEtICAtIQqRFxwkMzMkIDEFfgEODhekIC0gIC0gIC0gIC0AAf/YAFoEuQS8AFsAACUBNjc2JicmIyIOAwcABw4EFx4BMzI3ATYnLgEjIgcGBwEOASY0NwA3PgEzMhceARcWBgcOBgcGIyImJyY2NwE2NzYzMhceARcWBgcBDgEnLgECIgHVWwgHdl8WGSJBMD8hIP6IDx4eLRMNBQlZN0ozAiQkEAcdEhoYDRr+qw8pHA4BRyIjQS4ODyw9DQ4YIwwod26La1YOOEBGdiIwGkQB/0coW2tQSE5nDxE4Qv4eDyoQEAOtAdZbZWKbEQQUGjIhH/6JDxsdNSg3HT5CMwIkJCcQFBcMGv6uDwEcKQ4BTSIjIQEINykvYyMLKnhuiWZMBxtAOU6+RAH/SBg3ISSGV121Qv4kDwIPDyYAAAACAGQAWASvBEQAGQBEAAABPgIeAhUUDgMHLgQ1ND4CHgEFIg4DIi4DIyIGFRQeAhcWFx4EMj4DNzY3PgQ1NCYCiTB7eHVYNkN5hKg+PqeFeEM4WnZ4eQEjIT8yLSohJyktPyJDbxtBMjMPBw86KzEhDSIzKUAMBAgrKT8dF2oDtURIBS1TdkA5eYB/slVVsn+AeTlAdlMtBUgtJjY1JiY1NiZvTRc4SjQxDwcOPCouGBgwKEALBAkpKkQqMhNPbQACADn/8gR3BL4AFwAuAAAAMh8BFhUUBg8BJi8BNycBFwcvASY0NwEDNxYfARYUBwEGIi8BJjQ/ARYfAQcXAQKru0KNQjgiHR8uEl/3/nvUaRONQkIBGxJpCgmNQkL+5UK6Qo1CQjcdLhJf9wGFBL5CjUJeKmsiHTUuEl/4/nvUahKNQrpCARv+RmkICY1CukL+5UJCjUK7Qjc3LxFf+AGFAAAAAAMAyAAAA+gEsAARABUAHQAAADIeAhURFAYjISImNRE0PgEHESERACIGFBYyNjQCBqqaZDo7Kf2oKTs8Zj4CWP7/Vj09Vj0EsB4uMhX8Ryk7OykDuRUzLar9RAK8/RY9Vj09VgABAAAAAASwBLAAFgAACQEWFAYiLwEBEScBBRMBJyEBJyY0NjIDhgEbDx0qDiT+6dT+zP7oywEz0gEsAQsjDx0qBKH+5g8qHQ8j/vX+1NL+zcsBGAE01AEXJA4qHQAAAAADAScAEQQJBOAAMgBAAEsAAAEVHgQXIy4DJxEXHgQVFAYHFSM1JicuASczHgEXEScuBDU0PgI3NRkBDgMVFB4DFxYXET4ENC4CArwmRVI8LAKfBA0dMydAIjxQNyiym2SWVygZA4sFV0obLkJOMCAyVWg6HSoqFQ4TJhkZCWgWKTEiGBkzNwTgTgUTLD9pQiQuLBsH/s0NBxMtPGQ+i6oMTU8QVyhrVk1iEAFPCA4ZLzlYNkZwSCoGTf4SARIEDh02Jh0rGRQIBgPQ/soCCRYgNEM0JRkAAAABAGQAZgOUBK0ASgAAATIeARUjNC4CIyIGBwYVFB4BFxYXMxUjFgYHBgc+ATM2FjMyNxcOAyMiLgEHDgEPASc+BTc+AScjNTMmJy4CPgE3NgIxVJlemSc8OxolVBQpGxoYBgPxxQgVFS02ImIWIIwiUzUyHzY4HCAXanQmJ1YYFzcEGAcTDBEJMAwk3aYXFQcKAg4tJGEErVCLTig/IhIdFSw5GkowKgkFZDKCHj4yCg8BIh6TExcIASIfBAMaDAuRAxAFDQsRCjePR2QvORQrREFMIVgAAAACABn//wSXBLAADwAfAAABMzIWDwEGIi8BJjY7AREzBRcWBisBESMRIyImPwE2MgGQlhUIDuYOKg7mDggVlsgCF+YOCBWWyJYVCA7mDioBLBYO+g8P+g4WA4QQ+Q4V/HwDhBUO+Q8AAAQAGf//A+gEsAAHABcAGwAlAAABIzUjFSMRIQEzMhYPAQYiLwEmNjsBETMFFTM1EwczFSE1NyM1IQPoZGRkASz9qJYVCA7mDioO5g4IFZbIAZFkY8jI/tTIyAEsArxkZAH0/HwWDvoPD/oOFgOEZMjI/RL6ZJb6ZAAAAAAEABn//wPoBLAADwAZACEAJQAAATMyFg8BBiIvASY2OwERMwUHMxUhNTcjNSERIzUjFSMRIQcVMzUBkJYVCA7mDioO5g4IFZbIAljIyP7UyMgBLGRkZAEsx2QBLBYO+g8P+g4WA4SW+mSW+mT7UGRkAfRkyMgAAAAEABn//wRMBLAADwAVABsAHwAAATMyFg8BBiIvASY2OwERMwEjESM1MxMjNSMRIQcVMzUBkJYVCA7mDioO5g4IFZbIAlhkZMhkZMgBLMdkASwWDvoPD/oOFgOE/gwBkGT7UGQBkGTIyAAAAAAEABn//wRMBLAADwAVABkAHwAAATMyFg8BBiIvASY2OwERMwEjNSMRIQcVMzUDIxEjNTMBkJYVCA7mDioO5g4IFZbIArxkyAEsx2QBZGTIASwWDvoPD/oOFgOE/gxkAZBkyMj7tAGQZAAAAAAFABn//wSwBLAADwATABcAGwAfAAABMzIWDwEGIi8BJjY7AREzBSM1MxMhNSETITUhEyE1IQGQlhUIDuYOKg7mDggVlsgB9MjIZP7UASxk/nABkGT+DAH0ASwWDvoPD/oOFgOEyMj+DMj+DMj+DMgABQAZ//8EsASwAA8AEwAXABsAHwAAATMyFg8BBiIvASY2OwERMwUhNSEDITUhAyE1IQMjNTMBkJYVCA7mDioO5g4IFZbIAyD+DAH0ZP5wAZBk/tQBLGTIyAEsFg76Dw/6DhYDhMjI/gzI/gzI/gzIAAIAAAAABEwETAAPAB8AAAEhMhYVERQGIyEiJjURNDYFISIGFREUFjMhMjY1ETQmAV4BkKK8u6P+cKW5uQJn/gwpOzspAfQpOzsETLuj/nClubmlAZClucg7Kf4MKTs7KQH0KTsAAAAAAwAAAAAETARMAA8AHwArAAABITIWFREUBiMhIiY1ETQ2BSEiBhURFBYzITI2NRE0JgUXFhQPAQYmNRE0NgFeAZClubml/nCju7wCZP4MKTs7KQH0KTs7/m/9ERH9EBgYBEy5pf5wpbm5pQGQo7vIOyn+DCk7OykB9Ck7gr4MJAy+DAsVAZAVCwAAAAADAAAAAARMBEwADwAfACsAAAEhMhYVERQGIyEiJjURNDYFISIGFREUFjMhMjY1ETQmBSEyFg8BBiIvASY2AV4BkKO7uaX+cKW5uQJn/gwpOzspAfQpOzv+FQGQFQsMvgwkDL4MCwRMvKL+cKW5uaUBkKO7yDsp/gwpOzspAfQpO8gYEP0REf0QGAAAAAMAAAAABEwETAAPAB8AKwAAASEyFhURFAYjISImNRE0NgUhIgYVERQWMyEyNjURNCYFFxYGIyEiJj8BNjIBXgGQpbm5pf5wo7u5Amf+DCk7OykB9Ck7O/77vgwLFf5wFQsMvgwkBEy5pf5wo7u8ogGQpbnIOyn+DCk7OykB9Ck7z/0QGBgQ/REAAAAAAgAAAAAFFARMAB8ANQAAASEyFhURFAYjISImPQE0NjMhMjY1ETQmIyEiJj0BNDYHARYUBwEGJj0BIyImPQE0NjsBNTQ2AiYBkKW5uaX+cBUdHRUBwik7Oyn+PhUdHb8BRBAQ/rwQFvoVHR0V+hYETLml/nCluR0VZBUdOykB9Ck7HRVkFR3p/uQOJg7+5A4KFZYdFcgVHZYVCgAAAQDZAAID1wSeACMAAAEXFgcGAgclMhYHIggBBwYrAScmNz4BPwEhIicmNzYANjc2MwMZCQgDA5gCASwYEQ4B/vf+8wQMDgkJCQUCUCcn/tIXCAoQSwENuwUJEASeCQoRC/5TBwEjEv7K/sUFDwgLFQnlbm4TFRRWAS/TBhAAAAACAAAAAAT+BEwAHwA1AAABITIWHQEUBiMhIgYVERQWMyEyFh0BFAYjISImNRE0NgUBFhQHAQYmPQEjIiY9ATQ2OwE1NDYBXgGQFR0dFf4+KTs7KQHCFR0dFf5wpbm5AvEBRBAQ/rwQFvoVHR0V+hYETB0VZBUdOyn+DCk7HRVkFR25pQGQpbnp/uQOJg7+5A4KFZYdFcgVHZYVCgACAAAAAASwBLAAFQAxAAABITIWFREUBi8BAQYiLwEmNDcBJyY2ASMiBhURFBYzITI2PQE3ERQGIyEiJjURNDYzIQLuAZAVHRUObf7IDykPjQ8PAThtDgj+75wpOzspAfQpO8i7o/5wpbm5pQEsBLAdFf5wFQgObf7IDw+NDykPAThtDhX+1Dsp/gwpOzsplMj+1qW5uaUBkKW5AAADAA4ADgSiBKIADwAbACMAAAAyHgIUDgIiLgI0PgEEIg4BFB4BMj4BNCYEMhYUBiImNAHh7tmdXV2d2e7ZnV1dnQHD5sJxccLmwnFx/nugcnKgcgSiXZ3Z7tmdXV2d2e7ZnUdxwubCcXHC5sJzcqBycqAAAAMAAAAABEwEsAAVAB8AIwAAATMyFhURMzIWBwEGIicBJjY7ARE0NgEhMhYdASE1NDYFFTM1AcLIFR31FAoO/oEOJw3+hQ0JFfod/oUD6BUd+7QdA2dkBLAdFf6iFg/+Vg8PAaoPFgFeFR38fB0V+voVHWQyMgAAAAMAAAAABEwErAAVAB8AIwAACQEWBisBFRQGKwEiJj0BIyImNwE+AQEhMhYdASE1NDYFFTM1AkcBeg4KFfQiFsgUGPoUCw4Bfw4n/fkD6BUd+7QdA2dkBJ7+TQ8g+hQeHRX6IQ8BrxAC/H8dFfr6FR1kMjIAAwAAAAAETARLABQAHgAiAAAJATYyHwEWFAcBBiInASY0PwE2MhcDITIWHQEhNTQ2BRUzNQGMAXEHFQeLBwf98wcVB/7cBweLCBUH1APoFR37tB0DZ2QC0wFxBweLCBUH/fMICAEjCBQIiwcH/dIdFfr6FR1kMjIABAAAAAAETASbAAkAGQAjACcAABM3NjIfAQcnJjQFNzYWFQMOASMFIiY/ASc3ASEyFh0BITU0NgUVMzWHjg4qDk3UTQ4CFtIOFQIBHRX9qxUIDtCa1P49A+gVHfu0HQNnZAP/jg4OTdRMDyqa0g4IFf2pFB4BFQ7Qm9T9Oh0V+voVHWQyMgAAAAQAAAAABEwEsAAPABkAIwAnAAABBR4BFRMUBi8BByc3JyY2EwcGIi8BJjQ/AQEhMhYdASE1NDYFFTM1AV4CVxQeARUO0JvUm9IOCMNMDyoOjg4OTf76A+gVHfu0HQNnZASwAgEdFf2rFQgO0JrUmtIOFf1QTQ4Ojg4qDk3+WB0V+voVHWQyMgACAAT/7ASwBK8ABQAIAAAlCQERIQkBFQEEsP4d/sb+cQSs/TMCq2cBFP5xAacDHPz55gO5AAAAAAIAAABkBEwEsAAVABkAAAERFAYrAREhESMiJjURNDY7AREhETMHIzUzBEwdFZb9RJYVHR0V+gH0ZMhkZAPo/K4VHQGQ/nAdFQPoFB7+1AEsyMgAAAMAAABFBN0EsAAWABoALwAAAQcBJyYiDwEhESMiJjURNDY7AREhETMHIzUzARcWFAcBBiIvASY0PwE2Mh8BATYyBEwC/tVfCRkJlf7IlhUdHRX6AfRkyGRkAbBqBwf+XAgUCMoICGoHFQdPASkHFQPolf7VXwkJk/5wHRUD6BQe/tQBLMjI/c5qBxUH/lsHB8sHFQdqCAhPASkHAAMAAAANBQcEsAAWABoAPgAAAREHJy4BBwEhESMiJjURNDY7AREhETMHIzUzARcWFA8BFxYUDwEGIi8BBwYiLwEmND8BJyY0PwE2Mh8BNzYyBExnhg8lEP72/reWFR0dFfoB9GTIZGQB9kYPD4ODDw9GDykPg4MPKQ9GDw+Dgw8PRg8pD4ODDykD6P7zZ4YPAw7+9v5wHRUD6BQe/tQBLMjI/YxGDykPg4MPKQ9GDw+Dgw8PRg8pD4ODDykPRg8Pg4MPAAADAAAAFQSXBLAAFQAZAC8AAAERISIGHQEhESMiJjURNDY7AREhETMHIzUzEzMyFh0BMzIWDwEGIi8BJjY7ATU0NgRM/qIVHf4MlhUdHRX6AfRkyGRklmQVHZYVCA7mDioO5g4IFZYdA+j+1B0Vlv5wHRUD6BQe/tQBLMjI/agdFfoVDuYODuYOFfoVHQAAAAADAAAAAASXBLAAFQAZAC8AAAERJyYiBwEhESMiJjURNDY7AREhETMHIzUzExcWBisBFRQGKwEiJj0BIyImPwE2MgRMpQ4qDv75/m6WFR0dFfoB9GTIZGTr5g4IFZYdFWQVHZYVCA7mDioD6P5wpQ8P/vf+cB0VA+gUHv7UASzIyP2F5Q8V+hQeHhT6FQ/lDwADAAAAyASwBEwACQATABcAABMhMhYdASE1NDYBERQGIyEiJjURExUhNTIETBUd+1AdBJMdFfu0FR1kAZAETB0VlpYVHf7U/doVHR0VAib+1MjIAAAGAAMAfQStBJcADwAZAB0ALQAxADsAAAEXFhQPAQYmPQEhNSE1NDYBIyImPQE0NjsBFyM1MwE3NhYdASEVIRUUBi8BJjQFIzU7AjIWHQEUBisBA6f4Dg74DhX+cAGQFf0vMhUdHRUyyGRk/oL3DhUBkP5wFQ73DwOBZGRkMxQdHRQzBI3mDioO5g4IFZbIlhUI/oUdFWQVHcjI/cvmDggVlsiWFQgO5g4qecgdFWQVHQAAAAACAGQAAASwBLAAFgBRAAABJTYWFREUBisBIiY1ES4ENRE0NiUyFh8BERQOAg8BERQGKwEiJjURLgQ1ETQ+AzMyFh8BETMRPAE+AjMyFh8BETMRND4DA14BFBklHRXIFR0EDiIaFiX+4RYZAgEVHR0LCh0VyBUdBA4iGhYBBwoTDRQZAgNkBQkVDxcZAQFkAQUJFQQxdBIUH/uuFR0dFQGNAQgbHzUeAWcfRJEZDA3+Phw/MSkLC/5BFR0dFQG/BA8uLkAcAcICBxENCxkMDf6iAV4CBxENCxkMDf6iAV4CBxENCwABAGQAAASwBEwAMwAAARUiDgMVERQWHwEVITUyNjURIREUFjMVITUyPgM1ETQmLwE1IRUiBhURIRE0JiM1BLAEDiIaFjIZGf5wSxn+DBlL/nAEDiIaFjIZGQGQSxkB9BlLBEw4AQUKFA78iBYZAQI4OA0lAYr+diUNODgBBQoUDgN4FhkBAjg4DSX+dgGKJQ04AAAABgAAAAAETARMAAwAHAAgACQAKAA0AAABITIWHQEjBTUnITchBSEyFhURFAYjISImNRE0NhcVITUBBTUlBRUhNQUVFAYjIQchJyE3MwKjAXcVHWn+2cj+cGQBd/4lASwpOzsp/tQpOzspASwCvP5wAZD8GAEsArwdFf6JZP6JZAGQyGkD6B0VlmJiyGTIOyn+DCk7OykB9Ck7ZMjI/veFo4XGyMhm+BUdZGTIAAEAEAAQBJ8EnwAmAAATNzYWHwEWBg8BHgEXNz4BHwEeAQ8BBiIuBicuBTcRohEuDosOBhF3ZvyNdxEzE8ATBxGjAw0uMUxPZWZ4O0p3RjITCwED76IRBhPCFDERdo78ZXYRBA6IDi8RogEECBUgNUNjO0qZfHNVQBAAAAACAAAAAASwBEwAIwBBAAAAMh4EHwEVFAYvAS4BPQEmIAcVFAYPAQYmPQE+BRIyHgIfARUBHgEdARQGIyEiJj0BNDY3ATU0PgIB/LimdWQ/LAkJHRTKFB2N/sKNHRTKFB0DDTE7ZnTKcFImFgEBAW0OFR0V+7QVHRUOAW0CFiYETBUhKCgiCgrIFRgDIgMiFZIYGJIVIgMiAxgVyAQNJyQrIP7kExwcCgoy/tEPMhTUFR0dFdQUMg8BLzIEDSEZAAADAAAAAASwBLAADQAdACcAAAEHIScRMxUzNTMVMzUzASEyFhQGKwEXITcjIiY0NgMhMhYdASE1NDYETMj9qMjIyMjIyPyuArwVHR0VDIn8SokMFR0dswRMFR37UB0CvMjIAfTIyMjI/OAdKh1kZB0qHf7UHRUyMhUdAAAAAwBkAAAEsARMAAkAEwAdAAABIyIGFREhETQmASMiBhURIRE0JgEhETQ2OwEyFhUCvGQpOwEsOwFnZCk7ASw7/Rv+1DspZCk7BEw7KfwYA+gpO/7UOyn9RAK8KTv84AGQKTs7KQAAAAAF/5wAAASwBEwADwATAB8AJQApAAATITIWFREUBiMhIiY1ETQ2FxEhEQUjFTMRITUzNSMRIQURByMRMwcRMxHIArx8sLB8/UR8sLAYA4T+DMjI/tTIyAEsAZBkyMhkZARMsHz+DHywsHwB9HywyP1EArzIZP7UZGQBLGT+1GQB9GT+1AEsAAAABf+cAAAEsARMAA8AEwAfACUAKQAAEyEyFhURFAYjISImNRE0NhcRIREBIzUjFSMRMxUzNTMFEQcjETMHETMRyAK8fLCwfP1EfLCwGAOE/gxkZGRkZGQBkGTIyGRkBEywfP4MfLCwfAH0fLDI/UQCvP2oyMgB9MjIZP7UZAH0ZP7UASwABP+cAAAEsARMAA8AEwAbACMAABMhMhYVERQGIyEiJjURNDYXESERBSMRMxUhESEFIxEzFSERIcgCvHywsHz9RHywsBgDhP4MyMj+1AEsAZDIyP7UASwETLB8/gx8sLB8AfR8sMj9RAK8yP7UZAH0ZP7UZAH0AAAABP+cAAAEsARMAA8AEwAWABkAABMhMhYVERQGIyEiJjURNDYXESERAS0BDQERyAK8fLCwfP1EfLCwGAOE/gz+1AEsAZD+1ARMsHz+DHywsHwB9HywyP1EArz+DJaWlpYBLAAAAAX/nAAABLAETAAPABMAFwAgACkAABMhMhYVERQGIyEiJjURNDYXESERAyERIQcjIgYVFBY7AQERMzI2NTQmI8gCvHywsHz9RHywsBgDhGT9RAK8ZIImOTYpgv4Mgik2OSYETLB8/gx8sLB8AfR8sMj9RAK8/agB9GRWQUFUASz+1FRBQVYAAAAF/5wAAASwBEwADwATAB8AJQApAAATITIWFREUBiMhIiY1ETQ2FxEhEQUjFTMRITUzNSMRIQEjESM1MwMjNTPIArx8sLB8/UR8sLAYA4T+DMjI/tTIyAEsAZBkZMjIZGQETLB8/gx8sLB8AfR8sMj9RAK8yGT+1GRkASz+DAGQZP4MZAAG/5wAAASwBEwADwATABkAHwAjACcAABMhMhYVERQGIyEiJjURNDYXESERBTMRIREzASMRIzUzBRUzNQEjNTPIArx8sLB8/UR8sLAYA4T9RMj+1GQCWGRkyP2oZAEsZGQETLB8/gx8sLB8AfR8sMj9RAK8yP5wAfT+DAGQZMjIyP7UZAAF/5wAAASwBEwADwATABwAIgAmAAATITIWFREUBiMhIiY1ETQ2FxEhEQEHIzU3NSM1IQEjESM1MwMjNTPIArx8sLB8/UR8sLAYA4T+DMdkx8gBLAGQZGTIx2RkBEywfP4MfLCwfAH0fLDI/UQCvP5wyDLIlmT+DAGQZP4MZAAAAAMACQAJBKcEpwAPABsAJQAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JgchFSEVISc1NyEB4PDbnl5entvw255eXp4BxeTCcXHC5MJxcWz+1AEs/tRkZAEsBKdentvw255eXp7b8NueTHHC5MJxccLkwtDIZGTIZAAAAAAEAAkACQSnBKcADwAbACcAKwAAADIeAhQOAiIuAjQ+AQQiDgEUHgEyPgE0JgcVBxcVIycjFSMRIQcVMzUB4PDbnl5entvw255eXp4BxeTCcXHC5MJxcWwyZGRklmQBLMjIBKdentvw255eXp7b8NueTHHC5MJxccLkwtBkMmQyZGQBkGRkZAAAAv/y/50EwgRBACAANgAAATIWFzYzMhYUBisBNTQmIyEiBh0BIyImNTQ2NyY1ND4BEzMyFhURMzIWDwEGIi8BJjY7ARE0NgH3brUsLC54qqp4gB0V/tQVHd5QcFZBAmKqepYKD4kVCg3fDSYN3w0KFYkPBEF3YQ6t8a36FR0dFfpzT0VrDhMSZKpi/bMPCv7tFxD0EBD0EBcBEwoPAAAAAAL/8v+cBMMEQQAcADMAAAEyFhc2MzIWFxQGBwEmIgcBIyImNTQ2NyY1ND4BExcWBisBERQGKwEiJjURIyImNzY3NjIB9m62LCsueaoBeFr+hg0lDf6DCU9xVkECYqnm3w0KFYkPCpYKD4kVCg3HGBMZBEF3YQ+teGOkHAFoEBD+k3NPRWsOExNkqWP9kuQQF/7tCg8PCgETFxDMGBMAAAABAGQAAARMBG0AGAAAJTUhATMBMwkBMwEzASEVIyIGHQEhNTQmIwK8AZD+8qr+8qr+1P7Uqv7yqv7yAZAyFR0BkB0VZGQBLAEsAU3+s/7U/tRkHRUyMhUdAAAAAAEAeQAABDcEmwAvAAABMhYXHgEVFAYHFhUUBiMiJxUyFh0BITU0NjM1BiMiJjU0Ny4BNTQ2MzIXNCY1NDYCWF6TGll7OzIJaUo3LRUd/tQdFS03SmkELzlpSgUSAqMEm3FZBoNaPWcfHRpKaR77HRUyMhUd+x5pShIUFVg1SmkCAhAFdKMAAAAGACcAFASJBJwAEQAqAEIASgBiAHsAAAEWEgIHDgEiJicmAhI3PgEyFgUiBw4BBwYWHwEWMzI3Njc2Nz4BLwEmJyYXIgcOAQcGFh8BFjMyNz4BNz4BLwEmJyYWJiIGFBYyNjciBw4BBw4BHwEWFxYzMjc+ATc2Ji8BJhciBwYHBgcOAR8BFhcWMzI3PgE3NiYvASYD8m9PT29T2dzZU29PT29T2dzZ/j0EBHmxIgQNDCQDBBcGG0dGYAsNAwkDCwccBAVQdRgEDA0iBAQWBhJROQwMAwkDCwf5Y4xjY4xjVhYGElE6CwwDCQMLBwgEBVB1GAQNDCIEjRcGG0dGYAsNAwkDCwcIBAR5sSIEDQwkAwPyb/7V/tVvU1dXU28BKwErb1NXVxwBIrF5DBYDCQEWYEZHGwMVDCMNBgSRAhh1UA0WAwkBFTpREgMVCyMMBwT6Y2OMY2MVFTpREQQVCyMMBwQCGHVQDRYDCQEkFmBGRxsDFQwjDQYEASKxeQwWAwkBAAAABQBkAAAD6ASwAAwADwAWABwAIgAAASERIzUhFSERNDYzIQEjNQMzByczNTMDISImNREFFRQGKwECvAEstP6s/oQPCgI/ASzIZKLU1KJktP51Cg8DhA8KwwMg/oTIyALzCg/+1Mj84NTUyP4MDwoBi8jDCg8AAAAABQBkAAAD6ASwAAkADAATABoAIQAAASERCQERNDYzIQEjNRMjFSM1IzcDISImPQEpARUUBisBNQK8ASz+ov3aDwoCPwEsyD6iZKLUqv6dCg8BfAIIDwqbAyD9+AFe/doERwoP/tTI/HzIyNT+ZA8KNzcKD1AAAAAAAwAAAAAEsAP0AAgAGQAfAAABIxUzFyERIzcFMzIeAhUhFSEDETM0PgIBMwMhASEEiqJkZP7UotT9EsgbGiEOASz9qMhkDiEaAnPw8PzgASwB9AMgyGQBLNTUBBErJGT+ogHCJCsRBP5w/nAB9AAAAAMAAAAABEwETAAZADIAOQAAATMyFh0BMzIWHQEUBiMhIiY9ATQ2OwE1NDYFNTIWFREUBiMhIic3ARE0NjMVFBYzITI2AQc1IzUzNQKKZBUdMhUdHRX+1BUdHRUyHQFzKTs7Kf2oARP2/ro7KVg+ASw+WP201MjIBEwdFTIdFWQVHR0VZBUdMhUd+pY7KfzgKTsE9gFGAUQpO5Y+WFj95tSiZKIAAwBkAAAEvARMABkANgA9AAABMzIWHQEzMhYdARQGIyEiJj0BNDY7ATU0NgU1MhYVESMRMxQOAiMhIiY1ETQ2MxUUFjMhMjYBBzUjNTM1AcJkFR0yFR0dFf7UFR0dFTIdAXMpO8jIDiEaG/2oKTs7KVg+ASw+WAGc1MjIBEwdFTIdFWQVHR0VZBUdMhUd+pY7Kf4M/tQkKxEEOykDICk7lj5YWP3m1KJkogAAAAP/ogAABRYE1AALABsAHwAACQEWBiMhIiY3ATYyEyMiBhcTHgE7ATI2NxM2JgMVMzUCkgJ9FyAs+wQsIBcCfRZARNAUGAQ6BCMUNhQjBDoEGODIBK37sCY3NyYEUCf+TB0U/tIUHR0UAS4UHf4MZGQAAAAACQAAAAAETARMAA8AHwAvAD8ATwBfAG8AfwCPAAABMzIWHQEUBisBIiY9ATQ2EzMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYBMzIWHQEUBisBIiY9ATQ2ITMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYBMzIWHQEUBisBIiY9ATQ2ITMyFh0BFAYrASImPQE0NiEzMhYdARQGKwEiJj0BNDYBqfoKDw8K+goPDwr6Cg8PCvoKDw8BmvoKDw8K+goPD/zq+goPDwr6Cg8PAZr6Cg8PCvoKDw8BmvoKDw8K+goPD/zq+goPDwr6Cg8PAZr6Cg8PCvoKDw8BmvoKDw8K+goPDwRMDwqWCg8PCpYKD/7UDwqWCg8PCpYKDw8KlgoPDwqWCg/+1A8KlgoPDwqWCg8PCpYKDw8KlgoPDwqWCg8PCpYKD/7UDwqWCg8PCpYKDw8KlgoPDwqWCg8PCpYKDw8KlgoPAAAAAwAAAAAEsAUUABkAKQAzAAABMxUjFSEyFg8BBgchJi8BJjYzITUjNTM1MwEhMhYUBisBFyE3IyImNDYDITIWHQEhNTQ2ArxkZAFePjEcQiko/PwoKUIcMT4BXmRkyP4+ArwVHR0VDIn8SooNFR0dswRMFR37UB0EsMhkTzeEUzMzU4Q3T2TIZPx8HSodZGQdKh3+1B0VMjIVHQAABAAAAAAEsAUUAAUAGQArADUAAAAyFhUjNAchFhUUByEyFg8BIScmNjMhJjU0AyEyFhQGKwEVBSElNSMiJjQ2AyEyFh0BITU0NgIwUDnCPAE6EgMBSCkHIq/9WrIiCikBSAOvArwVHR0VlgET/EoBE5YVHR2zBEwVHftQHQUUOykpjSUmCBEhFpGRFiERCCb+lR0qHcjIyMgdKh39qB0VMjIVHQAEAAAAAASwBJ0ABwAUACQALgAAADIWFAYiJjQTMzIWFRQXITY1NDYzASEyFhQGKwEXITcjIiY0NgMhMhYdASE1NDYCDZZqapZqty4iKyf+vCcrI/7NArwVHR0VDYr8SokMFR0dswRMFR37UB0EnWqWamqW/us5Okxra0w6Of5yHSodZGQdKh3+1B0VMjIVHQAEAAAAAASwBRQADwAcACwANgAAATIeARUUBiImNTQ3FzcnNhMzMhYVFBchNjU0NjMBITIWFAYrARchNyMiJjQ2AyEyFh0BITU0NgJYL1szb5xvIpBvoyIfLiIrJ/68Jysj/s0CvBUdHRUNivxKiQwVHR2zBEwVHftQHQUUa4s2Tm9vTj5Rj2+jGv4KOTpMa2tMOjn+ch0qHWRkHSod/tQdFTIyFR0AAAADAAAAAASwBRIAEgAiACwAAAEFFSEUHgMXIS4BNTQ+AjcBITIWFAYrARchNyMiJjQ2AyEyFh0BITU0NgJYASz+1CU/P00T/e48PUJtj0r+ogK8FR0dFQ2K/EqJDBUdHbMETBUd+1AdBLChizlmUT9IGVO9VFShdksE/H4dKh1kZB0qHf7UHRUyMhUdAAIAyAAAA+gFFAAPACkAAAAyFh0BHgEdASE1NDY3NTQDITIWFyMVMxUjFTMVIxUzFAYjISImNRE0NgIvUjsuNv5wNi5kAZA2XBqsyMjIyMh1U/5wU3V1BRQ7KU4aXDYyMjZcGk4p/kc2LmRkZGRkU3V1UwGQU3UAAAMAZP//BEwETAAPAC8AMwAAEyEyFhURFAYjISImNRE0NgMhMhYdARQGIyEXFhQGIi8BIQcGIiY0PwEhIiY9ATQ2BQchJ5YDhBUdHRX8fBUdHQQDtgoPDwr+5eANGiUNWP30Vw0mGg3g/t8KDw8BqmQBRGQETB0V/gwVHR0VAfQVHf1EDwoyCg/gDSUbDVhYDRslDeAPCjIKD2RkZAAAAAAEAAAAAASwBEwAGQAjAC0ANwAAEyEyFh0BIzQmKwEiBhUjNCYrASIGFSM1NDYDITIWFREhETQ2ExUUBisBIiY9ASEVFAYrASImPQHIAyBTdWQ7KfopO2Q7KfopO2R1EQPoKTv7UDvxHRVkFR0D6B0VZBUdBEx1U8gpOzspKTs7KchTdf4MOyn+1AEsKTv+DDIVHR0VMjIVHR0VMgADAAEAAASpBKwADQARABsAAAkBFhQPASEBJjQ3ATYyCQMDITIWHQEhNTQ2AeACqh8fg/4f/fsgIAEnH1n+rAFWAS/+q6IDIBUd/HwdBI39VR9ZH4MCBh9ZHwEoH/5u/qoBMAFV/BsdFTIyFR0AAAAAAgCPAAAEIQSwABcALwAAAQMuASMhIgYHAwYWMyEVFBYyNj0BMzI2AyE1NDY7ATU0NjsBETMRMzIWHQEzMhYVBCG9CCcV/nAVJwi9CBMVAnEdKh19FROo/a0dFTIdFTDILxUdMhUdAocB+hMcHBP+BhMclhUdHRWWHP2MMhUdMhUdASz+1B0VMh0VAAAEAAAAAASwBLAADQAQAB8AIgAAASERFAYjIREBNTQ2MyEBIzUBIREUBiMhIiY1ETQ2MyEBIzUDhAEsDwr+if7UDwoBdwEsyP2oASwPCv12Cg8PCgF3ASzIAyD9wQoPAk8BLFQKD/7UyP4M/cEKDw8KA7YKD/7UyAAC/5wAZAUUBEcARgBWAAABMzIeAhcWFxY2NzYnJjc+ARYXFgcOASsBDgEPAQ4BKwEiJj8BBisBIicHDgErASImPwEmLwEuAT0BNDY7ATY3JyY2OwE2BSMiBh0BFBY7ATI2PQE0JgHkw0uOakkMEhEfQwoKGRMKBQ8XDCkCA1Y9Pgc4HCcDIhVkFRgDDDEqwxgpCwMiFWQVGAMaVCyfExwdFXwLLW8QBxXLdAFF+goPDwr6Cg8PBEdBa4pJDgYKISAiJRsQCAYIDCw9P1c3fCbqFB0dFEYOCEAUHR0UnUplNQcmFTIVHVdPXw4TZV8PCjIKDw8KMgoPAAb/nP/mBRQEfgAJACQANAA8AFIAYgAAASU2Fh8BFgYPASUzMhYfASEyFh0BFAYHBQYmJyYjISImPQE0NhcjIgYdARQ7ATI2NTQmJyYEIgYUFjI2NAE3PgEeARceAT8BFxYGDwEGJi8BJjYlBwYfAR4BPwE2Jy4BJy4BAoEBpxMuDiAOAxCL/CtqQ0geZgM3FR0cE/0fFyIJKjr+1D5YWLlQExIqhhALIAsSAYBALS1ALf4PmBIgHhMQHC0aPzANITNQL3wpgigJASlmHyElDR0RPRMFAhQHCxADhPcICxAmDyoNeMgiNtQdFTIVJgeEBBQPQ1g+yD5YrBwVODMQEAtEERzJLUAtLUD+24ITChESEyMgAwWzPUkrRSgJL5cvfRxYGyYrDwkLNRAhFEgJDAQAAAAAAwBkAAAEOQSwAFEAYABvAAABMzIWHQEeARcWDgIPATIeBRUUDgUjFRQGKwEiJj0BIxUUBisBIiY9ASMiJj0BNDY7AREjIiY9ATQ2OwE1NDY7ATIWHQEzNTQ2AxUhMj4CNTc0LgMjARUhMj4CNTc0LgMjAnGWCg9PaAEBIC4uEBEGEjQwOiodFyI2LUAjGg8KlgoPZA8KlgoPrwoPDwpLSwoPDwqvDwqWCg9kD9cBBxwpEwsBAQsTKRz++QFrHCkTCwEBCxMpHASwDwptIW1KLk0tHwYGAw8UKDJOLTtdPCoVCwJLCg8PCktLCg8PCksPCpYKDwJYDwqWCg9LCg8PCktLCg/+1MgVHR0LCgQOIhoW/nDIFR0dCwoEDiIaFgAAAwAEAAIEsASuABcAKQAsAAATITIWFREUBg8BDgEjISImJy4CNRE0NgQiDgQPARchNy4FAyMT1AMMVnokEhIdgVL9xFKCHAgYKHoCIIx9VkcrHQYGnAIwnAIIIClJVSGdwwSuelb+YDO3QkJXd3ZYHFrFMwGgVnqZFyYtLSUMDPPzBQ8sKDEj/sIBBQACAMgAAAOEBRQADwAZAAABMzIWFREUBiMhIiY1ETQ2ARUUBisBIiY9AQHblmesVCn+PilUrAFINhWWFTYFFKxn/gwpVFQpAfRnrPwY4RU2NhXhAAACAMgAAAOEBRQADwAZAAABMxQWMxEUBiMhIiY1ETQ2ARUUBisBIiY9AQHbYLOWVCn+PilUrAFINhWWFTYFFJaz/kIpVFQpAfRnrPwY4RU2NhXhAAACAAAAFAUOBBoAFAAaAAAJASUHFRcVJwc1NzU0Jj4CPwEnCQEFJTUFJQUO/YL+hk5klpZkAQEBBQQvkwKCAVz+ov6iAV4BXgL//uWqPOCWx5SVyJb6BA0GCgYDKEEBG/1ipqaTpaUAAAMAZAH0BLADIAAHAA8AFwAAEjIWFAYiJjQkMhYUBiImNCQyFhQGIiY0vHxYWHxYAeh8WFh8WAHofFhYfFgDIFh8WFh8WFh8WFh8WFh8WFh8AAAAAAMBkAAAArwETAAHAA8AFwAAADIWFAYiJjQSMhYUBiImNBIyFhQGIiY0Aeh8WFh8WFh8WFh8WFh8WFh8WARMWHxYWHz+yFh8WFh8/shYfFhYfAAAAAMAZABkBEwETAAPAB8ALwAAEyEyFh0BFAYjISImPQE0NhMhMhYdARQGIyEiJj0BNDYTITIWHQEUBiMhIiY9ATQ2fQO2Cg8PCvxKCg8PCgO2Cg8PCvxKCg8PCgO2Cg8PCvxKCg8PBEwPCpYKDw8KlgoP/nAPCpYKDw8KlgoP/nAPCpYKDw8KlgoPAAAABAAAAAAEsASwAA8AHwAvADMAAAEhMhYVERQGIyEiJjURNDYFISIGFREUFjMhMjY1ETQmBSEyFhURFAYjISImNRE0NhcVITUBXgH0ory7o/4Mpbm5Asv9qCk7OykCWCk7O/2xAfQVHR0V/gwVHR1HAZAEsLuj/gylubmlAfSlucg7Kf2oKTs7KQJYKTtkHRX+1BUdHRUBLBUdZMjIAAAAAAEAZABkBLAETAA7AAATITIWFAYrARUzMhYUBisBFTMyFhQGKwEVMzIWFAYjISImNDY7ATUjIiY0NjsBNSMiJjQ2OwE1IyImNDaWA+gVHR0VMjIVHR0VMjIVHR0VMjIVHR0V/BgVHR0VMjIVHR0VMjIVHR0VMjIVHR0ETB0qHcgdKh3IHSodyB0qHR0qHcgdKh3IHSodyB0qHQAAAAYBLAAFA+gEowAHAA0AEwAZAB8AKgAAAR4BBgcuATYBMhYVIiYlFAYjNDYBMhYVIiYlFAYjNDYDFRQGIiY9ARYzMgKKVz8/V1c/P/75fLB8sAK8sHyw/cB8sHywArywfLCwHSodKAMRBKNDsrJCQrKy/sCwfLB8fLB8sP7UsHywfHywfLD+05AVHR0VjgQAAAH/tQDIBJQDgQBCAAABNzYXAR4BBw4BKwEyFRQOBCsBIhE0NyYiBxYVECsBIi4DNTQzIyImJyY2NwE2HwEeAQ4BLwEHIScHBi4BNgLpRRkUASoLCAYFGg8IAQQNGyc/KZK4ChRUFQu4jjBJJxkHAgcPGQYGCAsBKhQaTBQVCiMUM7YDe7YsFCMKFgNuEwYS/tkLHw8OEw0dNkY4MhwBIBgXBAQYF/7gKjxTQyMNEw4PHwoBKBIHEwUjKBYGDMHBDAUWKCMAAAAAAgAAAAAEsASwACUAQwAAASM0LgUrAREUFh8BFSE1Mj4DNREjIg4FFSMRIQEjNC4DKwERFBYXMxUjNTI1ESMiDgMVIzUhBLAyCAsZEyYYGcgyGRn+cAQOIhoWyBkYJhMZCwgyA+j9RBkIChgQEWQZDQzIMmQREBgKCBkB9AOEFSAVDggDAfyuFhkBAmRkAQUJFQ4DUgEDCA4VIBUBLP0SDxMKBQH+VwsNATIyGQGpAQUKEw+WAAAAAAMAAAAABEwErgAdACAAMAAAATUiJy4BLwEBIwEGBw4BDwEVITUiJj8BIRcWBiMVARsBARUUBiMhIiY9ATQ2MyEyFgPoGR4OFgUE/t9F/tQSFQkfCwsBETE7EkUBJT0NISf+7IZ5AbEdFfwYFR0dFQPoFR0BLDIgDiIKCwLr/Q4jFQkTBQUyMisusKYiQTIBhwFW/qr942QVHR0VZBUdHQADAAAAAASwBLAADwBHAEoAABMhMhYVERQGIyEiJjURNDYFIyIHAQYHBgcGHQEUFjMhMjY9ATQmIyInJj8BIRcWBwYjIgYdARQWMyEyNj0BNCYnIicmJyMBJhMjEzIETBUdHRX7tBUdHQJGRg0F/tUREhImDAsJAREIDAwINxAKCj8BCjkLEQwYCAwMCAE5CAwLCBEZGQ8B/uAFDsVnBLAdFfu0FR0dFQRMFR1SDP0PIBMSEAUNMggMDAgyCAwXDhmjmR8YEQwIMggMDAgyBwwBGRskAuwM/gUBCAAABAAAAAAEsASwAAMAEwAjACcAAAEhNSEFITIWFREUBiMhIiY1ETQ2KQEyFhURFAYjISImNRE0NhcRIREEsPtQBLD7ggGQFR0dFf5wFR0dAm0BkBUdHRX+cBUdHUcBLARMZMgdFfx8FR0dFQOEFR0dFf5wFR0dFQGQFR1k/tQBLAAEAAAAAASwBLAADwAfACMAJwAAEyEyFhURFAYjISImNRE0NgEhMhYVERQGIyEiJjURNDYXESEREyE1ITIBkBUdHRX+cBUdHQJtAZAVHR0V/nAVHR1HASzI+1AEsASwHRX8fBUdHRUDhBUd/gwdFf5wFR0dFQGQFR1k/tQBLP2oZAAAAAACAAAAZASwA+gAJwArAAATITIWFREzNTQ2MyEyFh0BMxUjFRQGIyEiJj0BIxEUBiMhIiY1ETQ2AREhETIBkBUdZB0VAZAVHWRkHRX+cBUdZB0V/nAVHR0CnwEsA+gdFf6ilhUdHRWWZJYVHR0Vlv6iFR0dFQMgFR3+1P7UASwAAAQAAAAABLAEsAADABMAFwAnAAAzIxEzFyEyFhURFAYjISImNRE0NhcRIREBITIWFREUBiMhIiY1ETQ2ZGRklgGQFR0dFf5wFR0dRwEs/qIDhBUdHRX8fBUdHQSwZB0V/nAVHR0VAZAVHWT+1AEs/gwdFf5wFR0dFQGQFR0AAAAAAgBkAAAETASwACcAKwAAATMyFhURFAYrARUhMhYVERQGIyEiJjURNDYzITUjIiY1ETQ2OwE1MwcRIRECWJYVHR0VlgHCFR0dFfx8FR0dFQFelhUdHRWWZMgBLARMHRX+cBUdZB0V/nAVHR0VAZAVHWQdFQGQFR1kyP7UASwAAAAEAAAAAASwBLAAAwATABcAJwAAISMRMwUhMhYVERQGIyEiJjURNDYXESERASEyFhURFAYjISImNRE0NgSwZGT9dgGQFR0dFf5wFR0dRwEs/K4DhBUdHRX8fBUdHQSwZB0V/nAVHR0VAZAVHWT+1AEs/gwdFf5wFR0dFQGQFR0AAAEBLAAwA28EgAAPAAAJAQYjIiY1ETQ2MzIXARYUA2H+EhcSDhAQDhIXAe4OAjX+EhcbGQPoGRsX/hIOKgAAAAABAUEAMgOEBH4ACwAACQE2FhURFAYnASY0AU8B7h0qKh3+Eg4CewHuHREp/BgpER0B7g4qAAAAAAEAMgFBBH4DhAALAAATITIWBwEGIicBJjZkA+gpER3+Eg4qDv4SHREDhCod/hIODgHuHSoAAAAAAQAyASwEfgNvAAsAAAkBFgYjISImNwE2MgJ7Ae4dESn8GCkRHQHuDioDYf4SHSoqHQHuDgAAAAACAAgAAASwBCgABgAKAAABFQE1LQE1ASE1IQK8/UwBnf5jBKj84AMgAuW2/r3dwcHd+9jIAAAAAAIAAABkBLAEsAALADEAAAEjFTMVIREzNSM1IQEzND4FOwERFAYPARUhNSIuAzURMzIeBRUzESEEsMjI/tTIyAEs+1AyCAsZEyYYGWQyGRkBkAQOIhoWZBkYJhMZCwgy/OADhGRkASxkZP4MFSAVDggDAf3aFhkBAmRkAQUJFQ4CJgEDCA4VIBUBLAAAAgAAAAAETAPoACUAMQAAASM0LgUrAREUFh8BFSE1Mj4DNREjIg4FFSMRIQEjFTMVIREzNSM1IQMgMggLGRMmGBlkMhkZ/nAEDiIaFmQZGCYTGQsIMgMgASzIyP7UyMgBLAK8FSAVDggDAf3aFhkCAWRkAQUJFQ4CJgEDCA4VIBUBLPzgZGQBLGRkAAABAMgAZgNyBEoAEgAAATMyFgcJARYGKwEiJwEmNDcBNgK9oBAKDP4wAdAMChCgDQr+KQcHAdcKBEoWDP4w/jAMFgkB1wgUCAHXCQAAAQE+AGYD6ARKABIAAAEzMhcBFhQHAQYrASImNwkBJjYBU6ANCgHXBwf+KQoNoBAKDAHQ/jAMCgRKCf4pCBQI/ikJFgwB0AHQDBYAAAEAZgDIBEoDcgASAAAAFh0BFAcBBiInASY9ATQ2FwkBBDQWCf4pCBQI/ikJFgwB0AHQA3cKEKANCv4pBwcB1woNoBAKDP4wAdAAAAABAGYBPgRKA+gAEgAACQEWHQEUBicJAQYmPQE0NwE2MgJqAdcJFgz+MP4wDBYJAdcIFAPh/ikKDaAQCgwB0P4wDAoQoA0KAdcHAAAAAgDZ//kEPQSwAAUAOgAAARQGIzQ2BTMyFh8BNjc+Ah4EBgcOBgcGIiYjIgYiJy4DLwEuAT4EHgEXJyY2A+iwfLD+VmQVJgdPBQsiKFAzRyorDwURAQQSFyozTSwNOkkLDkc3EDlfNyYHBw8GDyUqPjdGMR+TDA0EsHywfLDIHBPCAQIGBwcFDx81S21DBxlLR1xKQhEFBQcHGWt0bCQjP2hJNyATBwMGBcASGAAAAAACAMgAFQOEBLAAFgAaAAATITIWFREUBisBEQcGJjURIyImNRE0NhcVITX6AlgVHR0Vlv8TGpYVHR2rASwEsB0V/nAVHf4MsgkQFQKKHRUBkBUdZGRkAAAAAgDIABkETASwAA4AEgAAEyEyFhURBRElIREjETQ2ARU3NfoC7ic9/UQCWP1EZB8BDWQEsFEs/Ft1A7Z9/BgEARc0/V1kFGQAAQAAAAECTW/DBF9fDzz1AB8EsAAAAADQdnOXAAAAANB2c5f/Uf+cBdwFFAAAAAgAAgAAAAAAAAABAAAFFP+FAAAFFP9R/tQF3AABAAAAAAAAAAAAAAAAAAAAowG4ACgAAAAAAZAAAASwAAAEsABkBLAAAASwAAAEsABwAooAAAUUAAACigAABRQAAAGxAAABRQAAANgAAADYAAAAogAAAQQAAABIAAABBAAAAUUAAASwAGQEsAB7BLAAyASwAMgB9AAABLD/8gSwAAAEsAAABLD/8ASwAAAEsAAOBLAACQSwAGQEsP/TBLD/0wSwAAAEsAAABLAAAASwAAAEsAAABLAAJgSwAG4EsAAXBLAAFwSwABcEsABkBLAAGgSwAGQEsAAMBLAAZASwABcEsP+cBLAAZASwABcEsAAXBLAAAASwABcEsAAXBLAAFwSwAGQEsAAABLAAZASwAAAEsAAABLAAAASwAAAEsAAABLAAAASwAAAEsAAABLAAZASwAMgEsAAABLAAAASwADUEsABkBLAAyASw/7UEsAAhBLAAAASwAAAEsAAABLAAAASwAAAEsP+cBLAAAASwAAAEsAAABLAA2wSwABcEsAB1BLAAAASwAAAEsAAABLAACgSwAMgEsAAABLAAnQSwAMgEsADIBLAAyASwAAAEsP/+BLABLASwAGQEsACIBLABOwSwABcEsAAXBLAAFwSwABcEsAAXBLAAFwSwAAAEsAAXBLAAFwSwABcEsAAXBLAAAASwALcEsAC3BLAAAASwAAAEsABJBLAAFwSwAAAEsAAABLAAXQSw/9wEsP/cBLD/nwSwAGQEsAAABLAAAASwAAAEsABkBLD//wSwAAAEsP9RBLAABgSwAAAEsAAABLABRQSwAAEEsAAABLD/nASwAEoEsAAUBLAAAASwAAAEsAAABLD/nASwAGEEsP/9BLAAFgSwABYEsAAWBLAAFgSwABgEsAAABMQAAASwAGQAAAAAAAD/2ABkADkAyAAAAScAZAAZABkAGQAZABkAGQAZAAAAAAAAAAAAAADZAAAAAAAOAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAMAZABkAAAAEAAAAAAAZP+c/5z/nP+c/5z/nP+c/5wACQAJ//L/8gBkAHkAJwBkAGQAAAAAAGT/ogAAAAAAAAAAAAAAAADIAGQAAAABAI8AAP+c/5wAZAAEAMgAyAAAAGQBkABkAAAAZAEs/7UAAAAAAAAAAAAAAAAAAABkAAABLAFBADIAMgAIAAAAAADIAT4AZgBmANkAyADIAAAAKgAqACoAKgCyAOgA6AFOAU4BTgFOAU4BTgFOAU4BTgFOAU4BTgFOAU4BpAIGAiICfgKGAqwC5ANGA24DjAPEBAgEMgRiBKIE3AVcBboGcgb0ByAHYgfKCB4IYgi+CTYJhAm2Cd4KKApMCpQK4gswC4oLygwIDFgNKg1eDbAODg5oDrQPKA+mD+YQEhBUEJAQqhEqEXYRthIKEjgSfBLAExoTdBPQFCoU1BU8FagVzBYEFjYWYBawFv4XUhemGAIYLhhqGJYYsBjgGP4ZKBloGZQZxBnaGe4aNhpoGrga9hteG7QcMhyUHOIdHB1EHWwdlB28HeYeLh52HsAfYh/SIEYgviEyIXYhuCJAIpYiuCMOIyIjOCN6I8Ij4CQCJDAkXiSWJOIlNCVgJbwmFCZ+JuYnUCe8J/goNChwKKwpoCnMKiYqSiqEKworeiwILGgsuizsLRwtiC30LiguZi6iLtgvDi9GL34vsi/4MD4whDDSMRIxYDGuMegyJDJeMpoy3jMiMz4zaDO2NBg0YDSoNNI1LDWeNeg2PjZ8Ntw3GjdON5I31DgQOEI4hjjIOQo5SjmIOcw6HDpsOpo63jugO9w8GDxQPKI8+D0yPew+Oj6MPtQ/KD9uP6o/+kBIQIBAxkECQX5CGEKoQu5DGENCQ3ZDoEPKRBBEYESuRPZFWkW2RgZGdEa0RvZHNkd2R7ZH9kgWSDJITkhqSIZIzEkSSThJXkmESapKAkouSlIAAQAAARcApwARAAAAAAACAAAAAQABAAAAQAAuAAAAAAAAABAAxgABAAAAAAATABIAAAADAAEECQAAAGoAEgADAAEECQABACgAfAADAAEECQACAA4ApAADAAEECQADAEwAsgADAAEECQAEADgA/gADAAEECQAFAHgBNgADAAEECQAGADYBrgADAAEECQAIABYB5AADAAEECQAJABYB+gADAAEECQALACQCEAADAAEECQAMACQCNAADAAEECQATACQCWAADAAEECQDIABYCfAADAAEECQDJADACkgADAAEECdkDABoCwnd3dy5nbHlwaGljb25zLmNvbQBDAG8AcAB5AHIAaQBnAGgAdAAgAKkAIAAyADAAMQA0ACAAYgB5ACAASgBhAG4AIABLAG8AdgBhAHIAaQBrAC4AIABBAGwAbAAgAHIAaQBnAGgAdABzACAAcgBlAHMAZQByAHYAZQBkAC4ARwBMAFkAUABIAEkAQwBPAE4AUwAgAEgAYQBsAGYAbABpAG4AZwBzAFIAZQBnAHUAbABhAHIAMQAuADAAMAA5ADsAVQBLAFcATgA7AEcATABZAFAASABJAEMATwBOAFMASABhAGwAZgBsAGkAbgBnAHMALQBSAGUAZwB1AGwAYQByAEcATABZAFAASABJAEMATwBOAFMAIABIAGEAbABmAGwAaQBuAGcAcwAgAFIAZQBnAHUAbABhAHIAVgBlAHIAcwBpAG8AbgAgADEALgAwADAAOQA7AFAAUwAgADAAMAAxAC4AMAAwADkAOwBoAG8AdABjAG8AbgB2ACAAMQAuADAALgA3ADAAOwBtAGEAawBlAG8AdABmAC4AbABpAGIAMgAuADUALgA1ADgAMwAyADkARwBMAFkAUABIAEkAQwBPAE4AUwBIAGEAbABmAGwAaQBuAGcAcwAtAFIAZQBnAHUAbABhAHIASgBhAG4AIABLAG8AdgBhAHIAaQBrAEoAYQBuACAASwBvAHYAYQByAGkAawB3AHcAdwAuAGcAbAB5AHAAaABpAGMAbwBuAHMALgBjAG8AbQB3AHcAdwAuAGcAbAB5AHAAaABpAGMAbwBuAHMALgBjAG8AbQB3AHcAdwAuAGcAbAB5AHAAaABpAGMAbwBuAHMALgBjAG8AbQBXAGUAYgBmAG8AbgB0ACAAMQAuADAAVwBlAGQAIABPAGMAdAAgADIAOQAgADAANgA6ADMANgA6ADAANwAgADIAMAAxADQARgBvAG4AdAAgAFMAcQB1AGkAcgByAGUAbAAAAAIAAAAAAAD/tQAyAAAAAAAAAAAAAAAAAAAAAAAAAAABFwAAAQIBAwADAA0ADgEEAJYBBQEGAQcBCAEJAQoBCwEMAQ0BDgEPARABEQESARMA7wEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFxAXIBcwF0AXUBdgF3AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPAZABkQGSAZMBlAGVAZYBlwGYAZkBmgGbAZwBnQGeAZ8BoAGhAaIBowGkAaUBpgGnAagBqQGqAasBrAGtAa4BrwGwAbEBsgGzAbQBtQG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAc8B0AHRAdIB0wHUAdUB1gHXAdgB2QHaAdsB3AHdAd4B3wHgAeEB4gHjAeQB5QHmAecB6AHpAeoB6wHsAe0B7gHvAfAB8QHyAfMB9AH1AfYB9wH4AfkB+gH7AfwB/QH+Af8CAAIBAgICAwIEAgUCBgIHAggCCQIKAgsCDAINAg4CDwIQAhECEgZnbHlwaDEGZ2x5cGgyB3VuaTAwQTAHdW5pMjAwMAd1bmkyMDAxB3VuaTIwMDIHdW5pMjAwMwd1bmkyMDA0B3VuaTIwMDUHdW5pMjAwNgd1bmkyMDA3B3VuaTIwMDgHdW5pMjAwOQd1bmkyMDBBB3VuaTIwMkYHdW5pMjA1RgRFdXJvB3VuaTIwQkQHdW5pMjMxQgd1bmkyNUZDB3VuaTI2MDEHdW5pMjZGQQd1bmkyNzA5B3VuaTI3MEYHdW5pRTAwMQd1bmlFMDAyB3VuaUUwMDMHdW5pRTAwNQd1bmlFMDA2B3VuaUUwMDcHdW5pRTAwOAd1bmlFMDA5B3VuaUUwMTAHdW5pRTAxMQd1bmlFMDEyB3VuaUUwMTMHdW5pRTAxNAd1bmlFMDE1B3VuaUUwMTYHdW5pRTAxNwd1bmlFMDE4B3VuaUUwMTkHdW5pRTAyMAd1bmlFMDIxB3VuaUUwMjIHdW5pRTAyMwd1bmlFMDI0B3VuaUUwMjUHdW5pRTAyNgd1bmlFMDI3B3VuaUUwMjgHdW5pRTAyOQd1bmlFMDMwB3VuaUUwMzEHdW5pRTAzMgd1bmlFMDMzB3VuaUUwMzQHdW5pRTAzNQd1bmlFMDM2B3VuaUUwMzcHdW5pRTAzOAd1bmlFMDM5B3VuaUUwNDAHdW5pRTA0MQd1bmlFMDQyB3VuaUUwNDMHdW5pRTA0NAd1bmlFMDQ1B3VuaUUwNDYHdW5pRTA0Nwd1bmlFMDQ4B3VuaUUwNDkHdW5pRTA1MAd1bmlFMDUxB3VuaUUwNTIHdW5pRTA1Mwd1bmlFMDU0B3VuaUUwNTUHdW5pRTA1Ngd1bmlFMDU3B3VuaUUwNTgHdW5pRTA1OQd1bmlFMDYwB3VuaUUwNjIHdW5pRTA2Mwd1bmlFMDY0B3VuaUUwNjUHdW5pRTA2Ngd1bmlFMDY3B3VuaUUwNjgHdW5pRTA2OQd1bmlFMDcwB3VuaUUwNzEHdW5pRTA3Mgd1bmlFMDczB3VuaUUwNzQHdW5pRTA3NQd1bmlFMDc2B3VuaUUwNzcHdW5pRTA3OAd1bmlFMDc5B3VuaUUwODAHdW5pRTA4MQd1bmlFMDgyB3VuaUUwODMHdW5pRTA4NAd1bmlFMDg1B3VuaUUwODYHdW5pRTA4Nwd1bmlFMDg4B3VuaUUwODkHdW5pRTA5MAd1bmlFMDkxB3VuaUUwOTIHdW5pRTA5Mwd1bmlFMDk0B3VuaUUwOTUHdW5pRTA5Ngd1bmlFMDk3B3VuaUUxMDEHdW5pRTEwMgd1bmlFMTAzB3VuaUUxMDQHdW5pRTEwNQd1bmlFMTA2B3VuaUUxMDcHdW5pRTEwOAd1bmlFMTA5B3VuaUUxMTAHdW5pRTExMQd1bmlFMTEyB3VuaUUxMTMHdW5pRTExNAd1bmlFMTE1B3VuaUUxMTYHdW5pRTExNwd1bmlFMTE4B3VuaUUxMTkHdW5pRTEyMAd1bmlFMTIxB3VuaUUxMjIHdW5pRTEyMwd1bmlFMTI0B3VuaUUxMjUHdW5pRTEyNgd1bmlFMTI3B3VuaUUxMjgHdW5pRTEyOQd1bmlFMTMwB3VuaUUxMzEHdW5pRTEzMgd1bmlFMTMzB3VuaUUxMzQHdW5pRTEzNQd1bmlFMTM2B3VuaUUxMzcHdW5pRTEzOAd1bmlFMTM5B3VuaUUxNDAHdW5pRTE0MQd1bmlFMTQyB3VuaUUxNDMHdW5pRTE0NAd1bmlFMTQ1B3VuaUUxNDYHdW5pRTE0OAd1bmlFMTQ5B3VuaUUxNTAHdW5pRTE1MQd1bmlFMTUyB3VuaUUxNTMHdW5pRTE1NAd1bmlFMTU1B3VuaUUxNTYHdW5pRTE1Nwd1bmlFMTU4B3VuaUUxNTkHdW5pRTE2MAd1bmlFMTYxB3VuaUUxNjIHdW5pRTE2Mwd1bmlFMTY0B3VuaUUxNjUHdW5pRTE2Ngd1bmlFMTY3B3VuaUUxNjgHdW5pRTE2OQd1bmlFMTcwB3VuaUUxNzEHdW5pRTE3Mgd1bmlFMTczB3VuaUUxNzQHdW5pRTE3NQd1bmlFMTc2B3VuaUUxNzcHdW5pRTE3OAd1bmlFMTc5B3VuaUUxODAHdW5pRTE4MQd1bmlFMTgyB3VuaUUxODMHdW5pRTE4NAd1bmlFMTg1B3VuaUUxODYHdW5pRTE4Nwd1bmlFMTg4B3VuaUUxODkHdW5pRTE5MAd1bmlFMTkxB3VuaUUxOTIHdW5pRTE5Mwd1bmlFMTk0B3VuaUUxOTUHdW5pRTE5Nwd1bmlFMTk4B3VuaUUxOTkHdW5pRTIwMAd1bmlFMjAxB3VuaUUyMDIHdW5pRTIwMwd1bmlFMjA0B3VuaUUyMDUHdW5pRTIwNgd1bmlFMjA5B3VuaUUyMTAHdW5pRTIxMQd1bmlFMjEyB3VuaUUyMTMHdW5pRTIxNAd1bmlFMjE1B3VuaUUyMTYHdW5pRTIxOAd1bmlFMjE5B3VuaUUyMjEHdW5pRTIyMwd1bmlFMjI0B3VuaUUyMjUHdW5pRTIyNgd1bmlFMjI3B3VuaUUyMzAHdW5pRTIzMQd1bmlFMjMyB3VuaUUyMzMHdW5pRTIzNAd1bmlFMjM1B3VuaUUyMzYHdW5pRTIzNwd1bmlFMjM4B3VuaUUyMzkHdW5pRTI0MAd1bmlFMjQxB3VuaUUyNDIHdW5pRTI0Mwd1bmlFMjQ0B3VuaUUyNDUHdW5pRTI0Ngd1bmlFMjQ3B3VuaUUyNDgHdW5pRTI0OQd1bmlFMjUwB3VuaUUyNTEHdW5pRTI1Mgd1bmlFMjUzB3VuaUUyNTQHdW5pRTI1NQd1bmlFMjU2B3VuaUUyNTcHdW5pRTI1OAd1bmlFMjU5B3VuaUUyNjAHdW5pRjhGRgZ1MUY1MTEGdTFGNkFBAAAAAAFUUMMXAAA=\";\n},function(e,t){e.exports=\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiID4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8bWV0YWRhdGE+PC9tZXRhZGF0YT4KPGRlZnM+Cjxmb250IGlkPSJnbHlwaGljb25zX2hhbGZsaW5nc3JlZ3VsYXIiIGhvcml6LWFkdi14PSIxMjAwIiA+Cjxmb250LWZhY2UgdW5pdHMtcGVyLWVtPSIxMjAwIiBhc2NlbnQ9Ijk2MCIgZGVzY2VudD0iLTI0MCIgLz4KPG1pc3NpbmctZ2x5cGggaG9yaXotYWR2LXg9IjUwMCIgLz4KPGdseXBoIGhvcml6LWFkdi14PSIwIiAvPgo8Z2x5cGggaG9yaXotYWR2LXg9IjQwMCIgLz4KPGdseXBoIHVuaWNvZGU9IiAiIC8+CjxnbHlwaCB1bmljb2RlPSIqIiBkPSJNNjAwIDExMDBxMTUgMCAzNCAtMS41dDMwIC0zLjVsMTEgLTFxMTAgLTIgMTcuNSAtMTAuNXQ3LjUgLTE4LjV2LTIyNGwxNTggMTU4cTcgNyAxOCA4dDE5IC02bDEwNiAtMTA2cTcgLTggNiAtMTl0LTggLTE4bC0xNTggLTE1OGgyMjRxMTAgMCAxOC41IC03LjV0MTAuNSAtMTcuNXE2IC00MSA2IC03NXEwIC0xNSAtMS41IC0zNHQtMy41IC0zMGwtMSAtMTFxLTIgLTEwIC0xMC41IC0xNy41dC0xOC41IC03LjVoLTIyNGwxNTggLTE1OCBxNyAtNyA4IC0xOHQtNiAtMTlsLTEwNiAtMTA2cS04IC03IC0xOSAtNnQtMTggOGwtMTU4IDE1OHYtMjI0cTAgLTEwIC03LjUgLTE4LjV0LTE3LjUgLTEwLjVxLTQxIC02IC03NSAtNnEtMTUgMCAtMzQgMS41dC0zMCAzLjVsLTExIDFxLTEwIDIgLTE3LjUgMTAuNXQtNy41IDE4LjV2MjI0bC0xNTggLTE1OHEtNyAtNyAtMTggLTh0LTE5IDZsLTEwNiAxMDZxLTcgOCAtNiAxOXQ4IDE4bDE1OCAxNThoLTIyNHEtMTAgMCAtMTguNSA3LjUgdC0xMC41IDE3LjVxLTYgNDEgLTYgNzVxMCAxNSAxLjUgMzR0My41IDMwbDEgMTFxMiAxMCAxMC41IDE3LjV0MTguNSA3LjVoMjI0bC0xNTggMTU4cS03IDcgLTggMTh0NiAxOWwxMDYgMTA2cTggNyAxOSA2dDE4IC04bDE1OCAtMTU4djIyNHEwIDEwIDcuNSAxOC41dDE3LjUgMTAuNXE0MSA2IDc1IDZ6IiAvPgo8Z2x5cGggdW5pY29kZT0iKyIgZD0iTTQ1MCAxMTAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMzUwaDM1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0zNTB2LTM1MHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMjAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYzNTBoLTM1MHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNSBoMzUwdjM1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4YTA7IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4YTU7IiBkPSJNODI1IDExMDBoMjUwcTEwIDAgMTIuNSAtNXQtNS41IC0xM2wtMzY0IC0zNjRxLTYgLTYgLTExIC0xOGgyNjhxMTAgMCAxMyAtNnQtMyAtMTRsLTEyMCAtMTYwcS02IC04IC0xOCAtMTR0LTIyIC02aC0xMjV2LTEwMGgyNzVxMTAgMCAxMyAtNnQtMyAtMTRsLTEyMCAtMTYwcS02IC04IC0xOCAtMTR0LTIyIC02aC0xMjV2LTE3NHEwIC0xMSAtNy41IC0xOC41dC0xOC41IC03LjVoLTE0OHEtMTEgMCAtMTguNSA3LjV0LTcuNSAxOC41djE3NCBoLTI3NXEtMTAgMCAtMTMgNnQzIDE0bDEyMCAxNjBxNiA4IDE4IDE0dDIyIDZoMTI1djEwMGgtMjc1cS0xMCAwIC0xMyA2dDMgMTRsMTIwIDE2MHE2IDggMTggMTR0MjIgNmgxMThxLTUgMTIgLTExIDE4bC0zNjQgMzY0cS04IDggLTUuNSAxM3QxMi41IDVoMjUwcTI1IDAgNDMgLTE4bDE2NCAtMTY0cTggLTggMTggLTh0MTggOGwxNjQgMTY0cTE4IDE4IDQzIDE4eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDIwMDA7IiBob3Jpei1hZHYteD0iNjUwIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjAwMTsiIGhvcml6LWFkdi14PSIxMzAwIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjAwMjsiIGhvcml6LWFkdi14PSI2NTAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMDAzOyIgaG9yaXotYWR2LXg9IjEzMDAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMDA0OyIgaG9yaXotYWR2LXg9IjQzMyIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDIwMDU7IiBob3Jpei1hZHYteD0iMzI1IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjAwNjsiIGhvcml6LWFkdi14PSIyMTYiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMDA3OyIgaG9yaXotYWR2LXg9IjIxNiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDIwMDg7IiBob3Jpei1hZHYteD0iMTYyIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjAwOTsiIGhvcml6LWFkdi14PSIyNjAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMDBhOyIgaG9yaXotYWR2LXg9IjcyIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjAyZjsiIGhvcml6LWFkdi14PSIyNjAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMDVmOyIgaG9yaXotYWR2LXg9IjMyNSIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDIwYWM7IiBkPSJNNzQ0IDExOThxMjQyIDAgMzU0IC0xODlxNjAgLTEwNCA2NiAtMjA5aC0xODFxMCA0NSAtMTcuNSA4Mi41dC00My41IDYxLjV0LTU4IDQwLjV0LTYwLjUgMjR0LTUxLjUgNy41cS0xOSAwIC00MC41IC01LjV0LTQ5LjUgLTIwLjV0LTUzIC0zOHQtNDkgLTYyLjV0LTM5IC04OS41aDM3OWwtMTAwIC0xMDBoLTMwMHEtNiAtNTAgLTYgLTEwMGg0MDZsLTEwMCAtMTAwaC0zMDBxOSAtNzQgMzMgLTEzMnQ1Mi41IC05MXQ2MS41IC01NC41dDU5IC0yOSB0NDcgLTcuNXEyMiAwIDUwLjUgNy41dDYwLjUgMjQuNXQ1OCA0MXQ0My41IDYxdDE3LjUgODBoMTc0cS0zMCAtMTcxIC0xMjggLTI3OHEtMTA3IC0xMTcgLTI3NCAtMTE3cS0yMDYgMCAtMzI0IDE1OHEtMzYgNDggLTY5IDEzM3QtNDUgMjA0aC0yMTdsMTAwIDEwMGgxMTJxMSA0NyA2IDEwMGgtMjE4bDEwMCAxMDBoMTM0cTIwIDg3IDUxIDE1My41dDYyIDEwMy41cTExNyAxNDEgMjk3IDE0MXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyMGJkOyIgZD0iTTQyOCAxMjAwaDM1MHE2NyAwIDEyMCAtMTN0ODYgLTMxdDU3IC00OS41dDM1IC01Ni41dDE3IC02NC41dDYuNSAtNjAuNXQwLjUgLTU3di0xNi41di0xNi41cTAgLTM2IC0wLjUgLTU3dC02LjUgLTYxdC0xNyAtNjV0LTM1IC01N3QtNTcgLTUwLjV0LTg2IC0zMS41dC0xMjAgLTEzaC0xNzhsLTIgLTEwMGgyODhxMTAgMCAxMyAtNnQtMyAtMTRsLTEyMCAtMTYwcS02IC04IC0xOCAtMTR0LTIyIC02aC0xMzh2LTE3NXEwIC0xMSAtNS41IC0xOCB0LTE1LjUgLTdoLTE0OXEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE3NWgtMjY3cS0xMCAwIC0xMyA2dDMgMTRsMTIwIDE2MHE2IDggMTggMTR0MjIgNmgxMTd2MTAwaC0yNjdxLTEwIDAgLTEzIDZ0MyAxNGwxMjAgMTYwcTYgOCAxOCAxNHQyMiA2aDExN3Y0NzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNNjAwIDEwMDB2LTMwMGgyMDNxNjQgMCA4Ni41IDMzdDIyLjUgMTE5cTAgODQgLTIyLjUgMTE2dC04Ni41IDMyaC0yMDN6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjIxMjsiIGQ9Ik0yNTAgNzAwaDgwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC04MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjMxYjsiIGQ9Ik0xMDAwIDEyMDB2LTE1MHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTB2LTEwMHEwIC05MSAtNDkuNSAtMTY1LjV0LTEzMC41IC0xMDkuNXE4MSAtMzUgMTMwLjUgLTEwOS41dDQ5LjUgLTE2NS41di0xNTBoNTBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTE1MGgtODAwdjE1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoNTB2MTUwcTAgOTEgNDkuNSAxNjUuNXQxMzAuNSAxMDkuNXEtODEgMzUgLTEzMC41IDEwOS41IHQtNDkuNSAxNjUuNXYxMDBoLTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxNTBoODAwek00MDAgMTAwMHYtMTAwcTAgLTYwIDMyLjUgLTEwOS41dDg3LjUgLTczLjVxMjggLTEyIDQ0IC0zN3QxNiAtNTV0LTE2IC01NXQtNDQgLTM3cS01NSAtMjQgLTg3LjUgLTczLjV0LTMyLjUgLTEwOS41di0xNTBoNDAwdjE1MHEwIDYwIC0zMi41IDEwOS41dC04Ny41IDczLjVxLTI4IDEyIC00NCAzN3QtMTYgNTV0MTYgNTV0NDQgMzcgcTU1IDI0IDg3LjUgNzMuNXQzMi41IDEwOS41djEwMGgtNDAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDI1ZmM7IiBob3Jpei1hZHYteD0iNTAwIiBkPSJNMCAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDI2MDE7IiBkPSJNNTAzIDEwODlxMTEwIDAgMjAwLjUgLTU5LjV0MTM0LjUgLTE1Ni41cTQ0IDE0IDkwIDE0cTEyMCAwIDIwNSAtODYuNXQ4NSAtMjA2LjVxMCAtMTIxIC04NSAtMjA3LjV0LTIwNSAtODYuNWgtNzUwcS03OSAwIC0xMzUuNSA1N3QtNTYuNSAxMzdxMCA2OSA0Mi41IDEyMi41dDEwOC41IDY3LjVxLTIgMTIgLTIgMzdxMCAxNTMgMTA4IDI2MC41dDI2MCAxMDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyNmZhOyIgZD0iTTc3NCAxMTkzLjVxMTYgLTkuNSAyMC41IC0yN3QtNS41IC0zMy41bC0xMzYgLTE4N2w0NjcgLTc0NmgzMHEyMCAwIDM1IC0xOC41dDE1IC0zOS41di00MmgtMTIwMHY0MnEwIDIxIDE1IDM5LjV0MzUgMTguNWgzMGw0NjggNzQ2bC0xMzUgMTgzcS0xMCAxNiAtNS41IDM0dDIwLjUgMjh0MzQgNS41dDI4IC0yMC41bDExMSAtMTQ4bDExMiAxNTBxOSAxNiAyNyAyMC41dDM0IC01ek02MDAgMjAwaDM3N2wtMTgyIDExMmwtMTk1IDUzNHYtNjQ2eiAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3gyNzA5OyIgZD0iTTI1IDExMDBoMTE1MHExMCAwIDEyLjUgLTV0LTUuNSAtMTNsLTU2NCAtNTY3cS04IC04IC0xOCAtOHQtMTggOGwtNTY0IDU2N3EtOCA4IC01LjUgMTN0MTIuNSA1ek0xOCA4ODJsMjY0IC0yNjRxOCAtOCA4IC0xOHQtOCAtMThsLTI2NCAtMjY0cS04IC04IC0xMyAtNS41dC01IDEyLjV2NTUwcTAgMTAgNSAxMi41dDEzIC01LjV6TTkxOCA2MThsMjY0IDI2NHE4IDggMTMgNS41dDUgLTEyLjV2LTU1MHEwIC0xMCAtNSAtMTIuNXQtMTMgNS41IGwtMjY0IDI2NHEtOCA4IC04IDE4dDggMTh6TTgxOCA0ODJsMzY0IC0zNjRxOCAtOCA1LjUgLTEzdC0xMi41IC01aC0xMTUwcS0xMCAwIC0xMi41IDV0NS41IDEzbDM2NCAzNjRxOCA4IDE4IDh0MTggLThsMTY0IC0xNjRxOCAtOCAxOCAtOHQxOCA4bDE2NCAxNjRxOCA4IDE4IDh0MTggLTh6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjcwZjsiIGQ9Ik0xMDExIDEyMTBxMTkgMCAzMyAtMTNsMTUzIC0xNTNxMTMgLTE0IDEzIC0zM3QtMTMgLTMzbC05OSAtOTJsLTIxNCAyMTRsOTUgOTZxMTMgMTQgMzIgMTR6TTEwMTMgODAwbC02MTUgLTYxNGwtMjE0IDIxNGw2MTQgNjE0ek0zMTcgOTZsLTMzMyAtMTEybDExMCAzMzV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAwMTsiIGQ9Ik03MDAgNjUwdi01NTBoMjUwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGgtODAwdjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWgyNTB2NTUwbC01MDAgNTUwaDEyMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAwMjsiIGQ9Ik0zNjggMTAxN2w2NDUgMTYzcTM5IDE1IDYzIDB0MjQgLTQ5di04MzFxMCAtNTUgLTQxLjUgLTk1LjV0LTExMS41IC02My41cS03OSAtMjUgLTE0NyAtNC41dC04NiA3NXQyNS41IDExMS41dDEyMi41IDgycTcyIDI0IDEzOCA4djUyMWwtNjAwIC0xNTV2LTYwNnEwIC00MiAtNDQgLTkwdC0xMDkgLTY5cS03OSAtMjYgLTE0NyAtNS41dC04NiA3NS41dDI1LjUgMTExLjV0MTIyLjUgODIuNXE3MiAyNCAxMzggN3Y2MzlxMCAzOCAxNC41IDU5IHQ1My41IDM0eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMDM7IiBkPSJNNTAwIDExOTFxMTAwIDAgMTkxIC0zOXQxNTYuNSAtMTA0LjV0MTA0LjUgLTE1Ni41dDM5IC0xOTFsLTEgLTJsMSAtNXEwIC0xNDEgLTc4IC0yNjJsMjc1IC0yNzRxMjMgLTI2IDIyLjUgLTQ0LjV0LTIyLjUgLTQyLjVsLTU5IC01OHEtMjYgLTIwIC00Ni41IC0yMHQtMzkuNSAyMGwtMjc1IDI3NHEtMTE5IC03NyAtMjYxIC03N2wtNSAxbC0yIC0xcS0xMDAgMCAtMTkxIDM5dC0xNTYuNSAxMDQuNXQtMTA0LjUgMTU2LjV0LTM5IDE5MSB0MzkgMTkxdDEwNC41IDE1Ni41dDE1Ni41IDEwNC41dDE5MSAzOXpNNTAwIDEwMjJxLTg4IDAgLTE2MiAtNDN0LTExNyAtMTE3dC00MyAtMTYydDQzIC0xNjJ0MTE3IC0xMTd0MTYyIC00M3QxNjIgNDN0MTE3IDExN3Q0MyAxNjJ0LTQzIDE2MnQtMTE3IDExN3QtMTYyIDQzeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMDU7IiBkPSJNNjQ5IDk0OXE0OCA2OCAxMDkuNSAxMDR0MTIxLjUgMzguNXQxMTguNSAtMjB0MTAyLjUgLTY0dDcxIC0xMDAuNXQyNyAtMTIzcTAgLTU3IC0zMy41IC0xMTcuNXQtOTQgLTEyNC41dC0xMjYuNSAtMTI3LjV0LTE1MCAtMTUyLjV0LTE0NiAtMTc0cS02MiA4NSAtMTQ1LjUgMTc0dC0xNTAgMTUyLjV0LTEyNi41IDEyNy41dC05My41IDEyNC41dC0zMy41IDExNy41cTAgNjQgMjggMTIzdDczIDEwMC41dDEwNCA2NHQxMTkgMjAgdDEyMC41IC0zOC41dDEwNC41IC0xMDR6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAwNjsiIGQ9Ik00MDcgODAwbDEzMSAzNTNxNyAxOSAxNy41IDE5dDE3LjUgLTE5bDEyOSAtMzUzaDQyMXEyMSAwIDI0IC04LjV0LTE0IC0yMC41bC0zNDIgLTI0OWwxMzAgLTQwMXE3IC0yMCAtMC41IC0yNS41dC0yNC41IDYuNWwtMzQzIDI0NmwtMzQyIC0yNDdxLTE3IC0xMiAtMjQuNSAtNi41dC0wLjUgMjUuNWwxMzAgNDAwbC0zNDcgMjUxcS0xNyAxMiAtMTQgMjAuNXQyMyA4LjVoNDI5eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMDc7IiBkPSJNNDA3IDgwMGwxMzEgMzUzcTcgMTkgMTcuNSAxOXQxNy41IC0xOWwxMjkgLTM1M2g0MjFxMjEgMCAyNCAtOC41dC0xNCAtMjAuNWwtMzQyIC0yNDlsMTMwIC00MDFxNyAtMjAgLTAuNSAtMjUuNXQtMjQuNSA2LjVsLTM0MyAyNDZsLTM0MiAtMjQ3cS0xNyAtMTIgLTI0LjUgLTYuNXQtMC41IDI1LjVsMTMwIDQwMGwtMzQ3IDI1MXEtMTcgMTIgLTE0IDIwLjV0MjMgOC41aDQyOXpNNDc3IDcwMGgtMjQwbDE5NyAtMTQybC03NCAtMjI2IGwxOTMgMTM5bDE5NSAtMTQwbC03NCAyMjlsMTkyIDE0MGgtMjM0bC03OCAyMTF6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAwODsiIGQ9Ik02MDAgMTIwMHExMjQgMCAyMTIgLTg4dDg4IC0yMTJ2LTI1MHEwIC00NiAtMzEgLTk4dC02OSAtNTJ2LTc1cTAgLTEwIDYgLTIxLjV0MTUgLTE3LjVsMzU4IC0yMzBxOSAtNSAxNSAtMTYuNXQ2IC0yMS41di05M3EwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTExNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY5M3EwIDEwIDYgMjEuNXQxNSAxNi41bDM1OCAyMzBxOSA2IDE1IDE3LjV0NiAyMS41djc1cS0zOCAwIC02OSA1MiB0LTMxIDk4djI1MHEwIDEyNCA4OCAyMTJ0MjEyIDg4eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMDk7IiBkPSJNMjUgMTEwMGgxMTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTA1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTExNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxMDUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTEwMCAxMDAwdi0xMDBoMTAwdjEwMGgtMTAwek04NzUgMTAwMGgtNTUwcS0xMCAwIC0xNy41IC03LjV0LTcuNSAtMTcuNXYtMzUwcTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoNTUwIHExMCAwIDE3LjUgNy41dDcuNSAxNy41djM1MHEwIDEwIC03LjUgMTcuNXQtMTcuNSA3LjV6TTEwMDAgMTAwMHYtMTAwaDEwMHYxMDBoLTEwMHpNMTAwIDgwMHYtMTAwaDEwMHYxMDBoLTEwMHpNMTAwMCA4MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTEwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTEwMDAgNjAwdi0xMDBoMTAwdjEwMGgtMTAwek04NzUgNTAwaC01NTBxLTEwIDAgLTE3LjUgLTcuNXQtNy41IC0xNy41di0zNTBxMCAtMTAgNy41IC0xNy41IHQxNy41IC03LjVoNTUwcTEwIDAgMTcuNSA3LjV0Ny41IDE3LjV2MzUwcTAgMTAgLTcuNSAxNy41dC0xNy41IDcuNXpNMTAwIDQwMHYtMTAwaDEwMHYxMDBoLTEwMHpNMTAwMCA0MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTEwMCAyMDB2LTEwMGgxMDB2MTAwaC0xMDB6TTEwMDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMTA7IiBkPSJNNTAgMTEwMGg0MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNDAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek02NTAgMTEwMGg0MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNDAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDAgcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgNTAwaDQwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djQwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTY1MCA1MDBoNDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di00MDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTQwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djQwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAxMTsiIGQ9Ik01MCAxMTAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQ1MCAxMTAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek04NTAgMTEwMGgyMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTIwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMjAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYyMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA3MDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQ1MCA3MDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNODUwIDcwMGgyMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTIwMCBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgMzAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQ1MCAzMDBoMjAwIHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTg1MCAzMDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41IHQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAxMjsiIGQ9Ik01MCAxMTAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQ1MCAxMTAwaDcwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC03MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA3MDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNDUwIDcwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTIwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNzAwIHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgMzAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQ1MCAzMDBoNzAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yMDAgcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC03MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAxMzsiIGQ9Ik00NjUgNDc3bDU3MSA1NzFxOCA4IDE4IDh0MTcgLThsMTc3IC0xNzdxOCAtNyA4IC0xN3QtOCAtMThsLTc4MyAtNzg0cS03IC04IC0xNy41IC04dC0xNy41IDhsLTM4NCAzODRxLTggOCAtOCAxOHQ4IDE3bDE3NyAxNzdxNyA4IDE3IDh0MTggLThsMTcxIC0xNzFxNyAtNyAxOCAtN3QxOCA3eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMTQ7IiBkPSJNOTA0IDEwODNsMTc4IC0xNzlxOCAtOCA4IC0xOC41dC04IC0xNy41bC0yNjcgLTI2OGwyNjcgLTI2OHE4IC03IDggLTE3LjV0LTggLTE4LjVsLTE3OCAtMTc4cS04IC04IC0xOC41IC04dC0xNy41IDhsLTI2OCAyNjdsLTI2OCAtMjY3cS03IC04IC0xNy41IC04dC0xOC41IDhsLTE3OCAxNzhxLTggOCAtOCAxOC41dDggMTcuNWwyNjcgMjY4bC0yNjcgMjY4cS04IDcgLTggMTcuNXQ4IDE4LjVsMTc4IDE3OHE4IDggMTguNSA4dDE3LjUgLTggbDI2OCAtMjY3bDI2OCAyNjhxNyA3IDE3LjUgN3QxOC41IC03eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMTU7IiBkPSJNNTA3IDExNzdxOTggMCAxODcuNSAtMzguNXQxNTQuNSAtMTAzLjV0MTAzLjUgLTE1NC41dDM4LjUgLTE4Ny41cTAgLTE0MSAtNzggLTI2MmwzMDAgLTI5OXE4IC04IDggLTE4LjV0LTggLTE4LjVsLTEwOSAtMTA4cS03IC04IC0xNy41IC04dC0xOC41IDhsLTMwMCAyOTlxLTExOSAtNzcgLTI2MSAtNzdxLTk4IDAgLTE4OCAzOC41dC0xNTQuNSAxMDN0LTEwMyAxNTQuNXQtMzguNSAxODh0MzguNSAxODcuNXQxMDMgMTU0LjUgdDE1NC41IDEwMy41dDE4OCAzOC41ek01MDYuNSAxMDIzcS04OS41IDAgLTE2NS41IC00NHQtMTIwIC0xMjAuNXQtNDQgLTE2NnQ0NCAtMTY1LjV0MTIwIC0xMjB0MTY1LjUgLTQ0dDE2NiA0NHQxMjAuNSAxMjB0NDQgMTY1LjV0LTQ0IDE2NnQtMTIwLjUgMTIwLjV0LTE2NiA0NHpNNDI1IDkwMGgxNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di03NWg3NXExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41IHQtMTcuNSAtNy41aC03NXYtNzVxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0xNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY3NWgtNzVxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWg3NXY3NXEwIDEwIDcuNSAxNy41dDE3LjUgNy41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMTY7IiBkPSJNNTA3IDExNzdxOTggMCAxODcuNSAtMzguNXQxNTQuNSAtMTAzLjV0MTAzLjUgLTE1NC41dDM4LjUgLTE4Ny41cTAgLTE0MSAtNzggLTI2MmwzMDAgLTI5OXE4IC04IDggLTE4LjV0LTggLTE4LjVsLTEwOSAtMTA4cS03IC04IC0xNy41IC04dC0xOC41IDhsLTMwMCAyOTlxLTExOSAtNzcgLTI2MSAtNzdxLTk4IDAgLTE4OCAzOC41dC0xNTQuNSAxMDN0LTEwMyAxNTQuNXQtMzguNSAxODh0MzguNSAxODcuNXQxMDMgMTU0LjUgdDE1NC41IDEwMy41dDE4OCAzOC41ek01MDYuNSAxMDIzcS04OS41IDAgLTE2NS41IC00NHQtMTIwIC0xMjAuNXQtNDQgLTE2NnQ0NCAtMTY1LjV0MTIwIC0xMjB0MTY1LjUgLTQ0dDE2NiA0NHQxMjAuNSAxMjB0NDQgMTY1LjV0LTQ0IDE2NnQtMTIwLjUgMTIwLjV0LTE2NiA0NHpNMzI1IDgwMGgzNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0zNTBxLTEwIDAgLTE3LjUgNy41IHQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAxNzsiIGQ9Ik01NTAgMTIwMGgxMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek04MDAgOTc1djE2NnExNjcgLTYyIDI3MiAtMjA5LjV0MTA1IC0zMzEuNXEwIC0xMTcgLTQ1LjUgLTIyNHQtMTIzIC0xODQuNXQtMTg0LjUgLTEyM3QtMjI0IC00NS41dC0yMjQgNDUuNSB0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHEwIDE4NCAxMDUgMzMxLjV0MjcyIDIwOS41di0xNjZxLTEwMyAtNTUgLTE2NSAtMTU1dC02MiAtMjIwcTAgLTExNiA1NyAtMjE0LjV0MTU1LjUgLTE1NS41dDIxNC41IC01N3QyMTQuNSA1N3QxNTUuNSAxNTUuNXQ1NyAyMTQuNXEwIDEyMCAtNjIgMjIwdC0xNjUgMTU1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMTg7IiBkPSJNMTAyNSAxMjAwaDE1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTExNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0xNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxMTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTcyNSA4MDBoMTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNzUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2NzUwIHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek00MjUgNTAwaDE1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTQ1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djQ1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek0xMjUgMzAwaDE1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTI1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41IHYyNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDE5OyIgZD0iTTYwMCAxMTc0cTMzIDAgNzQgLTVsMzggLTE1Mmw1IC0xcTQ5IC0xNCA5NCAtMzlsNSAtMmwxMzQgODBxNjEgLTQ4IDEwNCAtMTA1bC04MCAtMTM0bDMgLTVxMjUgLTQ0IDM5IC05M2wxIC02bDE1MiAtMzhxNSAtNDMgNSAtNzNxMCAtMzQgLTUgLTc0bC0xNTIgLTM4bC0xIC02cS0xNSAtNDkgLTM5IC05M2wtMyAtNWw4MCAtMTM0cS00OCAtNjEgLTEwNCAtMTA1bC0xMzQgODFsLTUgLTNxLTQ0IC0yNSAtOTQgLTM5bC01IC0ybC0zOCAtMTUxIHEtNDMgLTUgLTc0IC01cS0zMyAwIC03NCA1bC0zOCAxNTFsLTUgMnEtNDkgMTQgLTk0IDM5bC01IDNsLTEzNCAtODFxLTYwIDQ4IC0xMDQgMTA1bDgwIDEzNGwtMyA1cS0yNSA0NSAtMzggOTNsLTIgNmwtMTUxIDM4cS02IDQyIC02IDc0cTAgMzMgNiA3M2wxNTEgMzhsMiA2cTEzIDQ4IDM4IDkzbDMgNWwtODAgMTM0cTQ3IDYxIDEwNSAxMDVsMTMzIC04MGw1IDJxNDUgMjUgOTQgMzlsNSAxbDM4IDE1MnE0MyA1IDc0IDV6TTYwMCA4MTUgcS04OSAwIC0xNTIgLTYzdC02MyAtMTUxLjV0NjMgLTE1MS41dDE1MiAtNjN0MTUyIDYzdDYzIDE1MS41dC02MyAxNTEuNXQtMTUyIDYzeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMjA7IiBkPSJNNTAwIDEzMDBoMzAwcTQxIDAgNzAuNSAtMjkuNXQyOS41IC03MC41di0xMDBoMjc1cTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNzVoLTExMDB2NzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgyNzV2MTAwcTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNXpNNTAwIDEyMDB2LTEwMGgzMDB2MTAwaC0zMDB6TTExMDAgOTAwdi04MDBxMCAtNDEgLTI5LjUgLTcwLjV0LTcwLjUgLTI5LjVoLTcwMHEtNDEgMCAtNzAuNSAyOS41dC0yOS41IDcwLjUgdjgwMGg5MDB6TTMwMCA4MDB2LTcwMGgxMDB2NzAwaC0xMDB6TTUwMCA4MDB2LTcwMGgxMDB2NzAwaC0xMDB6TTcwMCA4MDB2LTcwMGgxMDB2NzAwaC0xMDB6TTkwMCA4MDB2LTcwMGgxMDB2NzAwaC0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAyMTsiIGQ9Ik0xOCA2MThsNjIwIDYwOHE4IDcgMTguNSA3dDE3LjUgLTdsNjA4IC02MDhxOCAtOCA1LjUgLTEzdC0xMi41IC01aC0xNzV2LTU3NXEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTI1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djM3NWgtMzAwdi0zNzVxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1NzVoLTE3NXEtMTAgMCAtMTIuNSA1dDUuNSAxM3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDIyOyIgZD0iTTYwMCAxMjAwdi00MDBxMCAtNDEgMjkuNSAtNzAuNXQ3MC41IC0yOS41aDMwMHYtNjUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC04MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djExMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDQ1MHpNMTAwMCA4MDBoLTI1MHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MjUweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMjM7IiBkPSJNNjAwIDExNzdxMTE3IDAgMjI0IC00NS41dDE4NC41IC0xMjN0MTIzIC0xODQuNXQ0NS41IC0yMjR0LTQ1LjUgLTIyNHQtMTIzIC0xODQuNXQtMTg0LjUgLTEyM3QtMjI0IC00NS41dC0yMjQgNDUuNXQtMTg0LjUgMTIzdC0xMjMgMTg0LjV0LTQ1LjUgMjI0dDQ1LjUgMjI0dDEyMyAxODQuNXQxODQuNSAxMjN0MjI0IDQ1LjV6TTYwMCAxMDI3cS0xMTYgMCAtMjE0LjUgLTU3dC0xNTUuNSAtMTU1LjV0LTU3IC0yMTQuNXQ1NyAtMjE0LjUgdDE1NS41IC0xNTUuNXQyMTQuNSAtNTd0MjE0LjUgNTd0MTU1LjUgMTU1LjV0NTcgMjE0LjV0LTU3IDIxNC41dC0xNTUuNSAxNTUuNXQtMjE0LjUgNTd6TTUyNSA5MDBoNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0yNzVoMTc1cTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYzNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDI0OyIgZD0iTTEzMDAgMGgtNTM4bC00MSA0MDBoLTI0MmwtNDEgLTQwMGgtNTM4bDQzMSAxMjAwaDIwOWwtMjEgLTMwMGgxNjJsLTIwIDMwMGgyMDh6TTUxNSA4MDBsLTI3IC0zMDBoMjI0bC0yNyAzMDBoLTE3MHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDI1OyIgZD0iTTU1MCAxMjAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDUwaDE5MXEyMCAwIDI1LjUgLTExLjV0LTcuNSAtMjcuNWwtMzI3IC00MDBxLTEzIC0xNiAtMzIgLTE2dC0zMiAxNmwtMzI3IDQwMHEtMTMgMTYgLTcuNSAyNy41dDI1LjUgMTEuNWgxOTF2NDUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMTEyNSA0MDBoNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0zNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41IGgtMTA1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djM1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41aDUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTc1aDkwMHYxNzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDI2OyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02MDAgMTAyN3EtMTE2IDAgLTIxNC41IC01N3QtMTU1LjUgLTE1NS41dC01NyAtMjE0LjV0NTcgLTIxNC41IHQxNTUuNSAtMTU1LjV0MjE0LjUgLTU3dDIxNC41IDU3dDE1NS41IDE1NS41dDU3IDIxNC41dC01NyAyMTQuNXQtMTU1LjUgMTU1LjV0LTIxNC41IDU3ek01MjUgOTAwaDE1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTI3NWgxMzdxMjEgMCAyNiAtMTEuNXQtOCAtMjcuNWwtMjIzIC0yNzVxLTEzIC0xNiAtMzIgLTE2dC0zMiAxNmwtMjIzIDI3NXEtMTMgMTYgLTggMjcuNXQyNiAxMS41aDEzN3YyNzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXogIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAyNzsiIGQ9Ik02MDAgMTE3N3ExMTcgMCAyMjQgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNHQtNDUuNSAtMjI0dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjQgLTQ1LjV0LTIyNCA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjR0NDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXpNNjAwIDEwMjdxLTExNiAwIC0yMTQuNSAtNTd0LTE1NS41IC0xNTUuNXQtNTcgLTIxNC41dDU3IC0yMTQuNSB0MTU1LjUgLTE1NS41dDIxNC41IC01N3QyMTQuNSA1N3QxNTUuNSAxNTUuNXQ1NyAyMTQuNXQtNTcgMjE0LjV0LTE1NS41IDE1NS41dC0yMTQuNSA1N3pNNjMyIDkxNGwyMjMgLTI3NXExMyAtMTYgOCAtMjcuNXQtMjYgLTExLjVoLTEzN3YtMjc1cTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2Mjc1aC0xMzdxLTIxIDAgLTI2IDExLjV0OCAyNy41bDIyMyAyNzVxMTMgMTYgMzIgMTYgdDMyIC0xNnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDI4OyIgZD0iTTIyNSAxMjAwaDc1MHExMCAwIDE5LjUgLTd0MTIuNSAtMTdsMTg2IC02NTJxNyAtMjQgNyAtNDl2LTQyNXEwIC0xMiAtNCAtMjd0LTkgLTE3cS0xMiAtNiAtMzcgLTZoLTExMDBxLTEyIDAgLTI3IDR0LTE3IDhxLTYgMTMgLTYgMzhsMSA0MjVxMCAyNSA3IDQ5bDE4NSA2NTJxMyAxMCAxMi41IDE3dDE5LjUgN3pNODc4IDEwMDBoLTU1NnEtMTAgMCAtMTkgLTd0LTExIC0xOGwtODcgLTQ1MHEtMiAtMTEgNCAtMTh0MTYgLTdoMTUwIHExMCAwIDE5LjUgLTd0MTEuNSAtMTdsMzggLTE1MnEyIC0xMCAxMS41IC0xN3QxOS41IC03aDI1MHExMCAwIDE5LjUgN3QxMS41IDE3bDM4IDE1MnEyIDEwIDExLjUgMTd0MTkuNSA3aDE1MHExMCAwIDE2IDd0NCAxOGwtODcgNDUwcS0yIDExIC0xMSAxOHQtMTkgN3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDI5OyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02MDAgMTAyN3EtMTE2IDAgLTIxNC41IC01N3QtMTU1LjUgLTE1NS41dC01NyAtMjE0LjV0NTcgLTIxNC41IHQxNTUuNSAtMTU1LjV0MjE0LjUgLTU3dDIxNC41IDU3dDE1NS41IDE1NS41dDU3IDIxNC41dC01NyAyMTQuNXQtMTU1LjUgMTU1LjV0LTIxNC41IDU3ek01NDAgODIwbDI1MyAtMTkwcTE3IC0xMiAxNyAtMzB0LTE3IC0zMGwtMjUzIC0xOTBxLTE2IC0xMiAtMjggLTYuNXQtMTIgMjYuNXY0MDBxMCAyMSAxMiAyNi41dDI4IC02LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAzMDsiIGQ9Ik05NDcgMTA2MGwxMzUgMTM1cTcgNyAxMi41IDV0NS41IC0xM3YtMzYycTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMzYycS0xMSAwIC0xMyA1LjV0NSAxMi41bDEzMyAxMzNxLTEwOSA3NiAtMjM4IDc2cS0xMTYgMCAtMjE0LjUgLTU3dC0xNTUuNSAtMTU1LjV0LTU3IC0yMTQuNXQ1NyAtMjE0LjV0MTU1LjUgLTE1NS41dDIxNC41IC01N3QyMTQuNSA1N3QxNTUuNSAxNTUuNXQ1NyAyMTQuNWgxNTBxMCAtMTE3IC00NS41IC0yMjQgdC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjQgLTQ1LjV0LTIyNCA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjR0NDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXExOTIgMCAzNDcgLTExN3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDMxOyIgZD0iTTk0NyAxMDYwbDEzNSAxMzVxNyA3IDEyLjUgNXQ1LjUgLTEzdi0zNjFxMCAtMTEgLTcuNSAtMTguNXQtMTguNSAtNy41aC0zNjFxLTExIDAgLTEzIDUuNXQ1IDEyLjVsMTM0IDEzNHEtMTEwIDc1IC0yMzkgNzVxLTExNiAwIC0yMTQuNSAtNTd0LTE1NS41IC0xNTUuNXQtNTcgLTIxNC41aC0xNTBxMCAxMTcgNDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXExOTIgMCAzNDcgLTExN3pNMTAyNyA2MDBoMTUwIHEwIC0xMTcgLTQ1LjUgLTIyNHQtMTIzIC0xODQuNXQtMTg0LjUgLTEyM3QtMjI0IC00NS41cS0xOTIgMCAtMzQ4IDExOGwtMTM0IC0xMzRxLTcgLTggLTEyLjUgLTUuNXQtNS41IDEyLjV2MzYwcTAgMTEgNy41IDE4LjV0MTguNSA3LjVoMzYwcTEwIDAgMTIuNSAtNS41dC01LjUgLTEyLjVsLTEzMyAtMTMzcTExMCAtNzYgMjQwIC03NnExMTYgMCAyMTQuNSA1N3QxNTUuNSAxNTUuNXQ1NyAyMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDMyOyIgZD0iTTEyNSAxMjAwaDEwNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xMTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMTA1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djExNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNMTA3NSAxMDAwaC04NTBxLTEwIDAgLTE3LjUgLTcuNXQtNy41IC0xNy41di04NTBxMCAtMTAgNy41IC0xNy41dDE3LjUgLTcuNWg4NTBxMTAgMCAxNy41IDcuNXQ3LjUgMTcuNXY4NTAgcTAgMTAgLTcuNSAxNy41dC0xNy41IDcuNXpNMzI1IDkwMGg1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek01MjUgOTAwaDQ1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtNDUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2NTAgcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTMyNSA3MDBoNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di01MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2NTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNNTI1IDcwMGg0NTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di01MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTQ1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djUwIHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek0zMjUgNTAwaDUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC01MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTUyNSA1MDBoNDUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC00NTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1MCBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNMzI1IDMwMGg1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek01MjUgMzAwaDQ1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtNDUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2NTAgcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAzMzsiIGQ9Ik05MDAgODAwdjIwMHEwIDgzIC01OC41IDE0MS41dC0xNDEuNSA1OC41aC0zMDBxLTgyIDAgLTE0MSAtNTl0LTU5IC0xNDF2LTIwMGgtMTAwcS00MSAwIC03MC41IC0yOS41dC0yOS41IC03MC41di02MDBxMCAtNDEgMjkuNSAtNzAuNXQ3MC41IC0yOS41aDkwMHE0MSAwIDcwLjUgMjkuNXQyOS41IDcwLjV2NjAwcTAgNDEgLTI5LjUgNzAuNXQtNzAuNSAyOS41aC0xMDB6TTQwMCA4MDB2MTUwcTAgMjEgMTUgMzUuNXQzNSAxNC41aDIwMCBxMjAgMCAzNSAtMTQuNXQxNSAtMzUuNXYtMTUwaC0zMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAzNDsiIGQ9Ik0xMjUgMTEwMGg1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTEwNzVoLTEwMHYxMDc1cTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTEwNzUgMTA1MnE0IDAgOSAtMnExNiAtNiAxNiAtMjN2LTQyMXEwIC02IC0zIC0xMnEtMzMgLTU5IC02Ni41IC05OXQtNjUuNSAtNTh0LTU2LjUgLTI0LjV0LTUyLjUgLTYuNXEtMjYgMCAtNTcuNSA2LjV0LTUyLjUgMTMuNXQtNjAgMjFxLTQxIDE1IC02MyAyMi41dC01Ny41IDE1dC02NS41IDcuNSBxLTg1IDAgLTE2MCAtNTdxLTcgLTUgLTE1IC01cS02IDAgLTExIDNxLTE0IDcgLTE0IDIydjQzOHEyMiA1NSA4MiA5OC41dDExOSA0Ni41cTIzIDIgNDMgMC41dDQzIC03dDMyLjUgLTguNXQzOCAtMTN0MzIuNSAtMTFxNDEgLTE0IDYzLjUgLTIxdDU3IC0xNHQ2My41IC03cTEwMyAwIDE4MyA4N3E3IDggMTggOHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDM1OyIgZD0iTTYwMCAxMTc1cTExNiAwIDIyNyAtNDkuNXQxOTIuNSAtMTMxdDEzMSAtMTkyLjV0NDkuNSAtMjI3di0zMDBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC01MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djMwMHEwIDEyNyAtNzAuNSAyMzEuNXQtMTg0LjUgMTYxLjV0LTI0NSA1N3QtMjQ1IC01N3QtMTg0LjUgLTE2MS41dC03MC41IC0yMzEuNXYtMzAwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtNTAgcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MzAwcTAgMTE2IDQ5LjUgMjI3dDEzMSAxOTIuNXQxOTIuNSAxMzF0MjI3IDQ5LjV6TTIyMCA1MDBoMTYwcTggMCAxNCAtNnQ2IC0xNHYtNDYwcTAgLTggLTYgLTE0dC0xNCAtNmgtMTYwcS04IDAgLTE0IDZ0LTYgMTR2NDYwcTAgOCA2IDE0dDE0IDZ6TTgyMCA1MDBoMTYwcTggMCAxNCAtNnQ2IC0xNHYtNDYwcTAgLTggLTYgLTE0dC0xNCAtNmgtMTYwcS04IDAgLTE0IDZ0LTYgMTR2NDYwIHEwIDggNiAxNHQxNCA2eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMzY7IiBkPSJNMzIxIDgxNGwyNTggMTcycTkgNiAxNSAyLjV0NiAtMTMuNXYtNzUwcTAgLTEwIC02IC0xMy41dC0xNSAyLjVsLTI1OCAxNzJxLTIxIDE0IC00NiAxNGgtMjUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MzUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoMjUwcTI1IDAgNDYgMTR6TTkwMCA2NjhsMTIwIDEyMHE3IDcgMTcgN3QxNyAtN2wzNCAtMzRxNyAtNyA3IC0xN3QtNyAtMTdsLTEyMCAtMTIwbDEyMCAtMTIwcTcgLTcgNyAtMTcgdC03IC0xN2wtMzQgLTM0cS03IC03IC0xNyAtN3QtMTcgN2wtMTIwIDExOWwtMTIwIC0xMTlxLTcgLTcgLTE3IC03dC0xNyA3bC0zNCAzNHEtNyA3IC03IDE3dDcgMTdsMTE5IDEyMGwtMTE5IDEyMHEtNyA3IC03IDE3dDcgMTdsMzQgMzRxNyA4IDE3IDh0MTcgLTh6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTAzNzsiIGQ9Ik0zMjEgODE0bDI1OCAxNzJxOSA2IDE1IDIuNXQ2IC0xMy41di03NTBxMCAtMTAgLTYgLTEzLjV0LTE1IDIuNWwtMjU4IDE3MnEtMjEgMTQgLTQ2IDE0aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYzNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgyNTBxMjUgMCA0NiAxNHpNNzY2IDkwMGg0cTEwIC0xIDE2IC0xMHE5NiAtMTI5IDk2IC0yOTBxMCAtMTU0IC05MCAtMjgxcS02IC05IC0xNyAtMTBsLTMgLTFxLTkgMCAtMTYgNiBsLTI5IDIzcS03IDcgLTguNSAxNi41dDQuNSAxNy41cTcyIDEwMyA3MiAyMjlxMCAxMzIgLTc4IDIzOHEtNiA4IC00LjUgMTh0OS41IDE3bDI5IDIycTcgNSAxNSA1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwMzg7IiBkPSJNOTY3IDEwMDRoM3ExMSAtMSAxNyAtMTBxMTM1IC0xNzkgMTM1IC0zOTZxMCAtMTA1IC0zNCAtMjA2LjV0LTk4IC0xODUuNXEtNyAtOSAtMTcgLTEwaC0zcS05IDAgLTE2IDZsLTQyIDM0cS04IDYgLTkgMTZ0NSAxOHExMTEgMTUwIDExMSAzMjhxMCA5MCAtMjkuNSAxNzZ0LTg0LjUgMTU3cS02IDkgLTUgMTl0MTAgMTZsNDIgMzNxNyA1IDE1IDV6TTMyMSA4MTRsMjU4IDE3MnE5IDYgMTUgMi41dDYgLTEzLjV2LTc1MHEwIC0xMCAtNiAtMTMuNSB0LTE1IDIuNWwtMjU4IDE3MnEtMjEgMTQgLTQ2IDE0aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYzNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgyNTBxMjUgMCA0NiAxNHpNNzY2IDkwMGg0cTEwIC0xIDE2IC0xMHE5NiAtMTI5IDk2IC0yOTBxMCAtMTU0IC05MCAtMjgxcS02IC05IC0xNyAtMTBsLTMgLTFxLTkgMCAtMTYgNmwtMjkgMjNxLTcgNyAtOC41IDE2LjV0NC41IDE3LjVxNzIgMTAzIDcyIDIyOXEwIDEzMiAtNzggMjM4IHEtNiA4IC00LjUgMTguNXQ5LjUgMTYuNWwyOSAyMnE3IDUgMTUgNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDM5OyIgZD0iTTUwMCA5MDBoMTAwdi0xMDBoLTEwMHYtMTAwaC00MDB2LTEwMGgtMTAwdjYwMGg1MDB2LTMwMHpNMTIwMCA3MDBoLTIwMHYtMTAwaDIwMHYtMjAwaC0zMDB2MzAwaC0yMDB2MzAwaC0xMDB2MjAwaDYwMHYtNTAwek0xMDAgMTEwMHYtMzAwaDMwMHYzMDBoLTMwMHpNODAwIDExMDB2LTMwMGgzMDB2MzAwaC0zMDB6TTMwMCA5MDBoLTEwMHYxMDBoMTAwdi0xMDB6TTEwMDAgOTAwaC0xMDB2MTAwaDEwMHYtMTAwek0zMDAgNTAwaDIwMHYtNTAwIGgtNTAwdjUwMGgyMDB2MTAwaDEwMHYtMTAwek04MDAgMzAwaDIwMHYtMTAwaC0xMDB2LTEwMGgtMjAwdjEwMGgtMTAwdjEwMGgxMDB2MjAwaC0yMDB2MTAwaDMwMHYtMzAwek0xMDAgNDAwdi0zMDBoMzAwdjMwMGgtMzAwek0zMDAgMjAwaC0xMDB2MTAwaDEwMHYtMTAwek0xMjAwIDIwMGgtMTAwdjEwMGgxMDB2LTEwMHpNNzAwIDBoLTEwMHYxMDBoMTAwdi0xMDB6TTEyMDAgMGgtMzAwdjEwMGgzMDB2LTEwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQwOyIgZD0iTTEwMCAyMDBoLTEwMHYxMDAwaDEwMHYtMTAwMHpNMzAwIDIwMGgtMTAwdjEwMDBoMTAwdi0xMDAwek03MDAgMjAwaC0yMDB2MTAwMGgyMDB2LTEwMDB6TTkwMCAyMDBoLTEwMHYxMDAwaDEwMHYtMTAwMHpNMTIwMCAyMDBoLTIwMHYxMDAwaDIwMHYtMTAwMHpNNDAwIDBoLTMwMHYxMDBoMzAwdi0xMDB6TTYwMCAwaC0xMDB2OTFoMTAwdi05MXpNODAwIDBoLTEwMHY5MWgxMDB2LTkxek0xMTAwIDBoLTIwMHY5MWgyMDB2LTkxeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNDE7IiBkPSJNNTAwIDEyMDBsNjgyIC02ODJxOCAtOCA4IC0xOHQtOCAtMThsLTQ2NCAtNDY0cS04IC04IC0xOCAtOHQtMTggOGwtNjgyIDY4MmwxIDQ3NXEwIDEwIDcuNSAxNy41dDE3LjUgNy41aDQ3NHpNMzE5LjUgMTAyNC41cS0yOS41IDI5LjUgLTcxIDI5LjV0LTcxIC0yOS41dC0yOS41IC03MS41dDI5LjUgLTcxLjV0NzEgLTI5LjV0NzEgMjkuNXQyOS41IDcxLjV0LTI5LjUgNzEuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQyOyIgZD0iTTUwMCAxMjAwbDY4MiAtNjgycTggLTggOCAtMTh0LTggLTE4bC00NjQgLTQ2NHEtOCAtOCAtMTggLTh0LTE4IDhsLTY4MiA2ODJsMSA0NzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWg0NzR6TTgwMCAxMjAwbDY4MiAtNjgycTggLTggOCAtMTh0LTggLTE4bC00NjQgLTQ2NHEtOCAtOCAtMTggLTh0LTE4IDhsLTU2IDU2bDQyNCA0MjZsLTcwMCA3MDBoMTUwek0zMTkuNSAxMDI0LjVxLTI5LjUgMjkuNSAtNzEgMjkuNXQtNzEgLTI5LjUgdC0yOS41IC03MS41dDI5LjUgLTcxLjV0NzEgLTI5LjV0NzEgMjkuNXQyOS41IDcxLjV0LTI5LjUgNzEuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQzOyIgZD0iTTMwMCAxMjAwaDgyNXE3NSAwIDc1IC03NXYtOTAwcTAgLTI1IC0xOCAtNDNsLTY0IC02NHEtOCAtOCAtMTMgLTUuNXQtNSAxMi41djk1MHEwIDEwIC03LjUgMTcuNXQtMTcuNSA3LjVoLTcwMHEtMjUgMCAtNDMgLTE4bC02NCAtNjRxLTggLTggLTUuNSAtMTN0MTIuNSAtNWg3MDBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di05NTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC04NTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY5NzUgcTAgMjUgMTggNDNsMTM5IDEzOXExOCAxOCA0MyAxOHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQ0OyIgZD0iTTI1MCAxMjAwaDgwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTE1MGwtNDUwIDQ0NGwtNDUwIC00NDV2MTE1MXEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA0NTsiIGQ9Ik04MjIgMTIwMGgtNDQ0cS0xMSAwIC0xOSAtNy41dC05IC0xNy41bC03OCAtMzAxcS03IC0yNCA3IC00NWw1NyAtMTA4cTYgLTkgMTcuNSAtMTV0MjEuNSAtNmg0NTBxMTAgMCAyMS41IDZ0MTcuNSAxNWw2MiAxMDhxMTQgMjEgNyA0NWwtODMgMzAxcS0xIDEwIC05IDE3LjV0LTE5IDcuNXpNMTE3NSA4MDBoLTE1MHEtMTAgMCAtMjEgLTYuNXQtMTUgLTE1LjVsLTc4IC0xNTZxLTQgLTkgLTE1IC0xNS41dC0yMSAtNi41aC01NTAgcS0xMCAwIC0yMSA2LjV0LTE1IDE1LjVsLTc4IDE1NnEtNCA5IC0xNSAxNS41dC0yMSA2LjVoLTE1MHEtMTAgMCAtMTcuNSAtNy41dC03LjUgLTE3LjV2LTY1MHEwIC0xMCA3LjUgLTE3LjV0MTcuNSAtNy41aDE1MHExMCAwIDE3LjUgNy41dDcuNSAxNy41djE1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41aDc1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCA3LjUgLTE3LjV0MTcuNSAtNy41aDE1MHExMCAwIDE3LjUgNy41IHQ3LjUgMTcuNXY2NTBxMCAxMCAtNy41IDE3LjV0LTE3LjUgNy41ek04NTAgMjAwaC01MDBxLTEwIDAgLTE5LjUgLTd0LTExLjUgLTE3bC0zOCAtMTUycS0yIC0xMCAzLjUgLTE3dDE1LjUgLTdoNjAwcTEwIDAgMTUuNSA3dDMuNSAxN2wtMzggMTUycS0yIDEwIC0xMS41IDE3dC0xOS41IDd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA0NjsiIGQ9Ik01MDAgMTEwMGgyMDBxNTYgMCAxMDIuNSAtMjAuNXQ3Mi41IC01MHQ0NCAtNTl0MjUgLTUwLjVsNiAtMjBoMTUwcTQxIDAgNzAuNSAtMjkuNXQyOS41IC03MC41di02MDBxMCAtNDEgLTI5LjUgLTcwLjV0LTcwLjUgLTI5LjVoLTEwMDBxLTQxIDAgLTcwLjUgMjkuNXQtMjkuNSA3MC41djYwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjVoMTUwcTIgOCA2LjUgMjEuNXQyNCA0OHQ0NSA2MXQ3MiA0OHQxMDIuNSAyMS41ek05MDAgODAwdi0xMDAgaDEwMHYxMDBoLTEwMHpNNjAwIDczMHEtOTUgMCAtMTYyLjUgLTY3LjV0LTY3LjUgLTE2Mi41dDY3LjUgLTE2Mi41dDE2Mi41IC02Ny41dDE2Mi41IDY3LjV0NjcuNSAxNjIuNXQtNjcuNSAxNjIuNXQtMTYyLjUgNjcuNXpNNjAwIDYwM3E0MyAwIDczIC0zMHQzMCAtNzN0LTMwIC03M3QtNzMgLTMwdC03MyAzMHQtMzAgNzN0MzAgNzN0NzMgMzB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA0NzsiIGQ9Ik02ODEgMTE5OWwzODUgLTk5OHEyMCAtNTAgNjAgLTkycTE4IC0xOSAzNi41IC0yOS41dDI3LjUgLTExLjVsMTAgLTJ2LTY2aC00MTd2NjZxNTMgMCA3NSA0My41dDUgODguNWwtODIgMjIyaC0zOTFxLTU4IC0xNDUgLTkyIC0yMzRxLTExIC0zNCAtNi41IC01N3QyNS41IC0zN3Q0NiAtMjB0NTUgLTZ2LTY2aC0zNjV2NjZxNTYgMjQgODQgNTJxMTIgMTIgMjUgMzAuNXQyMCAzMS41bDcgMTNsMzk5IDEwMDZoOTN6TTQxNiA1MjFoMzQwIGwtMTYyIDQ1N3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQ4OyIgZD0iTTc1MyA2NDFxNSAtMSAxNC41IC00LjV0MzYgLTE1LjV0NTAuNSAtMjYuNXQ1My41IC00MHQ1MC41IC01NC41dDM1LjUgLTcwdDE0LjUgLTg3cTAgLTY3IC0yNy41IC0xMjUuNXQtNzEuNSAtOTcuNXQtOTguNSAtNjYuNXQtMTA4LjUgLTQwLjV0LTEwMiAtMTNoLTUwMHY4OXE0MSA3IDcwLjUgMzIuNXQyOS41IDY1LjV2ODI3cTAgMjQgLTAuNSAzNHQtMy41IDI0dC04LjUgMTkuNXQtMTcgMTMuNXQtMjggMTIuNXQtNDIuNSAxMS41djcxIGw0NzEgLTFxNTcgMCAxMTUuNSAtMjAuNXQxMDggLTU3dDgwLjUgLTk0dDMxIC0xMjQuNXEwIC01MSAtMTUuNSAtOTYuNXQtMzggLTc0LjV0LTQ1IC01MC41dC0zOC41IC0zMC41ek00MDAgNzAwaDEzOXE3OCAwIDEzMC41IDQ4LjV0NTIuNSAxMjIuNXEwIDQxIC04LjUgNzAuNXQtMjkuNSA1NS41dC02Mi41IDM5LjV0LTEwMy41IDEzLjVoLTExOHYtMzUwek00MDAgMjAwaDIxNnE4MCAwIDEyMSA1MC41dDQxIDEzMC41cTAgOTAgLTYyLjUgMTU0LjUgdC0xNTYuNSA2NC41aC0xNTl2LTQwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDQ5OyIgZD0iTTg3NyAxMjAwbDIgLTU3cS04MyAtMTkgLTExNiAtNDUuNXQtNDAgLTY2LjVsLTEzMiAtODM5cS05IC00OSAxMyAtNjl0OTYgLTI2di05N2gtNTAwdjk3cTE4NiAxNiAyMDAgOThsMTczIDgzMnEzIDE3IDMgMzB0LTEuNSAyMi41dC05IDE3LjV0LTEzLjUgMTIuNXQtMjEuNSAxMHQtMjYgOC41dC0zMy41IDEwcS0xMyAzIC0xOSA1djU3aDQyNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDUwOyIgZD0iTTEzMDAgOTAwaC01MHEwIDIxIC00IDM3dC05LjUgMjYuNXQtMTggMTcuNXQtMjIgMTF0LTI4LjUgNS41dC0zMSAydC0zNyAwLjVoLTIwMHYtODUwcTAgLTIyIDI1IC0zNC41dDUwIC0xMy41bDI1IC0ydi0xMDBoLTQwMHYxMDBxNCAwIDExIDAuNXQyNCAzdDMwIDd0MjQgMTV0MTEgMjQuNXY4NTBoLTIwMHEtMjUgMCAtMzcgLTAuNXQtMzEgLTJ0LTI4LjUgLTUuNXQtMjIgLTExdC0xOCAtMTcuNXQtOS41IC0yNi41dC00IC0zN2gtNTB2MzAwIGgxMDAwdi0zMDB6TTE3NSAxMDAwaC03NXYtODAwaDc1bC0xMjUgLTE2N2wtMTI1IDE2N2g3NXY4MDBoLTc1bDEyNSAxNjd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1MTsiIGQ9Ik0xMTAwIDkwMGgtNTBxMCAyMSAtNCAzN3QtOS41IDI2LjV0LTE4IDE3LjV0LTIyIDExdC0yOC41IDUuNXQtMzEgMnQtMzcgMC41aC0yMDB2LTY1MHEwIC0yMiAyNSAtMzQuNXQ1MCAtMTMuNWwyNSAtMnYtMTAwaC00MDB2MTAwcTQgMCAxMSAwLjV0MjQgM3QzMCA3dDI0IDE1dDExIDI0LjV2NjUwaC0yMDBxLTI1IDAgLTM3IC0wLjV0LTMxIC0ydC0yOC41IC01LjV0LTIyIC0xMXQtMTggLTE3LjV0LTkuNSAtMjYuNXQtNCAtMzdoLTUwdjMwMCBoMTAwMHYtMzAwek0xMTY3IDUwbC0xNjcgLTEyNXY3NWgtODAwdi03NWwtMTY3IDEyNWwxNjcgMTI1di03NWg4MDB2NzV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1MjsiIGQ9Ik01MCAxMTAwaDYwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC02MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDgwMGgxMDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA1MDBoODAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTgwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgMjAwaDExMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTEwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1MzsiIGQ9Ik0yNTAgMTEwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNzAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA4MDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDAgcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMjUwIDUwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNzAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCAyMDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwIHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDU0OyIgZD0iTTUwMCA5NTB2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWg2MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNjAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXpNMTAwIDY1MHYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDEwMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41ek0zMDAgMzUwdjEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoODAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTgwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV6TTAgNTB2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWgxMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDAgcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDU1OyIgZD0iTTUwIDExMDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA4MDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDAgcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgNTAwaDExMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgMjAwaDExMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTEwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1NjsiIGQ9Ik01MCAxMTAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTM1MCAxMTAwaDgwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC04MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCA4MDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMzUwIDgwMGg4MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtODAwIHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgNTAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTM1MCA1MDBoODAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDAgcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC04MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDIwMGgxMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0zNTAgMjAwaDgwMCBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtODAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNTc7IiBkPSJNNDAwIDBoLTEwMHYxMTAwaDEwMHYtMTEwMHpNNTUwIDExMDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTUwIDgwMGg1MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTAwIHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMjY3IDU1MGwtMTY3IC0xMjV2NzVoLTIwMHYxMDBoMjAwdjc1ek01NTAgNTAwaDMwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0zMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTU1MCAyMDBoNjAwIHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC02MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1ODsiIGQ9Ik01MCAxMTAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTkwMCAwaC0xMDB2MTEwMGgxMDB2LTExMDB6TTUwIDgwMGg1MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTAwIHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMTEwMCA2MDBoMjAwdi0xMDBoLTIwMHYtNzVsLTE2NyAxMjVsMTY3IDEyNXYtNzV6TTUwIDUwMGgzMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMzAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek01MCAyMDBoNjAwIHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC02MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA1OTsiIGQ9Ik03NSAxMDAwaDc1MHEzMSAwIDUzIC0yMnQyMiAtNTN2LTY1MHEwIC0zMSAtMjIgLTUzdC01MyAtMjJoLTc1MHEtMzEgMCAtNTMgMjJ0LTIyIDUzdjY1MHEwIDMxIDIyIDUzdDUzIDIyek0xMjAwIDMwMGwtMzAwIDMwMGwzMDAgMzAwdi02MDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA2MDsiIGQ9Ik00NCAxMTAwaDExMTJxMTggMCAzMSAtMTN0MTMgLTMxdi0xMDEycTAgLTE4IC0xMyAtMzF0LTMxIC0xM2gtMTExMnEtMTggMCAtMzEgMTN0LTEzIDMxdjEwMTJxMCAxOCAxMyAzMXQzMSAxM3pNMTAwIDEwMDB2LTczN2wyNDcgMTgybDI5OCAtMTMxbC03NCAxNTZsMjkzIDMxOGwyMzYgLTI4OHY1MDBoLTEwMDB6TTM0MiA4ODRxNTYgMCA5NSAtMzl0MzkgLTk0LjV0LTM5IC05NXQtOTUgLTM5LjV0LTk1IDM5LjV0LTM5IDk1dDM5IDk0LjUgdDk1IDM5eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNjI7IiBkPSJNNjQ4IDExNjlxMTE3IDAgMjE2IC02MHQxNTYuNSAtMTYxdDU3LjUgLTIxOHEwIC0xMTUgLTcwIC0yNThxLTY5IC0xMDkgLTE1OCAtMjI1LjV0LTE0MyAtMTc5LjVsLTU0IC02MnEtOSA4IC0yNS41IDI0LjV0LTYzLjUgNjcuNXQtOTEgMTAzdC05OC41IDEyOHQtOTUuNSAxNDhxLTYwIDEzMiAtNjAgMjQ5cTAgODggMzQgMTY5LjV0OTEuNSAxNDJ0MTM3IDk2LjV0MTY2LjUgMzZ6TTY1Mi41IDk3NHEtOTEuNSAwIC0xNTYuNSAtNjUgdC02NSAtMTU3dDY1IC0xNTYuNXQxNTYuNSAtNjQuNXQxNTYuNSA2NC41dDY1IDE1Ni41dC02NSAxNTd0LTE1Ni41IDY1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNjM7IiBkPSJNNjAwIDExNzdxMTE3IDAgMjI0IC00NS41dDE4NC41IC0xMjN0MTIzIC0xODQuNXQ0NS41IC0yMjR0LTQ1LjUgLTIyNHQtMTIzIC0xODQuNXQtMTg0LjUgLTEyM3QtMjI0IC00NS41dC0yMjQgNDUuNXQtMTg0LjUgMTIzdC0xMjMgMTg0LjV0LTQ1LjUgMjI0dDQ1LjUgMjI0dDEyMyAxODQuNXQxODQuNSAxMjN0MjI0IDQ1LjV6TTYwMCAxNzN2ODU0cS0xMTYgMCAtMjE0LjUgLTU3dC0xNTUuNSAtMTU1LjV0LTU3IC0yMTQuNXQ1NyAtMjE0LjUgdDE1NS41IC0xNTUuNXQyMTQuNSAtNTd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA2NDsiIGQ9Ik01NTQgMTI5NXEyMSAtNzIgNTcuNSAtMTQzLjV0NzYgLTEzMHQ4MyAtMTE4dDgyLjUgLTExN3Q3MCAtMTE2dDQ5LjUgLTEyNnQxOC41IC0xMzYuNXEwIC03MSAtMjUuNSAtMTM1dC02OC41IC0xMTF0LTk5IC04MnQtMTE4LjUgLTU0dC0xMjUuNSAtMjNxLTg0IDUgLTE2MS41IDM0dC0xMzkuNSA3OC41dC05OSAxMjV0LTM3IDE2NC41cTAgNjkgMTggMTM2LjV0NDkuNSAxMjYuNXQ2OS41IDExNi41dDgxLjUgMTE3LjV0ODMuNSAxMTkgdDc2LjUgMTMxdDU4LjUgMTQzek0zNDQgNzEwcS0yMyAtMzMgLTQzLjUgLTcwLjV0LTQwLjUgLTEwMi41dC0xNyAtMTIzcTEgLTM3IDE0LjUgLTY5LjV0MzAgLTUydDQxIC0zN3QzOC41IC0yNC41dDMzIC0xNXEyMSAtNyAzMiAtMXQxMyAyMmw2IDM0cTIgMTAgLTIuNSAyMnQtMTMuNSAxOXEtNSA0IC0xNCAxMnQtMjkuNSA0MC41dC0zMi41IDczLjVxLTI2IDg5IDYgMjcxcTIgMTEgLTYgMTFxLTggMSAtMTUgLTEweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNjU7IiBkPSJNMTAwMCAxMDEzbDEwOCAxMTVxMiAxIDUgMnQxMyAydDIwLjUgLTF0MjUgLTkuNXQyOC41IC0yMS41cTIyIC0yMiAyNyAtNDN0MCAtMzJsLTYgLTEwbC0xMDggLTExNXpNMzUwIDExMDBoNDAwcTUwIDAgMTA1IC0xM2wtMTg3IC0xODdoLTM2OHEtNDEgMCAtNzAuNSAtMjkuNXQtMjkuNSAtNzAuNXYtNTAwcTAgLTQxIDI5LjUgLTcwLjV0NzAuNSAtMjkuNWg1MDBxNDEgMCA3MC41IDI5LjV0MjkuNSA3MC41djE4MmwyMDAgMjAwdi0zMzIgcTAgLTE2NSAtOTMuNSAtMjU3LjV0LTI1Ni41IC05Mi41aC00MDBxLTE2NSAwIC0yNTcuNSA5Mi41dC05Mi41IDI1Ny41djQwMHEwIDE2NSA5Mi41IDI1Ny41dDI1Ny41IDkyLjV6TTEwMDkgODAzbC0zNjIgLTM2MmwtMTYxIC01MGw1NSAxNzBsMzU1IDM1NXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDY2OyIgZD0iTTM1MCAxMTAwaDM2MXEtMTY0IC0xNDYgLTIxNiAtMjAwaC0xOTVxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoNTAwcTQxIDAgNzAuNSAyOS41dDI5LjUgNzAuNWwyMDAgMTUzdi0xMDNxMCAtMTY1IC05Mi41IC0yNTcuNXQtMjU3LjUgLTkyLjVoLTQwMHEtMTY1IDAgLTI1Ny41IDkyLjV0LTkyLjUgMjU3LjV2NDAwcTAgMTY1IDkyLjUgMjU3LjV0MjU3LjUgOTIuNXogTTgyNCAxMDczbDMzOSAtMzAxcTggLTcgOCAtMTcuNXQtOCAtMTcuNWwtMzQwIC0zMDZxLTcgLTYgLTEyLjUgLTR0LTYuNSAxMXYyMDNxLTI2IDEgLTU0LjUgMHQtNzguNSAtNy41dC05MiAtMTcuNXQtODYgLTM1dC03MCAtNTdxMTAgNTkgMzMgMTA4dDUxLjUgODEuNXQ2NSA1OC41dDY4LjUgNDAuNXQ2NyAyNC41dDU2IDEzLjV0NDAgNC41djIxMHExIDEwIDYuNSAxMi41dDEzLjUgLTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDY3OyIgZD0iTTM1MCAxMTAwaDM1MHE2MCAwIDEyNyAtMjNsLTE3OCAtMTc3aC0zNDlxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoNTAwcTQxIDAgNzAuNSAyOS41dDI5LjUgNzAuNXY2OWwyMDAgMjAwdi0yMTlxMCAtMTY1IC05Mi41IC0yNTcuNXQtMjU3LjUgLTkyLjVoLTQwMHEtMTY1IDAgLTI1Ny41IDkyLjV0LTkyLjUgMjU3LjV2NDAwcTAgMTY1IDkyLjUgMjU3LjV0MjU3LjUgOTIuNXogTTY0MyA2MzlsMzk1IDM5NXE3IDcgMTcuNSA3dDE3LjUgLTdsMTAxIC0xMDFxNyAtNyA3IC0xNy41dC03IC0xNy41bC01MzEgLTUzMnEtNyAtNyAtMTcuNSAtN3QtMTcuNSA3bC0yNDggMjQ4cS03IDcgLTcgMTcuNXQ3IDE3LjVsMTAxIDEwMXE3IDcgMTcuNSA3dDE3LjUgLTdsMTExIC0xMTFxOCAtNyAxOCAtN3QxOCA3eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNjg7IiBkPSJNMzE4IDkxOGwyNjQgMjY0cTggOCAxOCA4dDE4IC04bDI2MCAtMjY0cTcgLTggNC41IC0xM3QtMTIuNSAtNWgtMTcwdi0yMDBoMjAwdjE3M3EwIDEwIDUgMTJ0MTMgLTVsMjY0IC0yNjBxOCAtNyA4IC0xNy41dC04IC0xNy41bC0yNjQgLTI2NXEtOCAtNyAtMTMgLTV0LTUgMTJ2MTczaC0yMDB2LTIwMGgxNzBxMTAgMCAxMi41IC01dC00LjUgLTEzbC0yNjAgLTI2NHEtOCAtOCAtMTggLTh0LTE4IDhsLTI2NCAyNjRxLTggOCAtNS41IDEzIHQxMi41IDVoMTc1djIwMGgtMjAwdi0xNzNxMCAtMTAgLTUgLTEydC0xMyA1bC0yNjQgMjY1cS04IDcgLTggMTcuNXQ4IDE3LjVsMjY0IDI2MHE4IDcgMTMgNXQ1IC0xMnYtMTczaDIwMHYyMDBoLTE3NXEtMTAgMCAtMTIuNSA1dDUuNSAxM3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDY5OyIgZD0iTTI1MCAxMTAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDM4bDQ2NCA0NTNxMTUgMTQgMjUuNSAxMHQxMC41IC0yNXYtMTAwMHEwIC0yMSAtMTAuNSAtMjV0LTI1LjUgMTBsLTQ2NCA0NTN2LTQzOHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDcwOyIgZD0iTTUwIDExMDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di00MzhsNDY0IDQ1M3ExNSAxNCAyNS41IDEwdDEwLjUgLTI1di00MzhsNDY0IDQ1M3ExNSAxNCAyNS41IDEwdDEwLjUgLTI1di0xMDAwcTAgLTIxIC0xMC41IC0yNXQtMjUuNSAxMGwtNDY0IDQ1M3YtNDM4cTAgLTIxIC0xMC41IC0yNXQtMjUuNSAxMGwtNDY0IDQ1M3YtNDM4cTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNSB0LTE0LjUgMzUuNXYxMDAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDcxOyIgZD0iTTEyMDAgMTA1MHYtMTAwMHEwIC0yMSAtMTAuNSAtMjV0LTI1LjUgMTBsLTQ2NCA0NTN2LTQzOHEwIC0yMSAtMTAuNSAtMjV0LTI1LjUgMTBsLTQ5MiA0ODBxLTE1IDE0IC0xNSAzNXQxNSAzNWw0OTIgNDgwcTE1IDE0IDI1LjUgMTB0MTAuNSAtMjV2LTQzOGw0NjQgNDUzcTE1IDE0IDI1LjUgMTB0MTAuNSAtMjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA3MjsiIGQ9Ik0yNDMgMTA3NGw4MTQgLTQ5OHExOCAtMTEgMTggLTI2dC0xOCAtMjZsLTgxNCAtNDk4cS0xOCAtMTEgLTMwLjUgLTR0LTEyLjUgMjh2MTAwMHEwIDIxIDEyLjUgMjh0MzAuNSAtNHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDczOyIgZD0iTTI1MCAxMDAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtODAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djgwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTY1MCAxMDAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtODAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djgwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNzQ7IiBkPSJNMTEwMCA5NTB2LTgwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtODAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY4MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDgwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDc1OyIgZD0iTTUwMCA2MTJ2NDM4cTAgMjEgMTAuNSAyNXQyNS41IC0xMGw0OTIgLTQ4MHExNSAtMTQgMTUgLTM1dC0xNSAtMzVsLTQ5MiAtNDgwcS0xNSAtMTQgLTI1LjUgLTEwdC0xMC41IDI1djQzOGwtNDY0IC00NTNxLTE1IC0xNCAtMjUuNSAtMTB0LTEwLjUgMjV2MTAwMHEwIDIxIDEwLjUgMjV0MjUuNSAtMTB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA3NjsiIGQ9Ik0xMDQ4IDExMDJsMTAwIDFxMjAgMCAzNSAtMTQuNXQxNSAtMzUuNWw1IC0xMDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41bC0xMDAgLTFxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41bC0yIDQzN2wtNDYzIC00NTRxLTE0IC0xNSAtMjQuNSAtMTAuNXQtMTAuNSAyNS41bC0yIDQzN2wtNDYyIC00NTVxLTE1IC0xNCAtMjUuNSAtOS41dC0xMC41IDI0LjVsLTUgMTAwMHEwIDIxIDEwLjUgMjUuNXQyNS41IC0xMC41bDQ2NiAtNDUwIGwtMiA0MzhxMCAyMCAxMC41IDI0LjV0MjUuNSAtOS41bDQ2NiAtNDUxbC0yIDQzOHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA3NzsiIGQ9Ik04NTAgMTEwMGgxMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NDM4bC00NjQgLTQ1M3EtMTUgLTE0IC0yNS41IC0xMHQtMTAuNSAyNXYxMDAwcTAgMjEgMTAuNSAyNXQyNS41IC0xMGw0NjQgLTQ1M3Y0MzhxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwNzg7IiBkPSJNNjg2IDEwODFsNTAxIC01NDBxMTUgLTE1IDEwLjUgLTI2dC0yNi41IC0xMWgtMTA0MnEtMjIgMCAtMjYuNSAxMXQxMC41IDI2bDUwMSA1NDBxMTUgMTUgMzYgMTV0MzYgLTE1ek0xNTAgNDAwaDEwMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDc5OyIgZD0iTTg4NSA5MDBsLTM1MiAtMzUzbDM1MiAtMzUzbC0xOTcgLTE5OGwtNTUyIDU1Mmw1NTIgNTUweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwODA7IiBkPSJNMTA2NCA1NDdsLTU1MSAtNTUxbC0xOTggMTk4bDM1MyAzNTNsLTM1MyAzNTNsMTk4IDE5OHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDgxOyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02NTAgOTAwaC0xMDBxLTIxIDAgLTM1LjUgLTE0LjV0LTE0LjUgLTM1LjV2LTE1MGgtMTUwIHEtMjEgMCAtMzUuNSAtMTQuNXQtMTQuNSAtMzUuNXYtMTAwcTAgLTIxIDE0LjUgLTM1LjV0MzUuNSAtMTQuNWgxNTB2LTE1MHEwIC0yMSAxNC41IC0zNS41dDM1LjUgLTE0LjVoMTAwcTIxIDAgMzUuNSAxNC41dDE0LjUgMzUuNXYxNTBoMTUwcTIxIDAgMzUuNSAxNC41dDE0LjUgMzUuNXYxMDBxMCAyMSAtMTQuNSAzNS41dC0zNS41IDE0LjVoLTE1MHYxNTBxMCAyMSAtMTQuNSAzNS41dC0zNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA4MjsiIGQ9Ik02MDAgMTE3N3ExMTcgMCAyMjQgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNHQtNDUuNSAtMjI0dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjQgLTQ1LjV0LTIyNCA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjR0NDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXpNODUwIDcwMGgtNTAwcS0yMSAwIC0zNS41IC0xNC41dC0xNC41IC0zNS41di0xMDBxMCAtMjEgMTQuNSAtMzUuNSB0MzUuNSAtMTQuNWg1MDBxMjEgMCAzNS41IDE0LjV0MTQuNSAzNS41djEwMHEwIDIxIC0xNC41IDM1LjV0LTM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDgzOyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek03NDEuNSA5MTNxLTEyLjUgMCAtMjEuNSAtOWwtMTIwIC0xMjBsLTEyMCAxMjBxLTkgOSAtMjEuNSA5IHQtMjEuNSAtOWwtMTQxIC0xNDFxLTkgLTkgLTkgLTIxLjV0OSAtMjEuNWwxMjAgLTEyMGwtMTIwIC0xMjBxLTkgLTkgLTkgLTIxLjV0OSAtMjEuNWwxNDEgLTE0MXE5IC05IDIxLjUgLTl0MjEuNSA5bDEyMCAxMjBsMTIwIC0xMjBxOSAtOSAyMS41IC05dDIxLjUgOWwxNDEgMTQxcTkgOSA5IDIxLjV0LTkgMjEuNWwtMTIwIDEyMGwxMjAgMTIwcTkgOSA5IDIxLjV0LTkgMjEuNWwtMTQxIDE0MXEtOSA5IC0yMS41IDl6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA4NDsiIGQ9Ik02MDAgMTE3N3ExMTcgMCAyMjQgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNHQtNDUuNSAtMjI0dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjQgLTQ1LjV0LTIyNCA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjR0NDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXpNNTQ2IDYyM2wtODQgODVxLTcgNyAtMTcuNSA3dC0xOC41IC03bC0xMzkgLTEzOXEtNyAtOCAtNyAtMTh0NyAtMTggbDI0MiAtMjQxcTcgLTggMTcuNSAtOHQxNy41IDhsMzc1IDM3NXE3IDcgNyAxNy41dC03IDE4LjVsLTEzOSAxMzlxLTcgNyAtMTcuNSA3dC0xNy41IC03eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwODU7IiBkPSJNNjAwIDExNzdxMTE3IDAgMjI0IC00NS41dDE4NC41IC0xMjN0MTIzIC0xODQuNXQ0NS41IC0yMjR0LTQ1LjUgLTIyNHQtMTIzIC0xODQuNXQtMTg0LjUgLTEyM3QtMjI0IC00NS41dC0yMjQgNDUuNXQtMTg0LjUgMTIzdC0xMjMgMTg0LjV0LTQ1LjUgMjI0dDQ1LjUgMjI0dDEyMyAxODQuNXQxODQuNSAxMjN0MjI0IDQ1LjV6TTU4OCA5NDFxLTI5IDAgLTU5IC01LjV0LTYzIC0yMC41dC01OCAtMzguNXQtNDEuNSAtNjN0LTE2LjUgLTg5LjUgcTAgLTI1IDIwIC0yNWgxMzFxMzAgLTUgMzUgMTFxNiAyMCAyMC41IDI4dDQ1LjUgOHEyMCAwIDMxLjUgLTEwLjV0MTEuNSAtMjguNXEwIC0yMyAtNyAtMzR0LTI2IC0xOHEtMSAwIC0xMy41IC00dC0xOS41IC03LjV0LTIwIC0xMC41dC0yMiAtMTd0LTE4LjUgLTI0dC0xNS41IC0zNXQtOCAtNDZxLTEgLTggNS41IC0xNi41dDIwLjUgLTguNWgxNzNxNyAwIDIyIDh0MzUgMjh0MzcuNSA0OHQyOS41IDc0dDEyIDEwMHEwIDQ3IC0xNyA4MyB0LTQyLjUgNTd0LTU5LjUgMzQuNXQtNjQgMTh0LTU5IDQuNXpNNjc1IDQwMGgtMTUwcS0xMCAwIC0xNy41IC03LjV0LTcuNSAtMTcuNXYtMTUwcTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoMTUwcTEwIDAgMTcuNSA3LjV0Ny41IDE3LjV2MTUwcTAgMTAgLTcuNSAxNy41dC0xNy41IDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDg2OyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02NzUgMTAwMGgtMTUwcS0xMCAwIC0xNy41IC03LjV0LTcuNSAtMTcuNXYtMTUwcTAgLTEwIDcuNSAtMTcuNSB0MTcuNSAtNy41aDE1MHExMCAwIDE3LjUgNy41dDcuNSAxNy41djE1MHEwIDEwIC03LjUgMTcuNXQtMTcuNSA3LjV6TTY3NSA3MDBoLTI1MHEtMTAgMCAtMTcuNSAtNy41dC03LjUgLTE3LjV2LTUwcTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoNzV2LTIwMGgtNzVxLTEwIDAgLTE3LjUgLTcuNXQtNy41IC0xNy41di01MHEwIC0xMCA3LjUgLTE3LjV0MTcuNSAtNy41aDM1MHExMCAwIDE3LjUgNy41dDcuNSAxNy41djUwcTAgMTAgLTcuNSAxNy41IHQtMTcuNSA3LjVoLTc1djI3NXEwIDEwIC03LjUgMTcuNXQtMTcuNSA3LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA4NzsiIGQ9Ik01MjUgMTIwMGgxNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xOTRxMTAzIC0yNyAxNzguNSAtMTAyLjV0MTAyLjUgLTE3OC41aDE5NHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE5NHEtMjcgLTEwMyAtMTAyLjUgLTE3OC41dC0xNzguNSAtMTAyLjV2LTE5NHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE5NCBxLTEwMyAyNyAtMTc4LjUgMTAyLjV0LTEwMi41IDE3OC41aC0xOTRxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgxOTRxMjcgMTAzIDEwMi41IDE3OC41dDE3OC41IDEwMi41djE5NHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek03MDAgODkzdi0xNjhxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0xNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNjhxLTY4IC0yMyAtMTE5IC03NCB0LTc0IC0xMTloMTY4cTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMTY4cTIzIC02OCA3NCAtMTE5dDExOSAtNzR2MTY4cTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoMTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTY4cTY4IDIzIDExOSA3NHQ3NCAxMTloLTE2OHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41aDE2OCBxLTIzIDY4IC03NCAxMTl0LTExOSA3NHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDg4OyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02MDAgMTAyN3EtMTE2IDAgLTIxNC41IC01N3QtMTU1LjUgLTE1NS41dC01NyAtMjE0LjV0NTcgLTIxNC41IHQxNTUuNSAtMTU1LjV0MjE0LjUgLTU3dDIxNC41IDU3dDE1NS41IDE1NS41dDU3IDIxNC41dC01NyAyMTQuNXQtMTU1LjUgMTU1LjV0LTIxNC41IDU3ek03NTkgODIzbDY0IC02NHE3IC03IDcgLTE3LjV0LTcgLTE3LjVsLTEyNCAtMTI0bDEyNCAtMTI0cTcgLTcgNyAtMTcuNXQtNyAtMTcuNWwtNjQgLTY0cS03IC03IC0xNy41IC03dC0xNy41IDdsLTEyNCAxMjRsLTEyNCAtMTI0cS03IC03IC0xNy41IC03dC0xNy41IDdsLTY0IDY0IHEtNyA3IC03IDE3LjV0NyAxNy41bDEyNCAxMjRsLTEyNCAxMjRxLTcgNyAtNyAxNy41dDcgMTcuNWw2NCA2NHE3IDcgMTcuNSA3dDE3LjUgLTdsMTI0IC0xMjRsMTI0IDEyNHE3IDcgMTcuNSA3dDE3LjUgLTd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA4OTsiIGQ9Ik02MDAgMTE3N3ExMTcgMCAyMjQgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNHQtNDUuNSAtMjI0dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjQgLTQ1LjV0LTIyNCA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjR0NDUuNSAyMjR0MTIzIDE4NC41dDE4NC41IDEyM3QyMjQgNDUuNXpNNjAwIDEwMjdxLTExNiAwIC0yMTQuNSAtNTd0LTE1NS41IC0xNTUuNXQtNTcgLTIxNC41dDU3IC0yMTQuNSB0MTU1LjUgLTE1NS41dDIxNC41IC01N3QyMTQuNSA1N3QxNTUuNSAxNTUuNXQ1NyAyMTQuNXQtNTcgMjE0LjV0LTE1NS41IDE1NS41dC0yMTQuNSA1N3pNNzgyIDc4OGwxMDYgLTEwNnE3IC03IDcgLTE3LjV0LTcgLTE3LjVsLTMyMCAtMzIxcS04IC03IC0xOCAtN3QtMTggN2wtMjAyIDIwM3EtOCA3IC04IDE3LjV0OCAxNy41bDEwNiAxMDZxNyA4IDE3LjUgOHQxNy41IC04bDc5IC03OWwxOTcgMTk3cTcgNyAxNy41IDd0MTcuNSAtN3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDkwOyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek02MDAgMTAyN3EtMTE2IDAgLTIxNC41IC01N3QtMTU1LjUgLTE1NS41dC01NyAtMjE0LjVxMCAtMTIwIDY1IC0yMjUgbDU4NyA1ODdxLTEwNSA2NSAtMjI1IDY1ek05NjUgODE5bC01ODQgLTU4NHExMDQgLTYyIDIxOSAtNjJxMTE2IDAgMjE0LjUgNTd0MTU1LjUgMTU1LjV0NTcgMjE0LjVxMCAxMTUgLTYyIDIxOXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDkxOyIgZD0iTTM5IDU4Mmw1MjIgNDI3cTE2IDEzIDI3LjUgOHQxMS41IC0yNnYtMjkxaDU1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC01NTB2LTI5MXEwIC0yMSAtMTEuNSAtMjZ0LTI3LjUgOGwtNTIyIDQyN3EtMTYgMTMgLTE2IDMydDE2IDMyeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUwOTI7IiBkPSJNNjM5IDEwMDlsNTIyIC00MjdxMTYgLTEzIDE2IC0zMnQtMTYgLTMybC01MjIgLTQyN3EtMTYgLTEzIC0yNy41IC04dC0xMS41IDI2djI5MWgtNTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYyMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDU1MHYyOTFxMCAyMSAxMS41IDI2dDI3LjUgLTh6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA5MzsiIGQ9Ik02ODIgMTE2MWw0MjcgLTUyMnExMyAtMTYgOCAtMjcuNXQtMjYgLTExLjVoLTI5MXYtNTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0yMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djU1MGgtMjkxcS0yMSAwIC0yNiAxMS41dDggMjcuNWw0MjcgNTIycTEzIDE2IDMyIDE2dDMyIC0xNnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDk0OyIgZD0iTTU1MCAxMjAwaDIwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTUwaDI5MXEyMSAwIDI2IC0xMS41dC04IC0yNy41bC00MjcgLTUyMnEtMTMgLTE2IC0zMiAtMTZ0LTMyIDE2bC00MjcgNTIycS0xMyAxNiAtOCAyNy41dDI2IDExLjVoMjkxdjU1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTA5NTsiIGQ9Ik02MzkgMTEwOWw1MjIgLTQyN3ExNiAtMTMgMTYgLTMydC0xNiAtMzJsLTUyMiAtNDI3cS0xNiAtMTMgLTI3LjUgLTh0LTExLjUgMjZ2MjkxcS05NCAtMiAtMTgyIC0yMHQtMTcwLjUgLTUydC0xNDcgLTkyLjV0LTEwMC41IC0xMzUuNXE1IDEwNSAyNyAxOTMuNXQ2Ny41IDE2N3QxMTMgMTM1dDE2NyA5MS41dDIyNS41IDQydjI2MnEwIDIxIDExLjUgMjZ0MjcuNSAtOHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDk2OyIgZD0iTTg1MCAxMjAwaDMwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMzAwcTAgLTIxIC0xMC41IC0yNXQtMjQuNSAxMGwtOTQgOTRsLTI0OSAtMjQ5cS04IC03IC0xOCAtN3QtMTggN2wtMTA2IDEwNnEtNyA4IC03IDE4dDcgMThsMjQ5IDI0OWwtOTQgOTRxLTE0IDE0IC0xMCAyNC41dDI1IDEwLjV6TTM1MCAwaC0zMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djMwMHEwIDIxIDEwLjUgMjV0MjQuNSAtMTBsOTQgLTk0bDI0OSAyNDkgcTggNyAxOCA3dDE4IC03bDEwNiAtMTA2cTcgLTggNyAtMTh0LTcgLTE4bC0yNDkgLTI0OWw5NCAtOTRxMTQgLTE0IDEwIC0yNC41dC0yNSAtMTAuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMDk3OyIgZD0iTTEwMTQgMTEyMGwxMDYgLTEwNnE3IC04IDcgLTE4dC03IC0xOGwtMjQ5IC0yNDlsOTQgLTk0cTE0IC0xNCAxMCAtMjQuNXQtMjUgLTEwLjVoLTMwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MzAwcTAgMjEgMTAuNSAyNXQyNC41IC0xMGw5NCAtOTRsMjQ5IDI0OXE4IDcgMTggN3QxOCAtN3pNMjUwIDYwMGgzMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTMwMHEwIC0yMSAtMTAuNSAtMjV0LTI0LjUgMTBsLTk0IDk0IGwtMjQ5IC0yNDlxLTggLTcgLTE4IC03dC0xOCA3bC0xMDYgMTA2cS03IDggLTcgMTh0NyAxOGwyNDkgMjQ5bC05NCA5NHEtMTQgMTQgLTEwIDI0LjV0MjUgMTAuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTAxOyIgZD0iTTYwMCAxMTc3cTExNyAwIDIyNCAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI0dC00NS41IC0yMjR0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNCAtNDUuNXQtMjI0IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNHQ0NS41IDIyNHQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNCA0NS41ek03MDQgOTAwaC0yMDhxLTIwIDAgLTMyIC0xNC41dC04IC0zNC41bDU4IC0zMDJxNCAtMjAgMjEuNSAtMzQuNSB0MzcuNSAtMTQuNWg1NHEyMCAwIDM3LjUgMTQuNXQyMS41IDM0LjVsNTggMzAycTQgMjAgLTggMzQuNXQtMzIgMTQuNXpNNjc1IDQwMGgtMTUwcS0xMCAwIC0xNy41IC03LjV0LTcuNSAtMTcuNXYtMTUwcTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoMTUwcTEwIDAgMTcuNSA3LjV0Ny41IDE3LjV2MTUwcTAgMTAgLTcuNSAxNy41dC0xNy41IDcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTAyOyIgZD0iTTI2MCAxMjAwcTkgMCAxOSAtMnQxNSAtNGw1IC0ycTIyIC0xMCA0NCAtMjNsMTk2IC0xMThxMjEgLTEzIDM2IC0yNHEyOSAtMjEgMzcgLTEycTExIDEzIDQ5IDM1bDE5NiAxMThxMjIgMTMgNDUgMjNxMTcgNyAzOCA3cTIzIDAgNDcgLTE2LjV0MzcgLTMzLjVsMTMgLTE2cTE0IC0yMSAxOCAtNDVsMjUgLTEyM2w4IC00NHExIC05IDguNSAtMTQuNXQxNy41IC01LjVoNjFxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di01MCBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC01MHEtMTAgMCAtMTcuNSAtNy41dC03LjUgLTE3LjV2LTE3NWgtNDAwdjMwMGgtMjAwdi0zMDBoLTQwMHYxNzVxMCAxMCAtNy41IDE3LjV0LTE3LjUgNy41aC01MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoNjFxMTEgMCAxOCAzdDcgOHEwIDQgOSA1MmwyNSAxMjhxNSAyNSAxOSA0NXEyIDMgNSA3dDEzLjUgMTV0MjEuNSAxOS41dDI2LjUgMTUuNSB0MjkuNSA3ek05MTUgMTA3OWwtMTY2IC0xNjJxLTcgLTcgLTUgLTEydDEyIC01aDIxOXExMCAwIDE1IDd0MiAxN2wtNTEgMTQ5cS0zIDEwIC0xMSAxMnQtMTUgLTZ6TTQ2MyA5MTdsLTE3NyAxNTdxLTggNyAtMTYgNXQtMTEgLTEybC01MSAtMTQzcS0zIC0xMCAyIC0xN3QxNSAtN2gyMzFxMTEgMCAxMi41IDV0LTUuNSAxMnpNNTAwIDBoLTM3NXEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djM3NWg0MDB2LTQwMHpNMTEwMCA0MDB2LTM3NSBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0zNzV2NDAwaDQwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTAzOyIgZD0iTTExNjUgMTE5MHE4IDMgMjEgLTYuNXQxMyAtMTcuNXEtMiAtMTc4IC0yNC41IC0zMjMuNXQtNTUuNSAtMjQ1LjV0LTg3IC0xNzQuNXQtMTAyLjUgLTExOC41dC0xMTggLTY4LjV0LTExOC41IC0zM3QtMTIwIC00LjV0LTEwNSA5LjV0LTkwIDE2LjVxLTYxIDEyIC03OCAxMXEtNCAxIC0xMi41IDB0LTM0IC0xNC41dC01Mi41IC00MC41bC0xNTMgLTE1M3EtMjYgLTI0IC0zNyAtMTQuNXQtMTEgNDMuNXEwIDY0IDQyIDEwMnE4IDggNTAuNSA0NSB0NjYuNSA1OHExOSAxNyAzNSA0N3QxMyA2MXEtOSA1NSAtMTAgMTAyLjV0NyAxMTF0MzcgMTMwdDc4IDEyOS41cTM5IDUxIDgwIDg4dDg5LjUgNjMuNXQ5NC41IDQ1dDExMy41IDM2dDEyOSAzMXQxNTcuNSAzN3QxODIgNDcuNXpNMTExNiAxMDk4cS04IDkgLTIyLjUgLTN0LTQ1LjUgLTUwcS0zOCAtNDcgLTExOSAtMTAzLjV0LTE0MiAtODkuNWwtNjIgLTMzcS01NiAtMzAgLTEwMiAtNTd0LTEwNCAtNjh0LTEwMi41IC04MC41dC04NS41IC05MSB0LTY0IC0xMDQuNXEtMjQgLTU2IC0zMSAtODZ0MiAtMzJ0MzEuNSAxNy41dDU1LjUgNTkuNXEyNSAzMCA5NCA3NS41dDEyNS41IDc3LjV0MTQ3LjUgODFxNzAgMzcgMTE4LjUgNjl0MTAyIDc5LjV0OTkgMTExdDg2LjUgMTQ4LjVxMjIgNTAgMjQgNjB0LTYgMTl6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEwNDsiIGQ9Ik02NTMgMTIzMXEtMzkgLTY3IC01NC41IC0xMzF0LTEwLjUgLTExNC41dDI0LjUgLTk2LjV0NDcuNSAtODB0NjMuNSAtNjIuNXQ2OC41IC00Ni41dDY1IC0zMHEtNCA3IC0xNy41IDM1dC0xOC41IDM5LjV0LTE3IDM5LjV0LTE3IDQzdC0xMyA0MnQtOS41IDQ0LjV0LTIgNDJ0NCA0M3QxMy41IDM5dDIzIDM4LjVxOTYgLTQyIDE2NSAtMTA3LjV0MTA1IC0xMzh0NTIgLTE1NnQxMyAtMTU5dC0xOSAtMTQ5LjVxLTEzIC01NSAtNDQgLTEwNi41IHQtNjggLTg3dC03OC41IC02NC41dC03Mi41IC00NXQtNTMgLTIycS03MiAtMjIgLTEyNyAtMTFxLTMxIDYgLTEzIDE5cTYgMyAxNyA3cTEzIDUgMzIuNSAyMXQ0MSA0NHQzOC41IDYzLjV0MjEuNSA4MS41dC02LjUgOTQuNXQtNTAgMTA3dC0xMDQgMTE1LjVxMTAgLTEwNCAtMC41IC0xODl0LTM3IC0xNDAuNXQtNjUgLTkzdC04NCAtNTJ0LTkzLjUgLTExdC05NSAyNC41cS04MCAzNiAtMTMxLjUgMTE0dC01My41IDE3MXEtMiAyMyAwIDQ5LjUgdDQuNSA1Mi41dDEzLjUgNTZ0MjcuNSA2MHQ0NiA2NC41dDY5LjUgNjguNXEtOCAtNTMgLTUgLTEwMi41dDE3LjUgLTkwdDM0IC02OC41dDQ0LjUgLTM5dDQ5IC0ycTMxIDEzIDM4LjUgMzZ0LTQuNSA1NXQtMjkgNjQuNXQtMzYgNzV0LTI2IDc1LjVxLTE1IDg1IDIgMTYxLjV0NTMuNSAxMjguNXQ4NS41IDkyLjV0OTMuNSA2MXQ4MS41IDI1LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEwNTsiIGQ9Ik02MDAgMTA5NHE4MiAwIDE2MC41IC0yMi41dDE0MCAtNTl0MTE2LjUgLTgyLjV0OTQuNSAtOTV0NjggLTk1dDQyLjUgLTgyLjV0MTQgLTU3LjV0LTE0IC01Ny41dC00MyAtODIuNXQtNjguNSAtOTV0LTk0LjUgLTk1dC0xMTYuNSAtODIuNXQtMTQwIC01OXQtMTU5LjUgLTIyLjV0LTE1OS41IDIyLjV0LTE0MCA1OXQtMTE2LjUgODIuNXQtOTQuNSA5NXQtNjguNSA5NXQtNDMgODIuNXQtMTQgNTcuNXQxNCA1Ny41dDQyLjUgODIuNXQ2OCA5NSB0OTQuNSA5NXQxMTYuNSA4Mi41dDE0MCA1OXQxNjAuNSAyMi41ek04ODggODI5cS0xNSAxNSAtMTggMTJ0NSAtMjJxMjUgLTU3IDI1IC0xMTlxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4dC0yMTIgODh0LTg4IDIxMnEwIDU5IDIzIDExNHE4IDE5IDQuNSAyMnQtMTcuNSAtMTJxLTcwIC02OSAtMTYwIC0xODRxLTEzIC0xNiAtMTUgLTQwLjV0OSAtNDIuNXEyMiAtMzYgNDcgLTcxdDcwIC04MnQ5Mi41IC04MXQxMTMgLTU4LjV0MTMzLjUgLTI0LjUgdDEzMy41IDI0dDExMyA1OC41dDkyLjUgODEuNXQ3MCA4MS41dDQ3IDcwLjVxMTEgMTggOSA0Mi41dC0xNCA0MS41cS05MCAxMTcgLTE2MyAxODl6TTQ0OCA3MjdsLTM1IC0zNnEtMTUgLTE1IC0xOS41IC0zOC41dDQuNSAtNDEuNXEzNyAtNjggOTMgLTExNnExNiAtMTMgMzguNSAtMTF0MzYuNSAxN2wzNSAzNHExNCAxNSAxMi41IDMzLjV0LTE2LjUgMzMuNXEtNDQgNDQgLTg5IDExN3EtMTEgMTggLTI4IDIwdC0zMiAtMTJ6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEwNjsiIGQ9Ik01OTIgMGgtMTQ4bDMxIDEyMHEtOTEgMjAgLTE3NS41IDY4LjV0LTE0My41IDEwNi41dC0xMDMuNSAxMTl0LTY2LjUgMTEwdC0yMiA3NnEwIDIxIDE0IDU3LjV0NDIuNSA4Mi41dDY4IDk1dDk0LjUgOTV0MTE2LjUgODIuNXQxNDAgNTl0MTYwLjUgMjIuNXE2MSAwIDEyNiAtMTVsMzIgMTIxaDE0OHpNOTQ0IDc3MGw0NyAxODFxMTA4IC04NSAxNzYuNSAtMTkydDY4LjUgLTE1OXEwIC0yNiAtMTkuNSAtNzF0LTU5LjUgLTEwMnQtOTMgLTExMiB0LTEyOSAtMTA0LjV0LTE1OCAtNzUuNWw0NiAxNzNxNzcgNDkgMTM2IDExN3Q5NyAxMzFxMTEgMTggOSA0Mi41dC0xNCA0MS41cS01NCA3MCAtMTA3IDEzMHpNMzEwIDgyNHEtNzAgLTY5IC0xNjAgLTE4NHEtMTMgLTE2IC0xNSAtNDAuNXQ5IC00Mi41cTE4IC0zMCAzOSAtNjB0NTcgLTcwLjV0NzQgLTczdDkwIC02MXQxMDUgLTQxLjVsNDEgMTU0cS0xMDcgMTggLTE3OC41IDEwMS41dC03MS41IDE5My41cTAgNTkgMjMgMTE0cTggMTkgNC41IDIyIHQtMTcuNSAtMTJ6TTQ0OCA3MjdsLTM1IC0zNnEtMTUgLTE1IC0xOS41IC0zOC41dDQuNSAtNDEuNXEzNyAtNjggOTMgLTExNnExNiAtMTMgMzguNSAtMTF0MzYuNSAxN2wxMiAxMWwyMiA4NmwtMyA0cS00NCA0NCAtODkgMTE3cS0xMSAxOCAtMjggMjB0LTMyIC0xMnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTA3OyIgZD0iTS05MCAxMDBsNjQyIDEwNjZxMjAgMzEgNDggMjguNXQ0OCAtMzUuNWw2NDIgLTEwNTZxMjEgLTMyIDcuNSAtNjcuNXQtNTAuNSAtMzUuNWgtMTI5NHEtMzcgMCAtNTAuNSAzNHQ3LjUgNjZ6TTE1NSAyMDBoMzQ1djc1cTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoMTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtNzVoMzQ1bC00NDUgNzIzek00OTYgNzAwaDIwOHEyMCAwIDMyIC0xNC41dDggLTM0LjVsLTU4IC0yNTIgcS00IC0yMCAtMjEuNSAtMzQuNXQtMzcuNSAtMTQuNWgtNTRxLTIwIDAgLTM3LjUgMTQuNXQtMjEuNSAzNC41bC01OCAyNTJxLTQgMjAgOCAzNC41dDMyIDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEwODsiIGQ9Ik02NTAgMTIwMHE2MiAwIDEwNiAtNDR0NDQgLTEwNnYtMzM5bDM2MyAtMzI1cTE1IC0xNCAyNiAtMzguNXQxMSAtNDQuNXYtNDFxMCAtMjAgLTEyIC0yNi41dC0yOSA1LjVsLTM1OSAyNDl2LTI2M3ExMDAgLTkzIDEwMCAtMTEzdi02NHEwIC0yMSAtMTMgLTI5dC0zMiAxbC0yMDUgMTI4bC0yMDUgLTEyOHEtMTkgLTkgLTMyIC0xdC0xMyAyOXY2NHEwIDIwIDEwMCAxMTN2MjYzbC0zNTkgLTI0OXEtMTcgLTEyIC0yOSAtNS41dC0xMiAyNi41djQxIHEwIDIwIDExIDQ0LjV0MjYgMzguNWwzNjMgMzI1djMzOXEwIDYyIDQ0IDEwNnQxMDYgNDR6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEwOTsiIGQ9Ik04NTAgMTIwMGgxMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTUwaDUwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xNTBoLTExMDB2MTUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWg1MHY1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGg1MDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0xMTAwIDgwMHYtNzUwcTAgLTIxIC0xNC41IC0zNS41IHQtMzUuNSAtMTQuNWgtMTAwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NzUwaDExMDB6TTEwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTMwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTUwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTcwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTkwMCA2MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTEwMCA0MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTMwMCA0MDB2LTEwMGgxMDB2MTAwaC0xMDB6TTUwMCA0MDAgdi0xMDBoMTAwdjEwMGgtMTAwek03MDAgNDAwdi0xMDBoMTAwdjEwMGgtMTAwek05MDAgNDAwdi0xMDBoMTAwdjEwMGgtMTAwek0xMDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAwek0zMDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAwek01MDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAwek03MDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAwek05MDAgMjAwdi0xMDBoMTAwdjEwMGgtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTA7IiBkPSJNMTEzNSAxMTY1bDI0OSAtMjMwcTE1IC0xNCAxNSAtMzV0LTE1IC0zNWwtMjQ5IC0yMzBxLTE0IC0xNCAtMjQuNSAtMTB0LTEwLjUgMjV2MTUwaC0xNTlsLTYwMCAtNjAwaC0yOTFxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoMjA5bDYwMCA2MDBoMjQxdjE1MHEwIDIxIDEwLjUgMjV0MjQuNSAtMTB6TTUyMiA4MTlsLTE0MSAtMTQxbC0xMjIgMTIyaC0yMDlxLTIxIDAgLTM1LjUgMTQuNSB0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDI5MXpNMTEzNSA1NjVsMjQ5IC0yMzBxMTUgLTE0IDE1IC0zNXQtMTUgLTM1bC0yNDkgLTIzMHEtMTQgLTE0IC0yNC41IC0xMHQtMTAuNSAyNXYxNTBoLTI0MWwtMTgxIDE4MWwxNDEgMTQxbDEyMiAtMTIyaDE1OXYxNTBxMCAyMSAxMC41IDI1dDI0LjUgLTEweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTE7IiBkPSJNMTAwIDExMDBoMTAwMHE0MSAwIDcwLjUgLTI5LjV0MjkuNSAtNzAuNXYtNjAwcTAgLTQxIC0yOS41IC03MC41dC03MC41IC0yOS41aC01OTZsLTMwNCAtMzAwdjMwMGgtMTAwcS00MSAwIC03MC41IDI5LjV0LTI5LjUgNzAuNXY2MDBxMCA0MSAyOS41IDcwLjV0NzAuNSAyOS41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTI7IiBkPSJNMTUwIDEyMDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yNTBoLTMwMHYyNTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek04NTAgMTIwMGgyMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTI1MGgtMzAwdjI1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTExMDAgODAwdi0zMDBxMCAtNDEgLTMgLTc3LjV0LTE1IC04OS41dC0zMiAtOTZ0LTU4IC04OXQtODkgLTc3dC0xMjkgLTUxdC0xNzQgLTIwdC0xNzQgMjAgdC0xMjkgNTF0LTg5IDc3dC01OCA4OXQtMzIgOTZ0LTE1IDg5LjV0LTMgNzcuNXYzMDBoMzAwdi0yNTB2LTI3di00Mi41dDEuNSAtNDF0NSAtMzh0MTAgLTM1dDE2LjUgLTMwdDI1LjUgLTI0LjV0MzUgLTE5dDQ2LjUgLTEydDYwIC00dDYwIDQuNXQ0Ni41IDEyLjV0MzUgMTkuNXQyNSAyNS41dDE3IDMwLjV0MTAgMzV0NSAzOHQyIDQwLjV0LTAuNSA0MnYyNXYyNTBoMzAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTM7IiBkPSJNMTEwMCA0MTFsLTE5OCAtMTk5bC0zNTMgMzUzbC0zNTMgLTM1M2wtMTk3IDE5OWw1NTEgNTUxeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTQ7IiBkPSJNMTEwMSA3ODlsLTU1MCAtNTUxbC01NTEgNTUxbDE5OCAxOTlsMzUzIC0zNTNsMzUzIDM1M3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTE1OyIgZD0iTTQwNCAxMDAwaDc0NnEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTUxaDE1MHEyMSAwIDI1IC0xMC41dC0xMCAtMjQuNWwtMjMwIC0yNDlxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI0OXEtMTQgMTQgLTEwIDI0LjV0MjUgMTAuNWgxNTB2NDAxaC0zODF6TTEzNSA5ODRsMjMwIC0yNDlxMTQgLTE0IDEwIC0yNC41dC0yNSAtMTAuNWgtMTUwdi00MDBoMzg1bDIxNSAtMjAwaC03NTBxLTIxIDAgLTM1LjUgMTQuNSB0LTE0LjUgMzUuNXY1NTBoLTE1MHEtMjEgMCAtMjUgMTAuNXQxMCAyNC41bDIzMCAyNDlxMTQgMTUgMzUgMTV0MzUgLTE1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTY7IiBkPSJNNTYgMTIwMGg5NHExNyAwIDMxIC0xMXQxOCAtMjdsMzggLTE2Mmg4OTZxMjQgMCAzOSAtMTguNXQxMCAtNDIuNWwtMTAwIC00NzVxLTUgLTIxIC0yNyAtNDIuNXQtNTUgLTIxLjVoLTYzM2w0OCAtMjAwaDUzNXEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTB2LTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41dC0zNS41IDE0LjV0LTE0LjUgMzUuNXY1MGgtMzAwdi01MCBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjV0LTM1LjUgMTQuNXQtMTQuNSAzNS41djUwaC0zMXEtMTggMCAtMzIuNSAxMHQtMjAuNSAxOWwtNSAxMGwtMjAxIDk2MWgtNTRxLTIwIDAgLTM1IDE0LjV0LTE1IDM1LjV0MTUgMzUuNXQzNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTc7IiBkPSJNMTIwMCAxMDAwdi0xMDBoLTEyMDB2MTAwaDIwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjVoMzAwcTQxIDAgNzAuNSAtMjkuNXQyOS41IC03MC41aDUwMHpNMCA4MDBoMTIwMHYtODAwaC0xMjAwdjgwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTE4OyIgZD0iTTIwMCA4MDBsLTIwMCAtNDAwdjYwMGgyMDBxMCA0MSAyOS41IDcwLjV0NzAuNSAyOS41aDMwMHE0MiAwIDcxIC0yOS41dDI5IC03MC41aDUwMHYtMjAwaC0xMDAwek0xNTAwIDcwMGwtMzAwIC03MDBoLTEyMDBsMzAwIDcwMGgxMjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMTk7IiBkPSJNNjM1IDExODRsMjMwIC0yNDlxMTQgLTE0IDEwIC0yNC41dC0yNSAtMTAuNWgtMTUwdi02MDFoMTUwcTIxIDAgMjUgLTEwLjV0LTEwIC0yNC41bC0yMzAgLTI0OXEtMTQgLTE1IC0zNSAtMTV0LTM1IDE1bC0yMzAgMjQ5cS0xNCAxNCAtMTAgMjQuNXQyNSAxMC41aDE1MHY2MDFoLTE1MHEtMjEgMCAtMjUgMTAuNXQxMCAyNC41bDIzMCAyNDlxMTQgMTUgMzUgMTV0MzUgLTE1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMjA7IiBkPSJNOTM2IDg2NGwyNDkgLTIyOXExNCAtMTUgMTQgLTM1LjV0LTE0IC0zNS41bC0yNDkgLTIyOXEtMTUgLTE1IC0yNS41IC0xMC41dC0xMC41IDI0LjV2MTUxaC02MDB2LTE1MXEwIC0yMCAtMTAuNSAtMjQuNXQtMjUuNSAxMC41bC0yNDkgMjI5cS0xNCAxNSAtMTQgMzUuNXQxNCAzNS41bDI0OSAyMjlxMTUgMTUgMjUuNSAxMC41dDEwLjUgLTI1LjV2LTE0OWg2MDB2MTQ5cTAgMjEgMTAuNSAyNS41dDI1LjUgLTEwLjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEyMTsiIGQ9Ik0xMTY5IDQwMGwtMTcyIDczMnEtNSAyMyAtMjMgNDUuNXQtMzggMjIuNWgtNjcycS0yMCAwIC0zOCAtMjB0LTIzIC00MWwtMTcyIC03MzloMTEzOHpNMTEwMCAzMDBoLTEwMDBxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTEwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoMTAwMHE0MSAwIDcwLjUgMjkuNXQyOS41IDcwLjV2MTAwcTAgNDEgLTI5LjUgNzAuNXQtNzAuNSAyOS41ek04MDAgMTAwdjEwMGgxMDB2LTEwMGgtMTAwIHpNMTAwMCAxMDB2MTAwaDEwMHYtMTAwaC0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEyMjsiIGQ9Ik0xMTUwIDExMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTg1MHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNXQtMzUuNSAxNC41dC0xNC41IDM1LjV2ODUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNMTAwMCAyMDBsLTY3NSAyMDBoLTM4bDQ3IC0yNzZxMyAtMTYgLTUuNSAtMjB0LTI5LjUgLTRoLTdoLTg0cS0yMCAwIC0zNC41IDE0dC0xOC41IDM1cS01NSAzMzcgLTU1IDM1MXYyNTB2NnEwIDE2IDEgMjMuNXQ2LjUgMTQgdDE3LjUgNi41aDIwMGw2NzUgMjUwdi04NTB6TTAgNzUwdi0yNTBxLTQgMCAtMTEgMC41dC0yNCA2dC0zMCAxNXQtMjQgMzB0LTExIDQ4LjV2NTBxMCAyNiAxMC41IDQ2dDI1IDMwdDI5IDE2dDI1LjUgN3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTIzOyIgZD0iTTU1MyAxMjAwaDk0cTIwIDAgMjkgLTEwLjV0MyAtMjkuNWwtMTggLTM3cTgzIC0xOSAxNDQgLTgyLjV0NzYgLTE0MC41bDYzIC0zMjdsMTE4IC0xNzNoMTdxMTkgMCAzMyAtMTQuNXQxNCAtMzV0LTEzIC00MC41dC0zMSAtMjdxLTggLTQgLTIzIC05LjV0LTY1IC0xOS41dC0xMDMgLTI1dC0xMzIuNSAtMjB0LTE1OC41IC05cS01NyAwIC0xMTUgNXQtMTA0IDEydC04OC41IDE1LjV0LTczLjUgMTcuNXQtNTQuNSAxNnQtMzUuNSAxMmwtMTEgNCBxLTE4IDggLTMxIDI4dC0xMyA0MC41dDE0IDM1dDMzIDE0LjVoMTdsMTE4IDE3M2w2MyAzMjdxMTUgNzcgNzYgMTQwdDE0NCA4M2wtMTggMzJxLTYgMTkgMy41IDMydDI4LjUgMTN6TTQ5OCAxMTBxNTAgLTYgMTAyIC02cTUzIDAgMTAyIDZxLTEyIC00OSAtMzkuNSAtNzkuNXQtNjIuNSAtMzAuNXQtNjMgMzAuNXQtMzkgNzkuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTI0OyIgZD0iTTgwMCA5NDZsMjI0IDc4bC03OCAtMjI0bDIzNCAtNDVsLTE4MCAtMTU1bDE4MCAtMTU1bC0yMzQgLTQ1bDc4IC0yMjRsLTIyNCA3OGwtNDUgLTIzNGwtMTU1IDE4MGwtMTU1IC0xODBsLTQ1IDIzNGwtMjI0IC03OGw3OCAyMjRsLTIzNCA0NWwxODAgMTU1bC0xODAgMTU1bDIzNCA0NWwtNzggMjI0bDIyNCAtNzhsNDUgMjM0bDE1NSAtMTgwbDE1NSAxODB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEyNTsiIGQ9Ik02NTAgMTIwMGg1MHE0MCAwIDcwIC00MC41dDMwIC04NC41di0xNTBsLTI4IC0xMjVoMzI4cTQwIDAgNzAgLTQwLjV0MzAgLTg0LjV2LTEwMHEwIC00NSAtMjkgLTc0bC0yMzggLTM0NHEtMTYgLTI0IC0zOCAtNDAuNXQtNDUgLTE2LjVoLTI1MHEtNyAwIC00MiAyNXQtNjYgNTBsLTMxIDI1aC02MXEtNDUgMCAtNzIuNSAxOHQtMjcuNSA1N3Y0MDBxMCAzNiAyMCA2M2wxNDUgMTk2bDk2IDE5OHExMyAyOCAzNy41IDQ4dDUxLjUgMjB6IE02NTAgMTEwMGwtMTAwIC0yMTJsLTE1MCAtMjEzdi0zNzVoMTAwbDEzNiAtMTAwaDIxNGwyNTAgMzc1djEyNWgtNDUwbDUwIDIyNXYxNzVoLTUwek01MCA4MDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTI2OyIgZD0iTTYwMCAxMTAwaDI1MHEyMyAwIDQ1IC0xNi41dDM4IC00MC41bDIzOCAtMzQ0cTI5IC0yOSAyOSAtNzR2LTEwMHEwIC00NCAtMzAgLTg0LjV0LTcwIC00MC41aC0zMjhxMjggLTExOCAyOCAtMTI1di0xNTBxMCAtNDQgLTMwIC04NC41dC03MCAtNDAuNWgtNTBxLTI3IDAgLTUxLjUgMjB0LTM3LjUgNDhsLTk2IDE5OGwtMTQ1IDE5NnEtMjAgMjcgLTIwIDYzdjQwMHEwIDM5IDI3LjUgNTd0NzIuNSAxOGg2MXExMjQgMTAwIDEzOSAxMDB6IE01MCAxMDAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djUwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTYzNiAxMDAwbC0xMzYgLTEwMGgtMTAwdi0zNzVsMTUwIC0yMTNsMTAwIC0yMTJoNTB2MTc1bC01MCAyMjVoNDUwdjEyNWwtMjUwIDM3NWgtMjE0eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMjc7IiBkPSJNMzU2IDg3M2wzNjMgMjMwcTMxIDE2IDUzIC02bDExMCAtMTEycTEzIC0xMyAxMy41IC0zMnQtMTEuNSAtMzRsLTg0IC0xMjFoMzAycTg0IDAgMTM4IC0zOHQ1NCAtMTEwdC01NSAtMTExdC0xMzkgLTM5aC0xMDZsLTEzMSAtMzM5cS02IC0yMSAtMTkuNSAtNDF0LTI4LjUgLTIwaC0zNDJxLTcgMCAtOTAgODF0LTgzIDk0djUyNXEwIDE3IDE0IDM1LjV0MjggMjguNXpNNDAwIDc5MnYtNTAzbDEwMCAtODloMjkzbDEzMSAzMzkgcTYgMjEgMTkuNSA0MXQyOC41IDIwaDIwM3EyMSAwIDMwLjUgMjV0MC41IDUwdC0zMSAyNWgtNDU2aC03aC02aC01LjV0LTYgMC41dC01IDEuNXQtNSAydC00IDIuNXQtNCA0dC0yLjUgNC41cS0xMiAyNSA1IDQ3bDE0NiAxODNsLTg2IDgzek01MCA4MDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NTAwIHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEyODsiIGQ9Ik00NzUgMTEwM2wzNjYgLTIzMHEyIC0xIDYgLTMuNXQxNCAtMTAuNXQxOCAtMTYuNXQxNC41IC0yMHQ2LjUgLTIyLjV2LTUyNXEwIC0xMyAtODYgLTk0dC05MyAtODFoLTM0MnEtMTUgMCAtMjguNSAyMHQtMTkuNSA0MWwtMTMxIDMzOWgtMTA2cS04NSAwIC0xMzkuNSAzOXQtNTQuNSAxMTF0NTQgMTEwdDEzOCAzOGgzMDJsLTg1IDEyMXEtMTEgMTUgLTEwLjUgMzR0MTMuNSAzMmwxMTAgMTEycTIyIDIyIDUzIDZ6TTM3MCA5NDVsMTQ2IC0xODMgcTE3IC0yMiA1IC00N3EtMiAtMiAtMy41IC00LjV0LTQgLTR0LTQgLTIuNXQtNSAtMnQtNSAtMS41dC02IC0wLjVoLTZoLTYuNWgtNmgtNDc1di0xMDBoMjIxcTE1IDAgMjkgLTIwdDIwIC00MWwxMzAgLTMzOWgyOTRsMTA2IDg5djUwM2wtMzQyIDIzNnpNMTA1MCA4MDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjUgdjUwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEyOTsiIGQ9Ik01NTAgMTI5NHE3MiAwIDExMSAtNTV0MzkgLTEzOXYtMTA2bDMzOSAtMTMxcTIxIC02IDQxIC0xOS41dDIwIC0yOC41di0zNDJxMCAtNyAtODEgLTkwdC05NCAtODNoLTUyNXEtMTcgMCAtMzUuNSAxNHQtMjguNSAyOGwtOSAxNGwtMjMwIDM2M3EtMTYgMzEgNiA1M2wxMTIgMTEwcTEzIDEzIDMyIDEzLjV0MzQgLTExLjVsMTIxIC04NHYzMDJxMCA4NCAzOCAxMzh0MTEwIDU0ek02MDAgOTcydjIwM3EwIDIxIC0yNSAzMC41dC01MCAwLjUgdC0yNSAtMzF2LTQ1NnYtN3YtNnYtNS41dC0wLjUgLTZ0LTEuNSAtNXQtMiAtNXQtMi41IC00dC00IC00dC00LjUgLTIuNXEtMjUgLTEyIC00NyA1bC0xODMgMTQ2bC04MyAtODZsMjM2IC0zMzloNTAzbDg5IDEwMHYyOTNsLTMzOSAxMzFxLTIxIDYgLTQxIDE5LjV0LTIwIDI4LjV6TTQ1MCAyMDBoNTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTUwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEzMDsiIGQ9Ik0zNTAgMTEwMGg1MDBxMjEgMCAzNS41IDE0LjV0MTQuNSAzNS41djEwMHEwIDIxIC0xNC41IDM1LjV0LTM1LjUgMTQuNWgtNTAwcS0yMSAwIC0zNS41IC0xNC41dC0xNC41IC0zNS41di0xMDBxMCAtMjEgMTQuNSAtMzUuNXQzNS41IC0xNC41ek02MDAgMzA2di0xMDZxMCAtODQgLTM5IC0xMzl0LTExMSAtNTV0LTExMCA1NHQtMzggMTM4djMwMmwtMTIxIC04NHEtMTUgLTEyIC0zNCAtMTEuNXQtMzIgMTMuNWwtMTEyIDExMCBxLTIyIDIyIC02IDUzbDIzMCAzNjNxMSAyIDMuNSA2dDEwLjUgMTMuNXQxNi41IDE3dDIwIDEzLjV0MjIuNSA2aDUyNXExMyAwIDk0IC04M3Q4MSAtOTB2LTM0MnEwIC0xNSAtMjAgLTI4LjV0LTQxIC0xOS41ek0zMDggOTAwbC0yMzYgLTMzOWw4MyAtODZsMTgzIDE0NnEyMiAxNyA0NyA1cTIgLTEgNC41IC0yLjV0NCAtNHQyLjUgLTR0MiAtNXQxLjUgLTV0MC41IC02di01LjV2LTZ2LTd2LTQ1NnEwIC0yMiAyNSAtMzF0NTAgMC41dDI1IDMwLjUgdjIwM3EwIDE1IDIwIDI4LjV0NDEgMTkuNWwzMzkgMTMxdjI5M2wtODkgMTAwaC01MDN6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEzMTsiIGQ9Ik02MDAgMTE3OHExMTggMCAyMjUgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNXQtNDUuNSAtMjI1dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjUgLTQ1LjV0LTIyNSA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjV0NDUuNSAyMjV0MTIzIDE4NC41dDE4NC41IDEyM3QyMjUgNDUuNXpNOTE0IDYzMmwtMjc1IDIyM3EtMTYgMTMgLTI3LjUgOHQtMTEuNSAtMjZ2LTEzN2gtMjc1IHEtMTAgMCAtMTcuNSAtNy41dC03LjUgLTE3LjV2LTE1MHEwIC0xMCA3LjUgLTE3LjV0MTcuNSAtNy41aDI3NXYtMTM3cTAgLTIxIDExLjUgLTI2dDI3LjUgOGwyNzUgMjIzcTE2IDEzIDE2IDMydC0xNiAzMnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTMyOyIgZD0iTTYwMCAxMTc4cTExOCAwIDIyNSAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI1dC00NS41IC0yMjV0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNSAtNDUuNXQtMjI1IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNXQ0NS41IDIyNXQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNSA0NS41ek01NjEgODU1bC0yNzUgLTIyM3EtMTYgLTEzIC0xNiAtMzJ0MTYgLTMybDI3NSAtMjIzcTE2IC0xMyAyNy41IC04IHQxMS41IDI2djEzN2gyNzVxMTAgMCAxNy41IDcuNXQ3LjUgMTcuNXYxNTBxMCAxMCAtNy41IDE3LjV0LTE3LjUgNy41aC0yNzV2MTM3cTAgMjEgLTExLjUgMjZ0LTI3LjUgLTh6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEzMzsiIGQ9Ik02MDAgMTE3OHExMTggMCAyMjUgLTQ1LjV0MTg0LjUgLTEyM3QxMjMgLTE4NC41dDQ1LjUgLTIyNXQtNDUuNSAtMjI1dC0xMjMgLTE4NC41dC0xODQuNSAtMTIzdC0yMjUgLTQ1LjV0LTIyNSA0NS41dC0xODQuNSAxMjN0LTEyMyAxODQuNXQtNDUuNSAyMjV0NDUuNSAyMjV0MTIzIDE4NC41dDE4NC41IDEyM3QyMjUgNDUuNXpNODU1IDYzOWwtMjIzIDI3NXEtMTMgMTYgLTMyIDE2dC0zMiAtMTZsLTIyMyAtMjc1cS0xMyAtMTYgLTggLTI3LjUgdDI2IC0xMS41aDEzN3YtMjc1cTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoMTUwcTEwIDAgMTcuNSA3LjV0Ny41IDE3LjV2Mjc1aDEzN3EyMSAwIDI2IDExLjV0LTggMjcuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTM0OyIgZD0iTTYwMCAxMTc4cTExOCAwIDIyNSAtNDUuNXQxODQuNSAtMTIzdDEyMyAtMTg0LjV0NDUuNSAtMjI1dC00NS41IC0yMjV0LTEyMyAtMTg0LjV0LTE4NC41IC0xMjN0LTIyNSAtNDUuNXQtMjI1IDQ1LjV0LTE4NC41IDEyM3QtMTIzIDE4NC41dC00NS41IDIyNXQ0NS41IDIyNXQxMjMgMTg0LjV0MTg0LjUgMTIzdDIyNSA0NS41ek02NzUgOTAwaC0xNTBxLTEwIDAgLTE3LjUgLTcuNXQtNy41IC0xNy41di0yNzVoLTEzN3EtMjEgMCAtMjYgLTExLjUgdDggLTI3LjVsMjIzIC0yNzVxMTMgLTE2IDMyIC0xNnQzMiAxNmwyMjMgMjc1cTEzIDE2IDggMjcuNXQtMjYgMTEuNWgtMTM3djI3NXEwIDEwIC03LjUgMTcuNXQtMTcuNSA3LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTEzNTsiIGQ9Ik02MDAgMTE3NnExMTYgMCAyMjIuNSAtNDZ0MTg0IC0xMjMuNXQxMjMuNSAtMTg0dDQ2IC0yMjIuNXQtNDYgLTIyMi41dC0xMjMuNSAtMTg0dC0xODQgLTEyMy41dC0yMjIuNSAtNDZ0LTIyMi41IDQ2dC0xODQgMTIzLjV0LTEyMy41IDE4NHQtNDYgMjIyLjV0NDYgMjIyLjV0MTIzLjUgMTg0dDE4NCAxMjMuNXQyMjIuNSA0NnpNNjI3IDExMDFxLTE1IC0xMiAtMzYuNSAtMjAuNXQtMzUuNSAtMTJ0LTQzIC04dC0zOSAtNi41IHEtMTUgLTMgLTQ1LjUgMHQtNDUuNSAtMnEtMjAgLTcgLTUxLjUgLTI2LjV0LTM0LjUgLTM0LjVxLTMgLTExIDYuNSAtMjIuNXQ4LjUgLTE4LjVxLTMgLTM0IC0yNy41IC05MXQtMjkuNSAtNzlxLTkgLTM0IDUgLTkzdDggLTg3cTAgLTkgMTcgLTQ0LjV0MTYgLTU5LjVxMTIgMCAyMyAtNXQyMy41IC0xNXQxOS41IC0xNHExNiAtOCAzMyAtMTV0NDAuNSAtMTV0MzQuNSAtMTJxMjEgLTkgNTIuNSAtMzJ0NjAgLTM4dDU3LjUgLTExIHE3IC0xNSAtMyAtMzR0LTIyLjUgLTQwdC05LjUgLTM4cTEzIC0yMSAyMyAtMzQuNXQyNy41IC0yNy41dDM2LjUgLTE4cTAgLTcgLTMuNSAtMTZ0LTMuNSAtMTR0NSAtMTdxMTA0IC0yIDIyMSAxMTJxMzAgMjkgNDYuNSA0N3QzNC41IDQ5dDIxIDYzcS0xMyA4IC0zNyA4LjV0LTM2IDcuNXEtMTUgNyAtNDkuNSAxNXQtNTEuNSAxOXEtMTggMCAtNDEgLTAuNXQtNDMgLTEuNXQtNDIgLTYuNXQtMzggLTE2LjVxLTUxIC0zNSAtNjYgLTEyIHEtNCAxIC0zLjUgMjUuNXQwLjUgMjUuNXEtNiAxMyAtMjYuNSAxNy41dC0yNC41IDYuNXExIDE1IC0wLjUgMzAuNXQtNyAyOHQtMTguNSAxMS41dC0zMSAtMjFxLTIzIC0yNSAtNDIgNHEtMTkgMjggLTggNThxNiAxNiAyMiAyMnE2IC0xIDI2IC0xLjV0MzMuNSAtNHQxOS41IC0xMy41cTcgLTEyIDE4IC0yNHQyMS41IC0yMC41dDIwIC0xNXQxNS41IC0xMC41bDUgLTNxMiAxMiA3LjUgMzAuNXQ4IDM0LjV0LTAuNSAzMnEtMyAxOCAzLjUgMjkgdDE4IDIyLjV0MTUuNSAyNC41cTYgMTQgMTAuNSAzNXQ4IDMxdDE1LjUgMjIuNXQzNCAyMi41cS02IDE4IDEwIDM2cTggMCAyNCAtMS41dDI0LjUgLTEuNXQyMCA0LjV0MjAuNSAxNS41cS0xMCAyMyAtMzEgNDIuNXQtMzcuNSAyOS41dC00OSAyN3QtNDMuNSAyM3EwIDEgMiA4dDMgMTEuNXQxLjUgMTAuNXQtMSA5LjV0LTQuNSA0LjVxMzEgLTEzIDU4LjUgLTE0LjV0MzguNSAyLjVsMTIgNXE1IDI4IC05LjUgNDZ0LTM2LjUgMjR0LTUwIDE1IHQtNDEgMjBxLTE4IC00IC0zNyAwek02MTMgOTk0cTAgLTE3IDggLTQydDE3IC00NXQ5IC0yM3EtOCAxIC0zOS41IDUuNXQtNTIuNSAxMHQtMzcgMTYuNXEzIDExIDE2IDI5LjV0MTYgMjUuNXExMCAtMTAgMTkgLTEwdDE0IDZ0MTMuNSAxNC41dDE2LjUgMTIuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTM2OyIgZD0iTTc1NiAxMTU3cTE2NCA5MiAzMDYgLTlsLTI1OSAtMTM4bDE0NSAtMjMybDI1MSAxMjZxNiAtODkgLTM0IC0xNTYuNXQtMTE3IC0xMTAuNXEtNjAgLTM0IC0xMjcgLTM5LjV0LTEyNiAxNi41bC01OTYgLTU5NnEtMTUgLTE2IC0zNi41IC0xNnQtMzYuNSAxNmwtMTExIDExMHEtMTUgMTUgLTE1IDM2LjV0MTUgMzcuNWw2MDAgNTk5cS0zNCAxMDEgNS41IDIwMS41dDEzNS41IDE1NC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMzc7IiBob3Jpei1hZHYteD0iMTIyMCIgZD0iTTEwMCAxMTk2aDEwMDBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTEwMHEwIC00MSAtMjkuNSAtNzAuNXQtNzAuNSAtMjkuNWgtMTAwMHEtNDEgMCAtNzAuNSAyOS41dC0yOS41IDcwLjV2MTAwcTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNXpNMTEwMCAxMDk2aC0yMDB2LTEwMGgyMDB2MTAwek0xMDAgNzk2aDEwMDBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTEwMHEwIC00MSAtMjkuNSAtNzAuNXQtNzAuNSAtMjkuNWgtMTAwMCBxLTQxIDAgLTcwLjUgMjkuNXQtMjkuNSA3MC41djEwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjV6TTExMDAgNjk2aC01MDB2LTEwMGg1MDB2MTAwek0xMDAgMzk2aDEwMDBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTEwMHEwIC00MSAtMjkuNSAtNzAuNXQtNzAuNSAtMjkuNWgtMTAwMHEtNDEgMCAtNzAuNSAyOS41dC0yOS41IDcwLjV2MTAwcTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNXpNMTEwMCAyOTZoLTMwMHYtMTAwaDMwMHYxMDB6ICIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxMzg7IiBkPSJNMTUwIDEyMDBoOTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41dC0xNC41IC0zNS41dC0zNS41IC0xNC41aC05MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6TTcwMCA1MDB2LTMwMGwtMjAwIC0yMDB2NTAwbC0zNTAgNTAwaDkwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTM5OyIgZD0iTTUwMCAxMjAwaDIwMHE0MSAwIDcwLjUgLTI5LjV0MjkuNSAtNzAuNXYtMTAwaDMwMHE0MSAwIDcwLjUgLTI5LjV0MjkuNSAtNzAuNXYtNDAwaC01MDB2MTAwaC0yMDB2LTEwMGgtNTAwdjQwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjVoMzAwdjEwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjV6TTUwMCAxMTAwdi0xMDBoMjAwdjEwMGgtMjAwek0xMjAwIDQwMHYtMjAwcTAgLTQxIC0yOS41IC03MC41dC03MC41IC0yOS41aC0xMDAwIHEtNDEgMCAtNzAuNSAyOS41dC0yOS41IDcwLjV2MjAwaDEyMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE0MDsiIGQ9Ik01MCAxMjAwaDMwMHEyMSAwIDI1IC0xMC41dC0xMCAtMjQuNWwtOTQgLTk0bDE5OSAtMTk5cTcgLTggNyAtMTh0LTcgLTE4bC0xMDYgLTEwNnEtOCAtNyAtMTggLTd0LTE4IDdsLTE5OSAxOTlsLTk0IC05NHEtMTQgLTE0IC0yNC41IC0xMHQtMTAuNSAyNXYzMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek04NTAgMTIwMGgzMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTMwMHEwIC0yMSAtMTAuNSAtMjV0LTI0LjUgMTBsLTk0IDk0IGwtMTk5IC0xOTlxLTggLTcgLTE4IC03dC0xOCA3bC0xMDYgMTA2cS03IDggLTcgMTh0NyAxOGwxOTkgMTk5bC05NCA5NHEtMTQgMTQgLTEwIDI0LjV0MjUgMTAuNXpNMzY0IDQ3MGwxMDYgLTEwNnE3IC04IDcgLTE4dC03IC0xOGwtMTk5IC0xOTlsOTQgLTk0cTE0IC0xNCAxMCAtMjQuNXQtMjUgLTEwLjVoLTMwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MzAwcTAgMjEgMTAuNSAyNXQyNC41IC0xMGw5NCAtOTRsMTk5IDE5OSBxOCA3IDE4IDd0MTggLTd6TTEwNzEgMjcxbDk0IDk0cTE0IDE0IDI0LjUgMTB0MTAuNSAtMjV2LTMwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMzAwcS0yMSAwIC0yNSAxMC41dDEwIDI0LjVsOTQgOTRsLTE5OSAxOTlxLTcgOCAtNyAxOHQ3IDE4bDEwNiAxMDZxOCA3IDE4IDd0MTggLTd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE0MTsiIGQ9Ik01OTYgMTE5MnExMjEgMCAyMzEuNSAtNDcuNXQxOTAgLTEyN3QxMjcgLTE5MHQ0Ny41IC0yMzEuNXQtNDcuNSAtMjMxLjV0LTEyNyAtMTkwLjV0LTE5MCAtMTI3dC0yMzEuNSAtNDd0LTIzMS41IDQ3dC0xOTAuNSAxMjd0LTEyNyAxOTAuNXQtNDcgMjMxLjV0NDcgMjMxLjV0MTI3IDE5MHQxOTAuNSAxMjd0MjMxLjUgNDcuNXpNNTk2IDEwMTBxLTExMiAwIC0yMDcuNSAtNTUuNXQtMTUxIC0xNTF0LTU1LjUgLTIwNy41dDU1LjUgLTIwNy41IHQxNTEgLTE1MXQyMDcuNSAtNTUuNXQyMDcuNSA1NS41dDE1MSAxNTF0NTUuNSAyMDcuNXQtNTUuNSAyMDcuNXQtMTUxIDE1MXQtMjA3LjUgNTUuNXpNNDU0LjUgOTA1cTIyLjUgMCAzOC41IC0xNnQxNiAtMzguNXQtMTYgLTM5dC0zOC41IC0xNi41dC0zOC41IDE2LjV0LTE2IDM5dDE2IDM4LjV0MzguNSAxNnpNNzU0LjUgOTA1cTIyLjUgMCAzOC41IC0xNnQxNiAtMzguNXQtMTYgLTM5dC0zOCAtMTYuNXEtMTQgMCAtMjkgMTBsLTU1IC0xNDUgcTE3IC0yMyAxNyAtNTFxMCAtMzYgLTI1LjUgLTYxLjV0LTYxLjUgLTI1LjV0LTYxLjUgMjUuNXQtMjUuNSA2MS41cTAgMzIgMjAuNSA1Ni41dDUxLjUgMjkuNWwxMjIgMTI2bDEgMXEtOSAxNCAtOSAyOHEwIDIzIDE2IDM5dDM4LjUgMTZ6TTM0NS41IDcwOXEyMi41IDAgMzguNSAtMTZ0MTYgLTM4LjV0LTE2IC0zOC41dC0zOC41IC0xNnQtMzguNSAxNnQtMTYgMzguNXQxNiAzOC41dDM4LjUgMTZ6TTg1NC41IDcwOXEyMi41IDAgMzguNSAtMTYgdDE2IC0zOC41dC0xNiAtMzguNXQtMzguNSAtMTZ0LTM4LjUgMTZ0LTE2IDM4LjV0MTYgMzguNXQzOC41IDE2eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNDI7IiBkPSJNNTQ2IDE3M2w0NjkgNDcwcTkxIDkxIDk5IDE5MnE3IDk4IC01MiAxNzUuNXQtMTU0IDk0LjVxLTIyIDQgLTQ3IDRxLTM0IDAgLTY2LjUgLTEwdC01Ni41IC0yM3QtNTUuNSAtMzh0LTQ4IC00MS41dC00OC41IC00Ny41cS0zNzYgLTM3NSAtMzkxIC0zOTBxLTMwIC0yNyAtNDUgLTQxLjV0LTM3LjUgLTQxdC0zMiAtNDYuNXQtMTYgLTQ3LjV0LTEuNSAtNTYuNXE5IC02MiA1My41IC05NXQ5OS41IC0zM3E3NCAwIDEyNSA1MWw1NDggNTQ4IHEzNiAzNiAyMCA3NXEtNyAxNiAtMjEuNSAyNnQtMzIuNSAxMHEtMjYgMCAtNTAgLTIzcS0xMyAtMTIgLTM5IC0zOGwtMzQxIC0zMzhxLTE1IC0xNSAtMzUuNSAtMTUuNXQtMzQuNSAxMy41dC0xNCAzNC41dDE0IDM0LjVxMzI3IDMzMyAzNjEgMzY3cTM1IDM1IDY3LjUgNTEuNXQ3OC41IDE2LjVxMTQgMCAyOSAtMXE0NCAtOCA3NC41IC0zNS41dDQzLjUgLTY4LjVxMTQgLTQ3IDIgLTk2LjV0LTQ3IC04NC41cS0xMiAtMTEgLTMyIC0zMiB0LTc5LjUgLTgxdC0xMTQuNSAtMTE1dC0xMjQuNSAtMTIzLjV0LTEyMyAtMTE5LjV0LTk2LjUgLTg5dC01NyAtNDVxLTU2IC0yNyAtMTIwIC0yN3EtNzAgMCAtMTI5IDMydC05MyA4OXEtNDggNzggLTM1IDE3M3Q4MSAxNjNsNTExIDUxMXE3MSA3MiAxMTEgOTZxOTEgNTUgMTk4IDU1cTgwIDAgMTUyIC0zM3E3OCAtMzYgMTI5LjUgLTEwM3Q2Ni41IC0xNTRxMTcgLTkzIC0xMSAtMTgzLjV0LTk0IC0xNTYuNWwtNDgyIC00NzYgcS0xNSAtMTUgLTM2IC0xNnQtMzcgMTR0LTE3LjUgMzR0MTQuNSAzNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTQzOyIgZD0iTTY0OSA5NDlxNDggNjggMTA5LjUgMTA0dDEyMS41IDM4LjV0MTE4LjUgLTIwdDEwMi41IC02NHQ3MSAtMTAwLjV0MjcgLTEyM3EwIC01NyAtMzMuNSAtMTE3LjV0LTk0IC0xMjQuNXQtMTI2LjUgLTEyNy41dC0xNTAgLTE1Mi41dC0xNDYgLTE3NHEtNjIgODUgLTE0NS41IDE3NHQtMTUwIDE1Mi41dC0xMjYuNSAxMjcuNXQtOTMuNSAxMjQuNXQtMzMuNSAxMTcuNXEwIDY0IDI4IDEyM3Q3MyAxMDAuNXQxMDQgNjR0MTE5IDIwIHQxMjAuNSAtMzguNXQxMDQuNSAtMTA0ek04OTYgOTcycS0zMyAwIC02NC41IC0xOXQtNTYuNSAtNDZ0LTQ3LjUgLTUzLjV0LTQzLjUgLTQ1LjV0LTM3LjUgLTE5dC0zNiAxOXQtNDAgNDUuNXQtNDMgNTMuNXQtNTQgNDZ0LTY1LjUgMTlxLTY3IDAgLTEyMi41IC01NS41dC01NS41IC0xMzIuNXEwIC0yMyAxMy41IC01MXQ0NiAtNjV0NTcuNSAtNjN0NzYgLTc1bDIyIC0yMnExNSAtMTQgNDQgLTQ0dDUwLjUgLTUxdDQ2IC00NHQ0MSAtMzV0MjMgLTEyIHQyMy41IDEydDQyLjUgMzZ0NDYgNDR0NTIuNSA1MnQ0NCA0M3E0IDQgMTIgMTNxNDMgNDEgNjMuNSA2MnQ1MiA1NXQ0NiA1NXQyNiA0NnQxMS41IDQ0cTAgNzkgLTUzIDEzMy41dC0xMjAgNTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTQ0OyIgZD0iTTc3Ni41IDEyMTRxOTMuNSAwIDE1OS41IC02NmwxNDEgLTE0MXE2NiAtNjYgNjYgLTE2MHEwIC00MiAtMjggLTk1LjV0LTYyIC04Ny41bC0yOSAtMjlxLTMxIDUzIC03NyA5OWwtMTggMThsOTUgOTVsLTI0NyAyNDhsLTM4OSAtMzg5bDIxMiAtMjEybC0xMDUgLTEwNmwtMTkgMThsLTE0MSAxNDFxLTY2IDY2IC02NiAxNTl0NjYgMTU5bDI4MyAyODNxNjUgNjYgMTU4LjUgNjZ6TTYwMCA3MDZsMTA1IDEwNXExMCAtOCAxOSAtMTdsMTQxIC0xNDEgcTY2IC02NiA2NiAtMTU5dC02NiAtMTU5bC0yODMgLTI4M3EtNjYgLTY2IC0xNTkgLTY2dC0xNTkgNjZsLTE0MSAxNDFxLTY2IDY2IC02NiAxNTkuNXQ2NiAxNTkuNWw1NSA1NXEyOSAtNTUgNzUgLTEwMmwxOCAtMTdsLTk1IC05NWwyNDcgLTI0OGwzODkgMzg5eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNDU7IiBkPSJNNjAzIDEyMDBxODUgMCAxNjIgLTE1dDEyNyAtMzh0NzkgLTQ4dDI5IC00NnYtOTUzcTAgLTQxIC0yOS41IC03MC41dC03MC41IC0yOS41aC02MDBxLTQxIDAgLTcwLjUgMjkuNXQtMjkuNSA3MC41djk1M3EwIDIxIDMwIDQ2LjV0ODEgNDh0MTI5IDM3LjV0MTYzIDE1ek0zMDAgMTAwMHYtNzAwaDYwMHY3MDBoLTYwMHpNNjAwIDI1NHEtNDMgMCAtNzMuNSAtMzAuNXQtMzAuNSAtNzMuNXQzMC41IC03My41dDczLjUgLTMwLjV0NzMuNSAzMC41IHQzMC41IDczLjV0LTMwLjUgNzMuNXQtNzMuNSAzMC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNDY7IiBkPSJNOTAyIDExODVsMjgzIC0yODJxMTUgLTE1IDE1IC0zNnQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNXQtMzUgMTVsLTM2IDM1bC0yNzkgLTI2N3YtMzAwbC0yMTIgMjEwbC0zMDggLTMwN2wtMjgwIC0yMDNsMjAzIDI4MGwzMDcgMzA4bC0yMTAgMjEyaDMwMGwyNjcgMjc5bC0zNSAzNnEtMTUgMTQgLTE1IDM1dDE0LjUgMzUuNXQzNS41IDE0LjV0MzUgLTE1eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNDg7IiBkPSJNNzAwIDEyNDh2LTc4cTM4IC01IDcyLjUgLTE0LjV0NzUuNSAtMzEuNXQ3MSAtNTMuNXQ1MiAtODR0MjQgLTExOC41aC0xNTlxLTQgMzYgLTEwLjUgNTl0LTIxIDQ1dC00MCAzNS41dC02NC41IDIwLjV2LTMwN2w2NCAtMTNxMzQgLTcgNjQgLTE2LjV0NzAgLTMydDY3LjUgLTUyLjV0NDcuNSAtODB0MjAgLTExMnEwIC0xMzkgLTg5IC0yMjR0LTI0NCAtOTd2LTc3aC0xMDB2NzlxLTE1MCAxNiAtMjM3IDEwM3EtNDAgNDAgLTUyLjUgOTMuNSB0LTE1LjUgMTM5LjVoMTM5cTUgLTc3IDQ4LjUgLTEyNnQxMTcuNSAtNjV2MzM1bC0yNyA4cS00NiAxNCAtNzkgMjYuNXQtNzIgMzZ0LTYzIDUydC00MCA3Mi41dC0xNiA5OHEwIDcwIDI1IDEyNnQ2Ny41IDkydDk0LjUgNTd0MTEwIDI3djc3aDEwMHpNNjAwIDc1NHYyNzRxLTI5IC00IC01MCAtMTF0LTQyIC0yMS41dC0zMS41IC00MS41dC0xMC41IC02NXEwIC0yOSA3IC01MC41dDE2LjUgLTM0dDI4LjUgLTIyLjV0MzEuNSAtMTR0MzcuNSAtMTAgcTkgLTMgMTMgLTR6TTcwMCA1NDd2LTMxMHEyMiAyIDQyLjUgNi41dDQ1IDE1LjV0NDEuNSAyN3QyOSA0MnQxMiA1OS41dC0xMi41IDU5LjV0LTM4IDQ0LjV0LTUzIDMxdC02Ni41IDI0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE0OTsiIGQ9Ik01NjEgMTE5N3E4NCAwIDE2MC41IC00MHQxMjMuNSAtMTA5LjV0NDcgLTE0Ny41aC0xNTNxMCA0MCAtMTkuNSA3MS41dC00OS41IDQ4LjV0LTU5LjUgMjZ0LTU1LjUgOXEtMzcgMCAtNzkgLTE0LjV0LTYyIC0zNS41cS00MSAtNDQgLTQxIC0xMDFxMCAtMjYgMTMuNSAtNjN0MjYuNSAtNjF0MzcgLTY2cTYgLTkgOSAtMTRoMjQxdi0xMDBoLTE5N3E4IC01MCAtMi41IC0xMTV0LTMxLjUgLTk1cS00NSAtNjIgLTk5IC0xMTIgcTM0IDEwIDgzIDE3LjV0NzEgNy41cTMyIDEgMTAyIC0xNnQxMDQgLTE3cTgzIDAgMTM2IDMwbDUwIC0xNDdxLTMxIC0xOSAtNTggLTMwLjV0LTU1IC0xNS41dC00MiAtNC41dC00NiAtMC41cS0yMyAwIC03NiAxN3QtMTExIDMyLjV0LTk2IDExLjVxLTM5IC0zIC04MiAtMTZ0LTY3IC0yNWwtMjMgLTExbC01NSAxNDVxNCAzIDE2IDExdDE1LjUgMTAuNXQxMyA5dDE1LjUgMTJ0MTQuNSAxNHQxNy41IDE4LjVxNDggNTUgNTQgMTI2LjUgdC0zMCAxNDIuNWgtMjIxdjEwMGgxNjZxLTIzIDQ3IC00NCAxMDRxLTcgMjAgLTEyIDQxLjV0LTYgNTUuNXQ2IDY2LjV0MjkuNSA3MC41dDU4LjUgNzFxOTcgODggMjYzIDg4eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTA7IiBkPSJNNDAwIDMwMGgxNTBxMjEgMCAyNSAtMTF0LTEwIC0yNWwtMjMwIC0yNTBxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI1MHEtMTQgMTQgLTEwIDI1dDI1IDExaDE1MHY5MDBoMjAwdi05MDB6TTkzNSAxMTg0bDIzMCAtMjQ5cTE0IC0xNCAxMCAtMjQuNXQtMjUgLTEwLjVoLTE1MHYtOTAwaC0yMDB2OTAwaC0xNTBxLTIxIDAgLTI1IDEwLjV0MTAgMjQuNWwyMzAgMjQ5cTE0IDE1IDM1IDE1dDM1IC0xNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTUxOyIgZD0iTTEwMDAgNzAwaC0xMDB2MTAwaC0xMDB2LTEwMGgtMTAwdjUwMGgzMDB2LTUwMHpNNDAwIDMwMGgxNTBxMjEgMCAyNSAtMTF0LTEwIC0yNWwtMjMwIC0yNTBxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI1MHEtMTQgMTQgLTEwIDI1dDI1IDExaDE1MHY5MDBoMjAwdi05MDB6TTgwMSAxMTAwdi0yMDBoMTAwdjIwMGgtMTAwek0xMDAwIDM1MGwtMjAwIC0yNTBoMjAwdi0xMDBoLTMwMHYxNTBsMjAwIDI1MGgtMjAwdjEwMGgzMDB2LTE1MHogIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE1MjsiIGQ9Ik00MDAgMzAwaDE1MHEyMSAwIDI1IC0xMXQtMTAgLTI1bC0yMzAgLTI1MHEtMTQgLTE1IC0zNSAtMTV0LTM1IDE1bC0yMzAgMjUwcS0xNCAxNCAtMTAgMjV0MjUgMTFoMTUwdjkwMGgyMDB2LTkwMHpNMTAwMCAxMDUwbC0yMDAgLTI1MGgyMDB2LTEwMGgtMzAwdjE1MGwyMDAgMjUwaC0yMDB2MTAwaDMwMHYtMTUwek0xMDAwIDBoLTEwMHYxMDBoLTEwMHYtMTAwaC0xMDB2NTAwaDMwMHYtNTAwek04MDEgNDAwdi0yMDBoMTAwdjIwMGgtMTAweiAiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTUzOyIgZD0iTTQwMCAzMDBoMTUwcTIxIDAgMjUgLTExdC0xMCAtMjVsLTIzMCAtMjUwcS0xNCAtMTUgLTM1IC0xNXQtMzUgMTVsLTIzMCAyNTBxLTE0IDE0IC0xMCAyNXQyNSAxMWgxNTB2OTAwaDIwMHYtOTAwek0xMDAwIDcwMGgtMTAwdjQwMGgtMTAwdjEwMGgyMDB2LTUwMHpNMTEwMCAwaC0xMDB2MTAwaC0yMDB2NDAwaDMwMHYtNTAwek05MDEgNDAwdi0yMDBoMTAwdjIwMGgtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTQ7IiBkPSJNNDAwIDMwMGgxNTBxMjEgMCAyNSAtMTF0LTEwIC0yNWwtMjMwIC0yNTBxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI1MHEtMTQgMTQgLTEwIDI1dDI1IDExaDE1MHY5MDBoMjAwdi05MDB6TTExMDAgNzAwaC0xMDB2MTAwaC0yMDB2NDAwaDMwMHYtNTAwek05MDEgMTEwMHYtMjAwaDEwMHYyMDBoLTEwMHpNMTAwMCAwaC0xMDB2NDAwaC0xMDB2MTAwaDIwMHYtNTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTU7IiBkPSJNNDAwIDMwMGgxNTBxMjEgMCAyNSAtMTF0LTEwIC0yNWwtMjMwIC0yNTBxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI1MHEtMTQgMTQgLTEwIDI1dDI1IDExaDE1MHY5MDBoMjAwdi05MDB6TTkwMCAxMDAwaC0yMDB2MjAwaDIwMHYtMjAwek0xMDAwIDcwMGgtMzAwdjIwMGgzMDB2LTIwMHpNMTEwMCA0MDBoLTQwMHYyMDBoNDAwdi0yMDB6TTEyMDAgMTAwaC01MDB2MjAwaDUwMHYtMjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTY7IiBkPSJNNDAwIDMwMGgxNTBxMjEgMCAyNSAtMTF0LTEwIC0yNWwtMjMwIC0yNTBxLTE0IC0xNSAtMzUgLTE1dC0zNSAxNWwtMjMwIDI1MHEtMTQgMTQgLTEwIDI1dDI1IDExaDE1MHY5MDBoMjAwdi05MDB6TTEyMDAgMTAwMGgtNTAwdjIwMGg1MDB2LTIwMHpNMTEwMCA3MDBoLTQwMHYyMDBoNDAwdi0yMDB6TTEwMDAgNDAwaC0zMDB2MjAwaDMwMHYtMjAwek05MDAgMTAwaC0yMDB2MjAwaDIwMHYtMjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTc7IiBkPSJNMzUwIDExMDBoNDAwcTE2MiAwIDI1NiAtOTMuNXQ5NCAtMjU2LjV2LTQwMHEwIC0xNjUgLTkzLjUgLTI1Ny41dC0yNTYuNSAtOTIuNWgtNDAwcS0xNjUgMCAtMjU3LjUgOTIuNXQtOTIuNSAyNTcuNXY0MDBxMCAxNjUgOTIuNSAyNTcuNXQyNTcuNSA5Mi41ek04MDAgOTAwaC01MDBxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoNTAwcTQxIDAgNzAuNSAyOS41dDI5LjUgNzAuNSB2NTAwcTAgNDEgLTI5LjUgNzAuNXQtNzAuNSAyOS41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNTg7IiBkPSJNMzUwIDExMDBoNDAwcTE2NSAwIDI1Ny41IC05Mi41dDkyLjUgLTI1Ny41di00MDBxMCAtMTY1IC05Mi41IC0yNTcuNXQtMjU3LjUgLTkyLjVoLTQwMHEtMTYzIDAgLTI1Ni41IDkyLjV0LTkzLjUgMjU3LjV2NDAwcTAgMTYzIDk0IDI1Ni41dDI1NiA5My41ek04MDAgOTAwaC01MDBxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoNTAwcTQxIDAgNzAuNSAyOS41dDI5LjUgNzAuNSB2NTAwcTAgNDEgLTI5LjUgNzAuNXQtNzAuNSAyOS41ek00NDAgNzcwbDI1MyAtMTkwcTE3IC0xMiAxNyAtMzB0LTE3IC0zMGwtMjUzIC0xOTBxLTE2IC0xMiAtMjggLTYuNXQtMTIgMjYuNXY0MDBxMCAyMSAxMiAyNi41dDI4IC02LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE1OTsiIGQ9Ik0zNTAgMTEwMGg0MDBxMTYzIDAgMjU2LjUgLTk0dDkzLjUgLTI1NnYtNDAwcTAgLTE2NSAtOTIuNSAtMjU3LjV0LTI1Ny41IC05Mi41aC00MDBxLTE2NSAwIC0yNTcuNSA5Mi41dC05Mi41IDI1Ny41djQwMHEwIDE2MyA5Mi41IDI1Ni41dDI1Ny41IDkzLjV6TTgwMCA5MDBoLTUwMHEtNDEgMCAtNzAuNSAtMjkuNXQtMjkuNSAtNzAuNXYtNTAwcTAgLTQxIDI5LjUgLTcwLjV0NzAuNSAtMjkuNWg1MDBxNDEgMCA3MC41IDI5LjV0MjkuNSA3MC41IHY1MDBxMCA0MSAtMjkuNSA3MC41dC03MC41IDI5LjV6TTM1MCA3MDBoNDAwcTIxIDAgMjYuNSAtMTJ0LTYuNSAtMjhsLTE5MCAtMjUzcS0xMiAtMTcgLTMwIC0xN3QtMzAgMTdsLTE5MCAyNTNxLTEyIDE2IC02LjUgMjh0MjYuNSAxMnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTYwOyIgZD0iTTM1MCAxMTAwaDQwMHExNjUgMCAyNTcuNSAtOTIuNXQ5Mi41IC0yNTcuNXYtNDAwcTAgLTE2MyAtOTIuNSAtMjU2LjV0LTI1Ny41IC05My41aC00MDBxLTE2MyAwIC0yNTYuNSA5NHQtOTMuNSAyNTZ2NDAwcTAgMTY1IDkyLjUgMjU3LjV0MjU3LjUgOTIuNXpNODAwIDkwMGgtNTAwcS00MSAwIC03MC41IC0yOS41dC0yOS41IC03MC41di01MDBxMCAtNDEgMjkuNSAtNzAuNXQ3MC41IC0yOS41aDUwMHE0MSAwIDcwLjUgMjkuNXQyOS41IDcwLjUgdjUwMHEwIDQxIC0yOS41IDcwLjV0LTcwLjUgMjkuNXpNNTgwIDY5M2wxOTAgLTI1M3ExMiAtMTYgNi41IC0yOHQtMjYuNSAtMTJoLTQwMHEtMjEgMCAtMjYuNSAxMnQ2LjUgMjhsMTkwIDI1M3ExMiAxNyAzMCAxN3QzMCAtMTd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE2MTsiIGQ9Ik01NTAgMTEwMGg0MDBxMTY1IDAgMjU3LjUgLTkyLjV0OTIuNSAtMjU3LjV2LTQwMHEwIC0xNjUgLTkyLjUgLTI1Ny41dC0yNTcuNSAtOTIuNWgtNDAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDQ1MHE0MSAwIDcwLjUgMjkuNXQyOS41IDcwLjV2NTAwcTAgNDEgLTI5LjUgNzAuNXQtNzAuNSAyOS41aC00NTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0zMzggODY3bDMyNCAtMjg0cTE2IC0xNCAxNiAtMzN0LTE2IC0zM2wtMzI0IC0yODRxLTE2IC0xNCAtMjcgLTl0LTExIDI2djE1MGgtMjUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYyMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDI1MHYxNTBxMCAyMSAxMSAyNnQyNyAtOXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTYyOyIgZD0iTTc5MyAxMTgybDkgLTlxOCAtMTAgNSAtMjdxLTMgLTExIC03OSAtMjI1LjV0LTc4IC0yMjEuNWwzMDAgMXEyNCAwIDMyLjUgLTE3LjV0LTUuNSAtMzUuNXEtMSAwIC0xMzMuNSAtMTU1dC0yNjcgLTMxMi41dC0xMzguNSAtMTYyLjVxLTEyIC0xNSAtMjYgLTE1aC05bC05IDhxLTkgMTEgLTQgMzJxMiA5IDQyIDEyMy41dDc5IDIyNC41bDM5IDExMGgtMzAycS0yMyAwIC0zMSAxOXEtMTAgMjEgNiA0MXE3NSA4NiAyMDkuNSAyMzcuNSB0MjI4IDI1N3Q5OC41IDExMS41cTkgMTYgMjUgMTZoOXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTYzOyIgZD0iTTM1MCAxMTAwaDQwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00NTBxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMHEwIC00MSAyOS41IC03MC41dDcwLjUgLTI5LjVoNDUwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTQwMHEtMTY1IDAgLTI1Ny41IDkyLjV0LTkyLjUgMjU3LjV2NDAwIHEwIDE2NSA5Mi41IDI1Ny41dDI1Ny41IDkyLjV6TTkzOCA4NjdsMzI0IC0yODRxMTYgLTE0IDE2IC0zM3QtMTYgLTMzbC0zMjQgLTI4NHEtMTYgLTE0IC0yNyAtOXQtMTEgMjZ2MTUwaC0yNTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djIwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoMjUwdjE1MHEwIDIxIDExIDI2dDI3IC05eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNjQ7IiBkPSJNNzUwIDEyMDBoNDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di00MDBxMCAtMjEgLTEwLjUgLTI1dC0yNC41IDEwbC0xMDkgMTA5bC0zMTIgLTMxMnEtMTUgLTE1IC0zNS41IC0xNXQtMzUuNSAxNWwtMTQxIDE0MXEtMTUgMTUgLTE1IDM1LjV0MTUgMzUuNWwzMTIgMzEybC0xMDkgMTA5cS0xNCAxNCAtMTAgMjQuNXQyNSAxMC41ek00NTYgOTAwaC0xNTZxLTQxIDAgLTcwLjUgLTI5LjV0LTI5LjUgLTcwLjV2LTUwMCBxMCAtNDEgMjkuNSAtNzAuNXQ3MC41IC0yOS41aDUwMHE0MSAwIDcwLjUgMjkuNXQyOS41IDcwLjV2MTQ4bDIwMCAyMDB2LTI5OHEwIC0xNjUgLTkzLjUgLTI1Ny41dC0yNTYuNSAtOTIuNWgtNDAwcS0xNjUgMCAtMjU3LjUgOTIuNXQtOTIuNSAyNTcuNXY0MDBxMCAxNjUgOTIuNSAyNTcuNXQyNTcuNSA5Mi41aDMwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTY1OyIgZD0iTTYwMCAxMTg2cTExOSAwIDIyNy41IC00Ni41dDE4NyAtMTI1dDEyNSAtMTg3dDQ2LjUgLTIyNy41dC00Ni41IC0yMjcuNXQtMTI1IC0xODd0LTE4NyAtMTI1dC0yMjcuNSAtNDYuNXQtMjI3LjUgNDYuNXQtMTg3IDEyNXQtMTI1IDE4N3QtNDYuNSAyMjcuNXQ0Ni41IDIyNy41dDEyNSAxODd0MTg3IDEyNXQyMjcuNSA0Ni41ek02MDAgMTAyMnEtMTE1IDAgLTIxMiAtNTYuNXQtMTUzLjUgLTE1My41dC01Ni41IC0yMTJ0NTYuNSAtMjEyIHQxNTMuNSAtMTUzLjV0MjEyIC01Ni41dDIxMiA1Ni41dDE1My41IDE1My41dDU2LjUgMjEydC01Ni41IDIxMnQtMTUzLjUgMTUzLjV0LTIxMiA1Ni41ek02MDAgNzk0cTgwIDAgMTM3IC01N3Q1NyAtMTM3dC01NyAtMTM3dC0xMzcgLTU3dC0xMzcgNTd0LTU3IDEzN3Q1NyAxMzd0MTM3IDU3eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNjY7IiBkPSJNNDUwIDEyMDBoMjAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0zNTBoMjQ1cTIwIDAgMjUgLTExdC05IC0yNmwtMzgzIC00MjZxLTE0IC0xNSAtMzMuNSAtMTV0LTMyLjUgMTVsLTM3OSA0MjZxLTEzIDE1IC04LjUgMjZ0MjUuNSAxMWgyNTB2MzUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNTAgMzAwaDEwMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTI1MGgtMTEwMHYyNTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiBNOTAwIDIwMHYtNTBoMTAwdjUwaC0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE2NzsiIGQ9Ik01ODMgMTE4MmwzNzggLTQzNXExNCAtMTUgOSAtMzF0LTI2IC0xNmgtMjQ0di0yNTBxMCAtMjAgLTE3IC0zNXQtMzkgLTE1aC0yMDBxLTIwIDAgLTMyIDE0LjV0LTEyIDM1LjV2MjUwaC0yNTBxLTIwIDAgLTI1LjUgMTYuNXQ4LjUgMzEuNWwzODMgNDMxcTE0IDE2IDMzLjUgMTd0MzMuNSAtMTR6TTUwIDMwMGgxMDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0yNTBoLTExMDB2MjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXogTTkwMCAyMDB2LTUwaDEwMHY1MGgtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNjg7IiBkPSJNMzk2IDcyM2wzNjkgMzY5cTcgNyAxNy41IDd0MTcuNSAtN2wxMzkgLTEzOXE3IC04IDcgLTE4LjV0LTcgLTE3LjVsLTUyNSAtNTI1cS03IC04IC0xNy41IC04dC0xNy41IDhsLTI5MiAyOTFxLTcgOCAtNyAxOHQ3IDE4bDEzOSAxMzlxOCA3IDE4LjUgN3QxNy41IC03ek01MCAzMDBoMTAwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjUwaC0xMTAwdjI1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTkwMCAyMDB2LTUwaDEwMHY1MCBoLTEwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTY5OyIgZD0iTTEzNSAxMDIzbDE0MiAxNDJxMTQgMTQgMzUgMTR0MzUgLTE0bDc3IC03N2wtMjEyIC0yMTJsLTc3IDc2cS0xNCAxNSAtMTQgMzZ0MTQgMzV6TTY1NSA4NTVsMjEwIDIxMHExNCAxNCAyNC41IDEwdDEwLjUgLTI1bC0yIC01OTlxLTEgLTIwIC0xNS41IC0zNXQtMzUuNSAtMTVsLTU5NyAtMXEtMjEgMCAtMjUgMTAuNXQxMCAyNC41bDIwOCAyMDhsLTE1NCAxNTVsMjEyIDIxMnpNNTAgMzAwaDEwMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjUgdi0yNTBoLTExMDB2MjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNOTAwIDIwMHYtNTBoMTAwdjUwaC0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE3MDsiIGQ9Ik0zNTAgMTIwMGw1OTkgLTJxMjAgLTEgMzUgLTE1LjV0MTUgLTM1LjVsMSAtNTk3cTAgLTIxIC0xMC41IC0yNXQtMjQuNSAxMGwtMjA4IDIwOGwtMTU1IC0xNTRsLTIxMiAyMTJsMTU1IDE1NGwtMjEwIDIxMHEtMTQgMTQgLTEwIDI0LjV0MjUgMTAuNXpNNTI0IDUxMmwtNzYgLTc3cS0xNSAtMTQgLTM2IC0xNHQtMzUgMTRsLTE0MiAxNDJxLTE0IDE0IC0xNCAzNXQxNCAzNWw3NyA3N3pNNTAgMzAwaDEwMDBxMjEgMCAzNS41IC0xNC41IHQxNC41IC0zNS41di0yNTBoLTExMDB2MjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNOTAwIDIwMHYtNTBoMTAwdjUwaC0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE3MTsiIGQ9Ik0xMjAwIDEwM2wtNDgzIDI3NmwtMzE0IC0zOTl2NDIzaC0zOTlsMTE5NiA3OTZ2LTEwOTZ6TTQ4MyA0MjR2LTIzMGw2ODMgOTUzeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNzI7IiBkPSJNMTEwMCAxMDAwdi04NTBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTE1MHY0MDBoLTcwMHYtNDAwaC0xNTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMDBxMCAyMCAxNC41IDM1dDM1LjUgMTVoMjUwdi0zMDBoNTAwdjMwMGgxMDB6TTcwMCAxMDAwaC0xMDB2MjAwaDEwMHYtMjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNzM7IiBkPSJNMTEwMCAxMDAwbC0yIC0xNDlsLTI5OSAtMjk5bC05NSA5NXEtOSA5IC0yMS41IDl0LTIxLjUgLTlsLTE0OSAtMTQ3aC0zMTJ2LTQwMGgtMTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDAwcTAgMjAgMTQuNSAzNXQzNS41IDE1aDI1MHYtMzAwaDUwMHYzMDBoMTAwek03MDAgMTAwMGgtMTAwdjIwMGgxMDB2LTIwMHpNMTEzMiA2MzhsMTA2IC0xMDZxNyAtNyA3IC0xNy41dC03IC0xNy41bC00MjAgLTQyMXEtOCAtNyAtMTggLTcgdC0xOCA3bC0yMDIgMjAzcS04IDcgLTggMTcuNXQ4IDE3LjVsMTA2IDEwNnE3IDggMTcuNSA4dDE3LjUgLThsNzkgLTc5bDI5NyAyOTdxNyA3IDE3LjUgN3QxNy41IC03eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNzQ7IiBkPSJNMTEwMCAxMDAwdi0yNjlsLTEwMyAtMTAzbC0xMzQgMTM0cS0xNSAxNSAtMzMuNSAxNi41dC0zNC41IC0xMi41bC0yNjYgLTI2NmgtMzI5di00MDBoLTE1MHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwMHEwIDIwIDE0LjUgMzV0MzUuNSAxNWgyNTB2LTMwMGg1MDB2MzAwaDEwMHpNNzAwIDEwMDBoLTEwMHYyMDBoMTAwdi0yMDB6TTEyMDIgNTcybDcwIC03MHExNSAtMTUgMTUgLTM1LjV0LTE1IC0zNS41bC0xMzEgLTEzMSBsMTMxIC0xMzFxMTUgLTE1IDE1IC0zNS41dC0xNSAtMzUuNWwtNzAgLTcwcS0xNSAtMTUgLTM1LjUgLTE1dC0zNS41IDE1bC0xMzEgMTMxbC0xMzEgLTEzMXEtMTUgLTE1IC0zNS41IC0xNXQtMzUuNSAxNWwtNzAgNzBxLTE1IDE1IC0xNSAzNS41dDE1IDM1LjVsMTMxIDEzMWwtMTMxIDEzMXEtMTUgMTUgLTE1IDM1LjV0MTUgMzUuNWw3MCA3MHExNSAxNSAzNS41IDE1dDM1LjUgLTE1bDEzMSAtMTMxbDEzMSAxMzFxMTUgMTUgMzUuNSAxNSB0MzUuNSAtMTV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE3NTsiIGQ9Ik0xMTAwIDEwMDB2LTMwMGgtMzUwcS0yMSAwIC0zNS41IC0xNC41dC0xNC41IC0zNS41di0xNTBoLTUwMHYtNDAwaC0xNTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMDBxMCAyMCAxNC41IDM1dDM1LjUgMTVoMjUwdi0zMDBoNTAwdjMwMGgxMDB6TTcwMCAxMDAwaC0xMDB2MjAwaDEwMHYtMjAwek04NTAgNjAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMjUwaDE1MHEyMSAwIDI1IC0xMC41dC0xMCAtMjQuNSBsLTIzMCAtMjMwcS0xNCAtMTQgLTM1IC0xNHQtMzUgMTRsLTIzMCAyMzBxLTE0IDE0IC0xMCAyNC41dDI1IDEwLjVoMTUwdjI1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE3NjsiIGQ9Ik0xMTAwIDEwMDB2LTQwMGwtMTY1IDE2NXEtMTQgMTUgLTM1IDE1dC0zNSAtMTVsLTI2MyAtMjY1aC00MDJ2LTQwMGgtMTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDAwcTAgMjAgMTQuNSAzNXQzNS41IDE1aDI1MHYtMzAwaDUwMHYzMDBoMTAwek03MDAgMTAwMGgtMTAwdjIwMGgxMDB2LTIwMHpNOTM1IDU2NWwyMzAgLTIyOXExNCAtMTUgMTAgLTI1LjV0LTI1IC0xMC41aC0xNTB2LTI1MHEwIC0yMCAtMTQuNSAtMzUgdC0zNS41IC0xNWgtMTAwcS0yMSAwIC0zNS41IDE1dC0xNC41IDM1djI1MGgtMTUwcS0yMSAwIC0yNSAxMC41dDEwIDI1LjVsMjMwIDIyOXExNCAxNSAzNSAxNXQzNSAtMTV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE3NzsiIGQ9Ik01MCAxMTAwaDExMDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTE1MGgtMTIwMHYxNTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0xMjAwIDgwMHYtNTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY1NTBoMTIwMHpNMTAwIDUwMHYtMjAwaDQwMHYyMDBoLTQwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTc4OyIgZD0iTTkzNSAxMTY1bDI0OCAtMjMwcTE0IC0xNCAxNCAtMzV0LTE0IC0zNWwtMjQ4IC0yMzBxLTE0IC0xNCAtMjQuNSAtMTB0LTEwLjUgMjV2MTUwaC00MDB2MjAwaDQwMHYxNTBxMCAyMSAxMC41IDI1dDI0LjUgLTEwek0yMDAgODAwaC01MHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWg1MHYtMjAwek00MDAgODAwaC0xMDB2MjAwaDEwMHYtMjAwek0xOCA0MzVsMjQ3IDIzMCBxMTQgMTQgMjQuNSAxMHQxMC41IC0yNXYtMTUwaDQwMHYtMjAwaC00MDB2LTE1MHEwIC0yMSAtMTAuNSAtMjV0LTI0LjUgMTBsLTI0NyAyMzBxLTE1IDE0IC0xNSAzNXQxNSAzNXpNOTAwIDMwMGgtMTAwdjIwMGgxMDB2LTIwMHpNMTAwMCA1MDBoNTFxMjAgMCAzNC41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzQuNSAtMTQuNWgtNTF2MjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxNzk7IiBkPSJNODYyIDEwNzNsMjc2IDExNnEyNSAxOCA0My41IDh0MTguNSAtNDF2LTExMDZxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2Mzk3cS00IDEgLTExIDV0LTI0IDE3LjV0LTMwIDI5dC0yNCA0MnQtMTEgNTYuNXYzNTlxMCAzMSAxOC41IDY1dDQzLjUgNTJ6TTU1MCAxMjAwcTIyIDAgMzQuNSAtMTIuNXQxNC41IC0yNC41bDEgLTEzdi00NTBxMCAtMjggLTEwLjUgLTU5LjUgdC0yNSAtNTZ0LTI5IC00NXQtMjUuNSAtMzEuNWwtMTAgLTExdi00NDdxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTIwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NDQ3cS00IDQgLTExIDExLjV0LTI0IDMwLjV0LTMwIDQ2dC0yNCA1NXQtMTEgNjB2NDUwcTAgMiAwLjUgNS41dDQgMTJ0OC41IDE1dDE0LjUgMTJ0MjIuNSA1LjVxMjAgMCAzMi41IC0xMi41dDE0LjUgLTI0LjVsMyAtMTN2LTM1MGgxMDB2MzUwdjUuNXQyLjUgMTIgdDcgMTV0MTUgMTJ0MjUuNSA1LjVxMjMgMCAzNS41IC0xMi41dDEzLjUgLTI0LjVsMSAtMTN2LTM1MGgxMDB2MzUwcTAgMiAwLjUgNS41dDMgMTJ0NyAxNXQxNSAxMnQyNC41IDUuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTgwOyIgZD0iTTEyMDAgMTEwMHYtNTZxLTQgMCAtMTEgLTAuNXQtMjQgLTN0LTMwIC03LjV0LTI0IC0xNXQtMTEgLTI0di04ODhxMCAtMjIgMjUgLTM0LjV0NTAgLTEzLjVsMjUgLTJ2LTU2aC00MDB2NTZxNzUgMCA4Ny41IDYuNXQxMi41IDQzLjV2Mzk0aC01MDB2LTM5NHEwIC0zNyAxMi41IC00My41dDg3LjUgLTYuNXYtNTZoLTQwMHY1NnE0IDAgMTEgMC41dDI0IDN0MzAgNy41dDI0IDE1dDExIDI0djg4OHEwIDIyIC0yNSAzNC41dC01MCAxMy41IGwtMjUgMnY1Nmg0MDB2LTU2cS03NSAwIC04Ny41IC02LjV0LTEyLjUgLTQzLjV2LTM5NGg1MDB2Mzk0cTAgMzcgLTEyLjUgNDMuNXQtODcuNSA2LjV2NTZoNDAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxODE7IiBkPSJNNjc1IDEwMDBoMzc1cTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xNTBoLTEwNWwtMjk1IC05OHY5OGwtMjAwIDIwMGgtNDAwbDEwMCAxMDBoMzc1ek0xMDAgOTAwaDMwMHE0MSAwIDcwLjUgLTI5LjV0MjkuNSAtNzAuNXYtNTAwcTAgLTQxIC0yOS41IC03MC41dC03MC41IC0yOS41aC0zMDBxLTQxIDAgLTcwLjUgMjkuNXQtMjkuNSA3MC41djUwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjV6TTEwMCA4MDB2LTIwMGgzMDB2MjAwIGgtMzAwek0xMTAwIDUzNWwtNDAwIC0xMzN2MTYzbDQwMCAxMzN2LTE2M3pNMTAwIDUwMHYtMjAwaDMwMHYyMDBoLTMwMHpNMTEwMCAzOTh2LTI0OHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMzc1bC0xMDAgLTEwMGgtMzc1bC0xMDAgMTAwaDQwMGwyMDAgMjAwaDEwNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTgyOyIgZD0iTTE3IDEwMDdsMTYyIDE2MnExNyAxNyA0MCAxNHQzNyAtMjJsMTM5IC0xOTRxMTQgLTIwIDExIC00NC41dC0yMCAtNDEuNWwtMTE5IC0xMThxMTAyIC0xNDIgMjI4IC0yNjh0MjY3IC0yMjdsMTE5IDExOHExNyAxNyA0Mi41IDE5dDQ0LjUgLTEybDE5MiAtMTM2cTE5IC0xNCAyMi41IC0zNy41dC0xMy41IC00MC41bC0xNjMgLTE2MnEtMyAtMSAtOS41IC0xdC0yOS41IDJ0LTQ3LjUgNnQtNjIuNSAxNC41dC03Ny41IDI2LjV0LTkwIDQyLjUgdC0xMDEuNSA2MHQtMTExIDgzdC0xMTkgMTA4LjVxLTc0IDc0IC0xMzMuNSAxNTAuNXQtOTQuNSAxMzguNXQtNjAgMTE5LjV0LTM0LjUgMTAwdC0xNSA3NC41dC00LjUgNDh6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE4MzsiIGQ9Ik02MDAgMTEwMHE5MiAwIDE3NSAtMTAuNXQxNDEuNSAtMjd0MTA4LjUgLTM2LjV0ODEuNSAtNDB0NTMuNSAtMzd0MzEgLTI3bDkgLTEwdi0yMDBxMCAtMjEgLTE0LjUgLTMzdC0zNC41IC05bC0yMDIgMzRxLTIwIDMgLTM0LjUgMjB0LTE0LjUgMzh2MTQ2cS0xNDEgMjQgLTMwMCAyNHQtMzAwIC0yNHYtMTQ2cTAgLTIxIC0xNC41IC0zOHQtMzQuNSAtMjBsLTIwMiAtMzRxLTIwIC0zIC0zNC41IDl0LTE0LjUgMzN2MjAwcTMgNCA5LjUgMTAuNSB0MzEgMjZ0NTQgMzcuNXQ4MC41IDM5LjV0MTA5IDM3LjV0MTQxIDI2LjV0MTc1IDEwLjV6TTYwMCA3OTVxNTYgMCA5NyAtOS41dDYwIC0yMy41dDMwIC0yOHQxMiAtMjRsMSAtMTB2LTUwbDM2NSAtMzAzcTE0IC0xNSAyNC41IC00MHQxMC41IC00NXYtMjEycTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYyMTJxMCAyMCAxMC41IDQ1dDI0LjUgNDBsMzY1IDMwM3Y1MCBxMCA0IDEgMTAuNXQxMiAyM3QzMCAyOXQ2MCAyMi41dDk3IDEweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxODQ7IiBkPSJNMTEwMCA3MDBsLTIwMCAtMjAwaC02MDBsLTIwMCAyMDB2NTAwaDIwMHYtMjAwaDIwMHYyMDBoMjAwdi0yMDBoMjAwdjIwMGgyMDB2LTUwMHpNMjUwIDQwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV0LTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEybDEzNyAtMTAwaC05NTBsMTM3IDEwMGgtMTJxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDEwMGgxMTAwcTIxIDAgMzUuNSAtMTQuNSB0MTQuNSAtMzUuNXYtNTBoLTEyMDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxODU7IiBkPSJNNzAwIDExMDBoLTEwMHEtNDEgMCAtNzAuNSAtMjkuNXQtMjkuNSAtNzAuNXYtMTAwMGgzMDB2MTAwMHEwIDQxIC0yOS41IDcwLjV0LTcwLjUgMjkuNXpNMTEwMCA4MDBoLTEwMHEtNDEgMCAtNzAuNSAtMjkuNXQtMjkuNSAtNzAuNXYtNzAwaDMwMHY3MDBxMCA0MSAtMjkuNSA3MC41dC03MC41IDI5LjV6TTQwMCAwaC0zMDB2NDAwcTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNWgxMDBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTQwMHogIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE4NjsiIGQ9Ik0yMDAgMTEwMGg3MDBxMTI0IDAgMjEyIC04OHQ4OCAtMjEydi01MDBxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4aC03MDBxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnY1MDBxMCAxMjQgODggMjEydDIxMiA4OHpNMTAwIDkwMHYtNzAwaDkwMHY3MDBoLTkwMHpNNTAwIDcwMGgtMjAwdi0xMDBoMjAwdi0zMDBoLTMwMHYxMDBoMjAwdjEwMGgtMjAwdjMwMGgzMDB2LTEwMHpNOTAwIDcwMHYtMzAwbC0xMDAgLTEwMGgtMjAwdjUwMGgyMDB6IE03MDAgNzAwdi0zMDBoMTAwdjMwMGgtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxODc7IiBkPSJNMjAwIDExMDBoNzAwcTEyNCAwIDIxMiAtODh0ODggLTIxMnYtNTAwcTAgLTEyNCAtODggLTIxMnQtMjEyIC04OGgtNzAwcS0xMjQgMCAtMjEyIDg4dC04OCAyMTJ2NTAwcTAgMTI0IDg4IDIxMnQyMTIgODh6TTEwMCA5MDB2LTcwMGg5MDB2NzAwaC05MDB6TTUwMCAzMDBoLTEwMHYyMDBoLTEwMHYtMjAwaC0xMDB2NTAwaDEwMHYtMjAwaDEwMHYyMDBoMTAwdi01MDB6TTkwMCA3MDB2LTMwMGwtMTAwIC0xMDBoLTIwMHY1MDBoMjAweiBNNzAwIDcwMHYtMzAwaDEwMHYzMDBoLTEwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTg4OyIgZD0iTTIwMCAxMTAwaDcwMHExMjQgMCAyMTIgLTg4dDg4IC0yMTJ2LTUwMHEwIC0xMjQgLTg4IC0yMTJ0LTIxMiAtODhoLTcwMHEtMTI0IDAgLTIxMiA4OHQtODggMjEydjUwMHEwIDEyNCA4OCAyMTJ0MjEyIDg4ek0xMDAgOTAwdi03MDBoOTAwdjcwMGgtOTAwek01MDAgNzAwaC0yMDB2LTMwMGgyMDB2LTEwMGgtMzAwdjUwMGgzMDB2LTEwMHpNOTAwIDcwMGgtMjAwdi0zMDBoMjAwdi0xMDBoLTMwMHY1MDBoMzAwdi0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE4OTsiIGQ9Ik0yMDAgMTEwMGg3MDBxMTI0IDAgMjEyIC04OHQ4OCAtMjEydi01MDBxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4aC03MDBxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnY1MDBxMCAxMjQgODggMjEydDIxMiA4OHpNMTAwIDkwMHYtNzAwaDkwMHY3MDBoLTkwMHpNNTAwIDQwMGwtMzAwIDE1MGwzMDAgMTUwdi0zMDB6TTkwMCA1NTBsLTMwMCAtMTUwdjMwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTkwOyIgZD0iTTIwMCAxMTAwaDcwMHExMjQgMCAyMTIgLTg4dDg4IC0yMTJ2LTUwMHEwIC0xMjQgLTg4IC0yMTJ0LTIxMiAtODhoLTcwMHEtMTI0IDAgLTIxMiA4OHQtODggMjEydjUwMHEwIDEyNCA4OCAyMTJ0MjEyIDg4ek0xMDAgOTAwdi03MDBoOTAwdjcwMGgtOTAwek05MDAgMzAwaC03MDB2NTAwaDcwMHYtNTAwek04MDAgNzAwaC0xMzBxLTM4IDAgLTY2LjUgLTQzdC0yOC41IC0xMDh0MjcgLTEwN3Q2OCAtNDJoMTMwdjMwMHpNMzAwIDcwMHYtMzAwIGgxMzBxNDEgMCA2OCA0MnQyNyAxMDd0LTI4LjUgMTA4dC02Ni41IDQzaC0xMzB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE5MTsiIGQ9Ik0yMDAgMTEwMGg3MDBxMTI0IDAgMjEyIC04OHQ4OCAtMjEydi01MDBxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4aC03MDBxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnY1MDBxMCAxMjQgODggMjEydDIxMiA4OHpNMTAwIDkwMHYtNzAwaDkwMHY3MDBoLTkwMHpNNTAwIDcwMGgtMjAwdi0xMDBoMjAwdi0zMDBoLTMwMHYxMDBoMjAwdjEwMGgtMjAwdjMwMGgzMDB2LTEwMHpNOTAwIDMwMGgtMTAwdjQwMGgtMTAwdjEwMGgyMDB2LTUwMHogTTcwMCAzMDBoLTEwMHYxMDBoMTAwdi0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE5MjsiIGQ9Ik0yMDAgMTEwMGg3MDBxMTI0IDAgMjEyIC04OHQ4OCAtMjEydi01MDBxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4aC03MDBxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnY1MDBxMCAxMjQgODggMjEydDIxMiA4OHpNMTAwIDkwMHYtNzAwaDkwMHY3MDBoLTkwMHpNMzAwIDcwMGgyMDB2LTQwMGgtMzAwdjUwMGgxMDB2LTEwMHpNOTAwIDMwMGgtMTAwdjQwMGgtMTAwdjEwMGgyMDB2LTUwMHpNMzAwIDYwMHYtMjAwaDEwMHYyMDBoLTEwMHogTTcwMCAzMDBoLTEwMHYxMDBoMTAwdi0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE5MzsiIGQ9Ik0yMDAgMTEwMGg3MDBxMTI0IDAgMjEyIC04OHQ4OCAtMjEydi01MDBxMCAtMTI0IC04OCAtMjEydC0yMTIgLTg4aC03MDBxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnY1MDBxMCAxMjQgODggMjEydDIxMiA4OHpNMTAwIDkwMHYtNzAwaDkwMHY3MDBoLTkwMHpNNTAwIDUwMGwtMTk5IC0yMDBoLTEwMHY1MGwxOTkgMjAwdjE1MGgtMjAwdjEwMGgzMDB2LTMwMHpNOTAwIDMwMGgtMTAwdjQwMGgtMTAwdjEwMGgyMDB2LTUwMHpNNzAxIDMwMGgtMTAwIHYxMDBoMTAwdi0xMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTE5NDsiIGQ9Ik02MDAgMTE5MXExMjAgMCAyMjkuNSAtNDd0MTg4LjUgLTEyNnQxMjYgLTE4OC41dDQ3IC0yMjkuNXQtNDcgLTIyOS41dC0xMjYgLTE4OC41dC0xODguNSAtMTI2dC0yMjkuNSAtNDd0LTIyOS41IDQ3dC0xODguNSAxMjZ0LTEyNiAxODguNXQtNDcgMjI5LjV0NDcgMjI5LjV0MTI2IDE4OC41dDE4OC41IDEyNnQyMjkuNSA0N3pNNjAwIDEwMjFxLTExNCAwIC0yMTEgLTU2LjV0LTE1My41IC0xNTMuNXQtNTYuNSAtMjExdDU2LjUgLTIxMSB0MTUzLjUgLTE1My41dDIxMSAtNTYuNXQyMTEgNTYuNXQxNTMuNSAxNTMuNXQ1Ni41IDIxMXQtNTYuNSAyMTF0LTE1My41IDE1My41dC0yMTEgNTYuNXpNODAwIDcwMGgtMzAwdi0yMDBoMzAwdi0xMDBoLTMwMGwtMTAwIDEwMHYyMDBsMTAwIDEwMGgzMDB2LTEwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTk1OyIgZD0iTTYwMCAxMTkxcTEyMCAwIDIyOS41IC00N3QxODguNSAtMTI2dDEyNiAtMTg4LjV0NDcgLTIyOS41dC00NyAtMjI5LjV0LTEyNiAtMTg4LjV0LTE4OC41IC0xMjZ0LTIyOS41IC00N3QtMjI5LjUgNDd0LTE4OC41IDEyNnQtMTI2IDE4OC41dC00NyAyMjkuNXQ0NyAyMjkuNXQxMjYgMTg4LjV0MTg4LjUgMTI2dDIyOS41IDQ3ek02MDAgMTAyMXEtMTE0IDAgLTIxMSAtNTYuNXQtMTUzLjUgLTE1My41dC01Ni41IC0yMTF0NTYuNSAtMjExIHQxNTMuNSAtMTUzLjV0MjExIC01Ni41dDIxMSA1Ni41dDE1My41IDE1My41dDU2LjUgMjExdC01Ni41IDIxMXQtMTUzLjUgMTUzLjV0LTIxMSA1Ni41ek04MDAgNzAwdi0xMDBsLTUwIC01MGwxMDAgLTEwMHYtNTBoLTEwMGwtMTAwIDEwMGgtMTUwdi0xMDBoLTEwMHY0MDBoMzAwek01MDAgNzAwdi0xMDBoMjAwdjEwMGgtMjAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxOTc7IiBkPSJNNTAzIDEwODlxMTEwIDAgMjAwLjUgLTU5LjV0MTM0LjUgLTE1Ni41cTQ0IDE0IDkwIDE0cTEyMCAwIDIwNSAtODYuNXQ4NSAtMjA3dC04NSAtMjA3dC0yMDUgLTg2LjVoLTEyOHYyNTBxMCAyMSAtMTQuNSAzNS41dC0zNS41IDE0LjVoLTMwMHEtMjEgMCAtMzUuNSAtMTQuNXQtMTQuNSAtMzUuNXYtMjUwaC0yMjJxLTgwIDAgLTEzNiA1Ny41dC01NiAxMzYuNXEwIDY5IDQzIDEyMi41dDEwOCA2Ny41cS0yIDE5IC0yIDM3cTAgMTAwIDQ5IDE4NSB0MTM0IDEzNHQxODUgNDl6TTUyNSA1MDBoMTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMjc1aDEzN3EyMSAwIDI2IC0xMS41dC04IC0yNy41bC0yMjMgLTI0NHEtMTMgLTE2IC0zMiAtMTZ0LTMyIDE2bC0yMjMgMjQ0cS0xMyAxNiAtOCAyNy41dDI2IDExLjVoMTM3djI3NXEwIDEwIDcuNSAxNy41dDE3LjUgNy41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUxOTg7IiBkPSJNNTAyIDEwODlxMTEwIDAgMjAxIC01OS41dDEzNSAtMTU2LjVxNDMgMTUgODkgMTVxMTIxIDAgMjA2IC04Ni41dDg2IC0yMDYuNXEwIC05OSAtNjAgLTE4MXQtMTUwIC0xMTBsLTM3OCAzNjBxLTEzIDE2IC0zMS41IDE2dC0zMS41IC0xNmwtMzgxIC0zNjVoLTlxLTc5IDAgLTEzNS41IDU3LjV0LTU2LjUgMTM2LjVxMCA2OSA0MyAxMjIuNXQxMDggNjcuNXEtMiAxOSAtMiAzOHEwIDEwMCA0OSAxODQuNXQxMzMuNSAxMzR0MTg0LjUgNDkuNXogTTYzMiA0NjdsMjIzIC0yMjhxMTMgLTE2IDggLTI3LjV0LTI2IC0xMS41aC0xMzd2LTI3NXEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djI3NWgtMTM3cS0yMSAwIC0yNiAxMS41dDggMjcuNXExOTkgMjA0IDIyMyAyMjhxMTkgMTkgMzEuNSAxOXQzMi41IC0xOXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMTk5OyIgZD0iTTcwMCAxMDB2MTAwaDQwMGwtMjcwIDMwMGgxNzBsLTI3MCAzMDBoMTcwbC0zMDAgMzMzbC0zMDAgLTMzM2gxNzBsLTI3MCAtMzAwaDE3MGwtMjcwIC0zMDBoNDAwdi0xMDBoLTUwcS0yMSAwIC0zNS41IC0xNC41dC0xNC41IC0zNS41di01MGg0MDB2NTBxMCAyMSAtMTQuNSAzNS41dC0zNS41IDE0LjVoLTUweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMDA7IiBkPSJNNjAwIDExNzlxOTQgMCAxNjcuNSAtNTYuNXQ5OS41IC0xNDUuNXE4OSAtNiAxNTAuNSAtNzEuNXQ2MS41IC0xNTUuNXEwIC02MSAtMjkuNSAtMTEyLjV0LTc5LjUgLTgyLjVxOSAtMjkgOSAtNTVxMCAtNzQgLTUyLjUgLTEyNi41dC0xMjYuNSAtNTIuNXEtNTUgMCAtMTAwIDMwdi0yNTFxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTUwaC0zMDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41djI1MXEtNDUgLTMwIC0xMDAgLTMwIHEtNzQgMCAtMTI2LjUgNTIuNXQtNTIuNSAxMjYuNXEwIDE4IDQgMzhxLTQ3IDIxIC03NS41IDY1dC0yOC41IDk3cTAgNzQgNTIuNSAxMjYuNXQxMjYuNSA1Mi41cTUgMCAyMyAtMnEwIDIgLTEgMTB0LTEgMTNxMCAxMTYgODEuNSAxOTcuNXQxOTcuNSA4MS41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMDE7IiBkPSJNMTAxMCAxMDEwcTExMSAtMTExIDE1MC41IC0yNjAuNXQwIC0yOTl0LTE1MC41IC0yNjAuNXEtODMgLTgzIC0xOTEuNSAtMTI2LjV0LTIxOC41IC00My41dC0yMTguNSA0My41dC0xOTEuNSAxMjYuNXEtMTExIDExMSAtMTUwLjUgMjYwLjV0MCAyOTl0MTUwLjUgMjYwLjVxODMgODMgMTkxLjUgMTI2LjV0MjE4LjUgNDMuNXQyMTguNSAtNDMuNXQxOTEuNSAtMTI2LjV6TTQ3NiAxMDY1cS00IDAgLTggLTFxLTEyMSAtMzQgLTIwOS41IC0xMjIuNSB0LTEyMi41IC0yMDkuNXEtNCAtMTIgMi41IC0yM3QxOC41IC0xNGwzNiAtOXEzIC0xIDcgLTFxMjMgMCAyOSAyMnEyNyA5NiA5OCAxNjZxNzAgNzEgMTY2IDk4cTExIDMgMTcuNSAxMy41dDMuNSAyMi41bC05IDM1cS0zIDEzIC0xNCAxOXEtNyA0IC0xNSA0ek01MTIgOTIwcS00IDAgLTkgLTJxLTgwIC0yNCAtMTM4LjUgLTgyLjV0LTgyLjUgLTEzOC41cS00IC0xMyAyIC0yNHQxOSAtMTRsMzQgLTlxNCAtMSA4IC0xcTIyIDAgMjggMjEgcTE4IDU4IDU4LjUgOTguNXQ5Ny41IDU4LjVxMTIgMyAxOCAxMy41dDMgMjEuNWwtOSAzNXEtMyAxMiAtMTQgMTlxLTcgNCAtMTUgNHpNNzE5LjUgNzE5LjVxLTQ5LjUgNDkuNSAtMTE5LjUgNDkuNXQtMTE5LjUgLTQ5LjV0LTQ5LjUgLTExOS41dDQ5LjUgLTExOS41dDExOS41IC00OS41dDExOS41IDQ5LjV0NDkuNSAxMTkuNXQtNDkuNSAxMTkuNXpNODU1IDU1MXEtMjIgMCAtMjggLTIxcS0xOCAtNTggLTU4LjUgLTk4LjV0LTk4LjUgLTU3LjUgcS0xMSAtNCAtMTcgLTE0LjV0LTMgLTIxLjVsOSAtMzVxMyAtMTIgMTQgLTE5cTcgLTQgMTUgLTRxNCAwIDkgMnE4MCAyNCAxMzguNSA4Mi41dDgyLjUgMTM4LjVxNCAxMyAtMi41IDI0dC0xOC41IDE0bC0zNCA5cS00IDEgLTggMXpNMTAwMCA1MTVxLTIzIDAgLTI5IC0yMnEtMjcgLTk2IC05OCAtMTY2cS03MCAtNzEgLTE2NiAtOThxLTExIC0zIC0xNy41IC0xMy41dC0zLjUgLTIyLjVsOSAtMzVxMyAtMTMgMTQgLTE5cTcgLTQgMTUgLTQgcTQgMCA4IDFxMTIxIDM0IDIwOS41IDEyMi41dDEyMi41IDIwOS41cTQgMTIgLTIuNSAyM3QtMTguNSAxNGwtMzYgOXEtMyAxIC03IDF6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIwMjsiIGQ9Ik03MDAgODAwaDMwMHYtMzgwaC0xODB2MjAwaC0zNDB2LTIwMGgtMzgwdjc1NXEwIDEwIDcuNSAxNy41dDE3LjUgNy41aDU3NXYtNDAwek0xMDAwIDkwMGgtMjAwdjIwMHpNNzAwIDMwMGgxNjJsLTIxMiAtMjEybC0yMTIgMjEyaDE2MnYyMDBoMTAwdi0yMDB6TTUyMCAwaC0zOTVxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYzOTV6TTEwMDAgMjIwdi0xOTVxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0xOTV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIwMzsiIGQ9Ik03MDAgODAwaDMwMHYtNTIwbC0zNTAgMzUwbC01NTAgLTU1MHYxMDk1cTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoNTc1di00MDB6TTEwMDAgOTAwaC0yMDB2MjAwek04NjIgMjAwaC0xNjJ2LTIwMGgtMTAwdjIwMGgtMTYybDIxMiAyMTJ6TTQ4MCAwaC0zNTVxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1NWgzODB2LTgwek0xMDAwIDgwdi01NXEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTE1NXY4MGgxODB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIwNDsiIGQ9Ik0xMTYyIDgwMGgtMTYydi0yMDBoMTAwbDEwMCAtMTAwaC0zMDB2MzAwaC0xNjJsMjEyIDIxMnpNMjAwIDgwMGgyMDBxMjcgMCA0MCAtMnQyOS41IC0xMC41dDIzLjUgLTMwdDcgLTU3LjVoMzAwdi0xMDBoLTYwMGwtMjAwIC0zNTB2NDUwaDEwMHEwIDM2IDcgNTcuNXQyMy41IDMwdDI5LjUgMTAuNXQ0MCAyek04MDAgNDAwaDI0MGwtMjQwIC00MDBoLTgwMGwzMDAgNTAwaDUwMHYtMTAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMDU7IiBkPSJNNjUwIDExMDBoMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGg1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0zMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djEwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoNTB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0xMDAwIDg1MHYxNTBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTgwMCBxMCAtNDEgLTI5LjUgLTcwLjV0LTcwLjUgLTI5LjVoLTYwMHEtMSAwIC0yMCA0bDI0NiAyNDZsLTMyNiAzMjZ2MzI0cTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNXYtMTUwcTAgLTYyIDQ0IC0xMDZ0MTA2IC00NGgzMDBxNjIgMCAxMDYgNDR0NDQgMTA2ek00MTIgMjUwbC0yMTIgLTIxMnYxNjJoLTIwMHYxMDBoMjAwdjE2MnoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjA2OyIgZD0iTTQ1MCAxMTAwaDEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTBoNTBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMzAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDUwdjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNODAwIDg1MHYxNTBxNDEgMCA3MC41IC0yOS41dDI5LjUgLTcwLjV2LTUwMCBoLTIwMHYtMzAwaDIwMHEwIC0zNiAtNyAtNTcuNXQtMjMuNSAtMzB0LTI5LjUgLTEwLjV0LTQwIC0yaC02MDBxLTQxIDAgLTcwLjUgMjkuNXQtMjkuNSA3MC41djgwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjV2LTE1MHEwIC02MiA0NCAtMTA2dDEwNiAtNDRoMzAwcTYyIDAgMTA2IDQ0dDQ0IDEwNnpNMTIxMiAyNTBsLTIxMiAtMjEydjE2MmgtMjAwdjEwMGgyMDB2MTYyeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMDk7IiBkPSJNNjU4IDExOTdsNjM3IC0xMTA0cTIzIC0zOCA3IC02NS41dC02MCAtMjcuNWgtMTI3NnEtNDQgMCAtNjAgMjcuNXQ3IDY1LjVsNjM3IDExMDRxMjIgMzkgNTQgMzl0NTQgLTM5ek03MDQgODAwaC0yMDhxLTIwIDAgLTMyIC0xNC41dC04IC0zNC41bDU4IC0zMDJxNCAtMjAgMjEuNSAtMzQuNXQzNy41IC0xNC41aDU0cTIwIDAgMzcuNSAxNC41dDIxLjUgMzQuNWw1OCAzMDJxNCAyMCAtOCAzNC41dC0zMiAxNC41ek01MDAgMzAwdi0xMDBoMjAwIHYxMDBoLTIwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjEwOyIgZD0iTTQyNSAxMTAwaDI1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTI1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek00MjUgODAwaDI1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTI1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MHEwIDEwIDcuNSAxNy41IHQxNy41IDcuNXpNODI1IDgwMGgyNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNMjUgNTAwaDI1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTI1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MCBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNNDI1IDUwMGgyNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNODI1IDUwMGgyNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNSB2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTI1IDIwMGgyNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXYxNTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNNDI1IDIwMGgyNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di0xNTBxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0yNTBxLTEwIDAgLTE3LjUgNy41IHQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTgyNSAyMDBoMjUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMjUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIxMTsiIGQ9Ik03MDAgMTIwMGgxMDB2LTIwMGgtMTAwdi0xMDBoMzUwcTYyIDAgODYuNSAtMzkuNXQtMy41IC05NC41bC02NiAtMTMycS00MSAtODMgLTgxIC0xMzRoLTc3MnEtNDAgNTEgLTgxIDEzNGwtNjYgMTMycS0yOCA1NSAtMy41IDk0LjV0ODYuNSAzOS41aDM1MHYxMDBoLTEwMHYyMDBoMTAwdjEwMGgyMDB2LTEwMHpNMjUwIDQwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV0LTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEybDEzNyAtMTAwIGgtOTUwbDEzOCAxMDBoLTEzcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXQxNC41IDM1LjV0MzUuNSAxNC41ek01MCAxMDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTBoLTEyMDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMTI7IiBkPSJNNjAwIDEzMDBxNDAgMCA2OC41IC0yOS41dDI4LjUgLTcwLjVoLTE5NHEwIDQxIDI4LjUgNzAuNXQ2OC41IDI5LjV6TTQ0MyAxMTAwaDMxNHExOCAtMzcgMTggLTc1cTAgLTggLTMgLTI1aDMyOHE0MSAwIDQ0LjUgLTE2LjV0LTMwLjUgLTM4LjVsLTE3NSAtMTQ1aC02NzhsLTE3OCAxNDVxLTM0IDIyIC0yOSAzOC41dDQ2IDE2LjVoMzI4cS0zIDE3IC0zIDI1cTAgMzggMTggNzV6TTI1MCA3MDBoNzAwcTIxIDAgMzUuNSAtMTQuNSB0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTUwdi0yMDBsMjc1IC0yMDBoLTk1MGwyNzUgMjAwdjIwMGgtMTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXQxNC41IDM1LjV0MzUuNSAxNC41ek01MCAxMDBoMTEwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTBoLTEyMDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMTM7IiBkPSJNNjAwIDExODFxNzUgMCAxMjggLTUzdDUzIC0xMjh0LTUzIC0xMjh0LTEyOCAtNTN0LTEyOCA1M3QtNTMgMTI4dDUzIDEyOHQxMjggNTN6TTYwMiA3OThoNDZxMzQgMCA1NS41IC0yOC41dDIxLjUgLTg2LjVxMCAtNzYgMzkgLTE4M2gtMzI0cTM5IDEwNyAzOSAxODNxMCA1OCAyMS41IDg2LjV0NTYuNSAyOC41aDQ1ek0yNTAgNDAwaDcwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTMgbDEzOCAtMTAwaC05NTBsMTM3IDEwMGgtMTJxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDEwMGgxMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGgtMTIwMHY1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIxNDsiIGQ9Ik02MDAgMTMwMHE0NyAwIDkyLjUgLTUzLjV0NzEgLTEyM3QyNS41IC0xMjMuNXEwIC03OCAtNTUuNSAtMTMzLjV0LTEzMy41IC01NS41dC0xMzMuNSA1NS41dC01NS41IDEzMy41cTAgNjIgMzQgMTQzbDE0NCAtMTQzbDExMSAxMTFsLTE2MyAxNjNxMzQgMjYgNjMgMjZ6TTYwMiA3OThoNDZxMzQgMCA1NS41IC0yOC41dDIxLjUgLTg2LjVxMCAtNzYgMzkgLTE4M2gtMzI0cTM5IDEwNyAzOSAxODNxMCA1OCAyMS41IDg2LjV0NTYuNSAyOC41aDQ1IHpNMjUwIDQwMGg3MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV0LTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTEzbDEzOCAtMTAwaC05NTBsMTM3IDEwMGgtMTJxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDEwMGgxMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGgtMTIwMHY1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIxNTsiIGQ9Ik02MDAgMTIwMGwzMDAgLTE2MXYtMTM5aC0zMDBxMCAtNTcgMTguNSAtMTA4dDUwIC05MS41dDYzIC03MnQ3MCAtNjcuNXQ1Ny41IC02MWgtNTMwcS02MCA4MyAtOTAuNSAxNzcuNXQtMzAuNSAxNzguNXQzMyAxNjQuNXQ4Ny41IDEzOS41dDEyNiA5Ni41dDE0NS41IDQxLjV2LTk4ek0yNTAgNDAwaDcwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTNsMTM4IC0xMDBoLTk1MGwxMzcgMTAwIGgtMTJxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6TTUwIDEwMGgxMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGgtMTIwMHY1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIxNjsiIGQ9Ik02MDAgMTMwMHE0MSAwIDcwLjUgLTI5LjV0MjkuNSAtNzAuNXYtNzhxNDYgLTI2IDczIC03MnQyNyAtMTAwdi01MGgtNDAwdjUwcTAgNTQgMjcgMTAwdDczIDcydjc4cTAgNDEgMjkuNSA3MC41dDcwLjUgMjkuNXpNNDAwIDgwMGg0MDBxNTQgMCAxMDAgLTI3dDcyIC03M2gtMTcydi0xMDBoMjAwdi0xMDBoLTIwMHYtMTAwaDIwMHYtMTAwaC0yMDB2LTEwMGgyMDBxMCAtODMgLTU4LjUgLTE0MS41dC0xNDEuNSAtNTguNWgtNDAwIHEtODMgMCAtMTQxLjUgNTguNXQtNTguNSAxNDEuNXY0MDBxMCA4MyA1OC41IDE0MS41dDE0MS41IDU4LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIxODsiIGQ9Ik0xNTAgMTEwMGg5MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTUwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtOTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY1MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0xMjUgNDAwaDk1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMjgzbDIyNCAtMjI0cTEzIC0xMyAxMyAtMzEuNXQtMTMgLTMyIHQtMzEuNSAtMTMuNXQtMzEuNSAxM2wtODggODhoLTUyNGwtODcgLTg4cS0xMyAtMTMgLTMyIC0xM3QtMzIgMTMuNXQtMTMgMzJ0MTMgMzEuNWwyMjQgMjI0aC0yODlxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41ek01NDEgMzAwbC0xMDAgLTEwMGgzMjRsLTEwMCAxMDBoLTEyNHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjE5OyIgZD0iTTIwMCAxMTAwaDgwMHE4MyAwIDE0MS41IC01OC41dDU4LjUgLTE0MS41di0yMDBoLTEwMHEwIDQxIC0yOS41IDcwLjV0LTcwLjUgMjkuNWgtMjUwcS00MSAwIC03MC41IC0yOS41dC0yOS41IC03MC41aC0xMDBxMCA0MSAtMjkuNSA3MC41dC03MC41IDI5LjVoLTI1MHEtNDEgMCAtNzAuNSAtMjkuNXQtMjkuNSAtNzAuNWgtMTAwdjIwMHEwIDgzIDU4LjUgMTQxLjV0MTQxLjUgNTguNXpNMTAwIDYwMGgxMDAwcTQxIDAgNzAuNSAtMjkuNSB0MjkuNSAtNzAuNXYtMzAwaC0xMjAwdjMwMHEwIDQxIDI5LjUgNzAuNXQ3MC41IDI5LjV6TTMwMCAxMDB2LTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djUwaDIwMHpNMTEwMCAxMDB2LTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djUwaDIwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjIxOyIgZD0iTTQ4MCAxMTY1bDY4MiAtNjgzcTMxIC0zMSAzMSAtNzUuNXQtMzEgLTc1LjVsLTEzMSAtMTMxaC00ODFsLTUxNyA1MThxLTMyIDMxIC0zMiA3NS41dDMyIDc1LjVsMjk1IDI5NnEzMSAzMSA3NS41IDMxdDc2LjUgLTMxek0xMDggNzk0bDM0MiAtMzQybDMwMyAzMDRsLTM0MSAzNDF6TTI1MCAxMDBoODAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di01MGgtOTAwdjUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjIzOyIgZD0iTTEwNTcgNjQ3bC0xODkgNTA2cS04IDE5IC0yNy41IDMzdC00MC41IDE0aC00MDBxLTIxIDAgLTQwLjUgLTE0dC0yNy41IC0zM2wtMTg5IC01MDZxLTggLTE5IDEuNSAtMzN0MzAuNSAtMTRoNjI1di0xNTBxMCAtMjEgMTQuNSAtMzUuNXQzNS41IC0xNC41dDM1LjUgMTQuNXQxNC41IDM1LjV2MTUwaDEyNXEyMSAwIDMwLjUgMTR0MS41IDMzek04OTcgMGgtNTk1djUwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWg1MHY1MCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDQ4djMwMGgyMDB2LTMwMGg0N3EyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTBoNTBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTUweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMjQ7IiBkPSJNOTAwIDgwMGgzMDB2LTU3NXEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTM3NXY1OTFsLTMwMCAzMDB2ODRxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgzNzV2LTQwMHpNMTIwMCA5MDBoLTIwMHYyMDB6TTQwMCA2MDBoMzAwdi01NzVxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC02NTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY5NTBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgzNzV2LTQwMHpNNzAwIDcwMGgtMjAwdjIwMHogIiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIyNTsiIGQ9Ik00ODQgMTA5NWgxOTVxNzUgMCAxNDYgLTMyLjV0MTI0IC04NnQ4OS41IC0xMjIuNXQ0OC41IC0xNDJxMTggLTE0IDM1IC0yMHEzMSAtMTAgNjQuNSA2LjV0NDMuNSA0OC41cTEwIDM0IC0xNSA3MXEtMTkgMjcgLTkgNDNxNSA4IDEyLjUgMTF0MTkgLTF0MjMuNSAtMTZxNDEgLTQ0IDM5IC0xMDVxLTMgLTYzIC00NiAtMTA2LjV0LTEwNCAtNDMuNWgtNjJxLTcgLTU1IC0zNSAtMTE3dC01NiAtMTAwbC0zOSAtMjM0cS0zIC0yMCAtMjAgLTM0LjUgdC0zOCAtMTQuNWgtMTAwcS0yMSAwIC0zMyAxNC41dC05IDM0LjVsMTIgNzBxLTQ5IC0xNCAtOTEgLTE0aC0xOTVxLTI0IDAgLTY1IDhsLTExIC02NHEtMyAtMjAgLTIwIC0zNC41dC0zOCAtMTQuNWgtMTAwcS0yMSAwIC0zMyAxNC41dC05IDM0LjVsMjYgMTU3cS04NCA3NCAtMTI4IDE3NWwtMTU5IDUzcS0xOSA3IC0zMyAyNnQtMTQgNDB2NTBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDEyNHExMSA4NyA1NiAxNjZsLTExMSA5NSBxLTE2IDE0IC0xMi41IDIzLjV0MjQuNSA5LjVoMjAzcTExNiAxMDEgMjUwIDEwMXpNNjc1IDEwMDBoLTI1MHEtMTAgMCAtMTcuNSAtNy41dC03LjUgLTE3LjV2LTUwcTAgLTEwIDcuNSAtMTcuNXQxNy41IC03LjVoMjUwcTEwIDAgMTcuNSA3LjV0Ny41IDE3LjV2NTBxMCAxMCAtNy41IDE3LjV0LTE3LjUgNy41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMjY7IiBkPSJNNjQxIDkwMGw0MjMgMjQ3cTE5IDggNDIgMi41dDM3IC0yMS41bDMyIC0zOHExNCAtMTUgMTIuNSAtMzZ0LTE3LjUgLTM0bC0xMzkgLTEyMGgtMzkwek01MCAxMTAwaDEwNnE2NyAwIDEwMyAtMTd0NjYgLTcxbDEwMiAtMjEyaDgyM3EyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNTBxMCAtMjEgLTE0IC00MHQtMzMgLTI2bC03MzcgLTEzMnEtMjMgLTQgLTQwIDZ0LTI2IDI1cS00MiA2NyAtMTAwIDY3aC0zMDBxLTYyIDAgLTEwNiA0NCB0LTQ0IDEwNnYyMDBxMCA2MiA0NCAxMDZ0MTA2IDQ0ek0xNzMgOTI4aC04MHEtMTkgMCAtMjggLTE0dC05IC0zNXYtNTZxMCAtNTEgNDIgLTUxaDEzNHExNiAwIDIxLjUgOHQ1LjUgMjRxMCAxMSAtMTYgNDV0LTI3IDUxcS0xOCAyOCAtNDMgMjh6TTU1MCA3MjdxLTMyIDAgLTU0LjUgLTIyLjV0LTIyLjUgLTU0LjV0MjIuNSAtNTQuNXQ1NC41IC0yMi41dDU0LjUgMjIuNXQyMi41IDU0LjV0LTIyLjUgNTQuNXQtNTQuNSAyMi41ek0xMzAgMzg5IGwxNTIgMTMwcTE4IDE5IDM0IDI0dDMxIC0zLjV0MjQuNSAtMTcuNXQyNS41IC0yOHEyOCAtMzUgNTAuNSAtNTF0NDguNSAtMTNsNjMgNWw0OCAtMTc5cTEzIC02MSAtMy41IC05Ny41dC02Ny41IC03OS41bC04MCAtNjlxLTQ3IC00MCAtMTA5IC0zNS41dC0xMDMgNTEuNWwtMTMwIDE1MXEtNDAgNDcgLTM1LjUgMTA5LjV0NTEuNSAxMDIuNXpNMzgwIDM3N2wtMTAyIC04OHEtMzEgLTI3IDIgLTY1bDM3IC00M3ExMyAtMTUgMjcuNSAtMTkuNSB0MzEuNSA2LjVsNjEgNTNxMTkgMTYgMTQgNDlxLTIgMjAgLTEyIDU2dC0xNyA0NXEtMTEgMTIgLTE5IDE0dC0yMyAtOHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjI3OyIgZD0iTTYyNSAxMjAwaDE1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTEwOXE3OSAtMzMgMTMxIC04Ny41dDUzIC0xMjguNXExIC00NiAtMTUgLTg0LjV0LTM5IC02MXQtNDYgLTM4dC0zOSAtMjEuNWwtMTcgLTZxNiAwIDE1IC0xLjV0MzUgLTl0NTAgLTE3LjV0NTMgLTMwdDUwIC00NXQzNS41IC02NHQxNC41IC04NHEwIC01OSAtMTEuNSAtMTA1LjV0LTI4LjUgLTc2LjV0LTQ0IC01MXQtNDkuNSAtMzEuNXQtNTQuNSAtMTZ0LTQ5LjUgLTYuNSB0LTQzLjUgLTF2LTc1cTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtMTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2NzVoLTEwMHYtNzVxMCAtMTAgLTcuNSAtMTcuNXQtMTcuNSAtNy41aC0xNTBxLTEwIDAgLTE3LjUgNy41dC03LjUgMTcuNXY3NWgtMTc1cS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjVoNzV2NjAwaC03NXEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MCBxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgxNzV2NzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNWgxNTBxMTAgMCAxNy41IC03LjV0Ny41IC0xNy41di03NWgxMDB2NzVxMCAxMCA3LjUgMTcuNXQxNy41IDcuNXpNNDAwIDkwMHYtMjAwaDI2M3EyOCAwIDQ4LjUgMTAuNXQzMCAyNXQxNSAyOXQ1LjUgMjUuNWwxIDEwcTAgNCAtMC41IDExdC02IDI0dC0xNSAzMHQtMzAgMjR0LTQ4LjUgMTFoLTI2M3pNNDAwIDUwMHYtMjAwaDM2M3EyOCAwIDQ4LjUgMTAuNSB0MzAgMjV0MTUgMjl0NS41IDI1LjVsMSAxMHEwIDQgLTAuNSAxMXQtNiAyNHQtMTUgMzB0LTMwIDI0dC00OC41IDExaC0zNjN6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIzMDsiIGQ9Ik0yMTIgMTE5OGg3ODBxODYgMCAxNDcgLTYxdDYxIC0xNDd2LTQxNnEwIC01MSAtMTggLTE0Mi41dC0zNiAtMTU3LjVsLTE4IC02NnEtMjkgLTg3IC05My41IC0xNDYuNXQtMTQ2LjUgLTU5LjVoLTU3MnEtODIgMCAtMTQ3IDU5dC05MyAxNDdxLTggMjggLTIwIDczdC0zMiAxNDMuNXQtMjAgMTQ5LjV2NDE2cTAgODYgNjEgMTQ3dDE0NyA2MXpNNjAwIDEwNDVxLTcwIDAgLTEzMi41IC0xMS41dC0xMDUuNSAtMzAuNXQtNzguNSAtNDEuNSB0LTU3IC00NXQtMzYgLTQxdC0yMC41IC0zMC41bC02IC0xMmwxNTYgLTI0M2g1NjBsMTU2IDI0M3EtMiA1IC02IDEyLjV0LTIwIDI5LjV0LTM2LjUgNDJ0LTU3IDQ0LjV0LTc5IDQydC0xMDUgMjkuNXQtMTMyLjUgMTJ6TTc2MiA3MDNoLTE1N2wxOTUgMjYxeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMzE7IiBkPSJNNDc1IDEzMDBoMTUwcTEwMyAwIDE4OSAtODZ0ODYgLTE4OXYtNTAwcTAgLTQxIC00MiAtODN0LTgzIC00MmgtNDUwcS00MSAwIC04MyA0MnQtNDIgODN2NTAwcTAgMTAzIDg2IDE4OXQxODkgODZ6TTcwMCAzMDB2LTIyNXEwIC0yMSAtMjcgLTQ4dC00OCAtMjdoLTE1MHEtMjEgMCAtNDggMjd0LTI3IDQ4djIyNWgzMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIzMjsiIGQ9Ik00NzUgMTMwMGg5NnEwIC0xNTAgODkuNSAtMjM5LjV0MjM5LjUgLTg5LjV2LTQ0NnEwIC00MSAtNDIgLTgzdC04MyAtNDJoLTQ1MHEtNDEgMCAtODMgNDJ0LTQyIDgzdjUwMHEwIDEwMyA4NiAxODl0MTg5IDg2ek03MDAgMzAwdi0yMjVxMCAtMjEgLTI3IC00OHQtNDggLTI3aC0xNTBxLTIxIDAgLTQ4IDI3dC0yNyA0OHYyMjVoMzAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMzM7IiBkPSJNMTI5NCA3NjdsLTYzOCAtMjgzbC0zNzggMTcwbC03OCAtNjB2LTIyNGwxMDAgLTE1MHYtMTk5bC0xNTAgMTQ4bC0xNTAgLTE0OXYyMDBsMTAwIDE1MHYyNTBxMCA0IC0wLjUgMTAuNXQwIDkuNXQxIDh0MyA4dDYuNSA2bDQ3IDQwbC0xNDcgNjVsNjQyIDI4M3pNMTAwMCAzODBsLTM1MCAtMTY2bC0zNTAgMTY2djE0N2wzNTAgLTE2NWwzNTAgMTY1di0xNDd6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIzNDsiIGQ9Ik0yNTAgODAwcTYyIDAgMTA2IC00NHQ0NCAtMTA2dC00NCAtMTA2dC0xMDYgLTQ0dC0xMDYgNDR0LTQ0IDEwNnQ0NCAxMDZ0MTA2IDQ0ek02NTAgODAwcTYyIDAgMTA2IC00NHQ0NCAtMTA2dC00NCAtMTA2dC0xMDYgLTQ0dC0xMDYgNDR0LTQ0IDEwNnQ0NCAxMDZ0MTA2IDQ0ek0xMDUwIDgwMHE2MiAwIDEwNiAtNDR0NDQgLTEwNnQtNDQgLTEwNnQtMTA2IC00NHQtMTA2IDQ0dC00NCAxMDZ0NDQgMTA2dDEwNiA0NHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjM1OyIgZD0iTTU1MCAxMTAwcTYyIDAgMTA2IC00NHQ0NCAtMTA2dC00NCAtMTA2dC0xMDYgLTQ0dC0xMDYgNDR0LTQ0IDEwNnQ0NCAxMDZ0MTA2IDQ0ek01NTAgNzAwcTYyIDAgMTA2IC00NHQ0NCAtMTA2dC00NCAtMTA2dC0xMDYgLTQ0dC0xMDYgNDR0LTQ0IDEwNnQ0NCAxMDZ0MTA2IDQ0ek01NTAgMzAwcTYyIDAgMTA2IC00NHQ0NCAtMTA2dC00NCAtMTA2dC0xMDYgLTQ0dC0xMDYgNDR0LTQ0IDEwNnQ0NCAxMDZ0MTA2IDQ0eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMzY7IiBkPSJNMTI1IDExMDBoOTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtOTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjV0MTcuNSA3LjV6TTEyNSA3MDBoOTUwcTEwIDAgMTcuNSAtNy41dDcuNSAtMTcuNXYtMTUwcTAgLTEwIC03LjUgLTE3LjV0LTE3LjUgLTcuNWgtOTUwcS0xMCAwIC0xNy41IDcuNXQtNy41IDE3LjV2MTUwcTAgMTAgNy41IDE3LjUgdDE3LjUgNy41ek0xMjUgMzAwaDk1MHExMCAwIDE3LjUgLTcuNXQ3LjUgLTE3LjV2LTE1MHEwIC0xMCAtNy41IC0xNy41dC0xNy41IC03LjVoLTk1MHEtMTAgMCAtMTcuNSA3LjV0LTcuNSAxNy41djE1MHEwIDEwIDcuNSAxNy41dDE3LjUgNy41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMzc7IiBkPSJNMzUwIDEyMDBoNTAwcTE2MiAwIDI1NiAtOTMuNXQ5NCAtMjU2LjV2LTUwMHEwIC0xNjUgLTkzLjUgLTI1Ny41dC0yNTYuNSAtOTIuNWgtNTAwcS0xNjUgMCAtMjU3LjUgOTIuNXQtOTIuNSAyNTcuNXY1MDBxMCAxNjUgOTIuNSAyNTcuNXQyNTcuNSA5Mi41ek05MDAgMTAwMGgtNjAwcS00MSAwIC03MC41IC0yOS41dC0yOS41IC03MC41di02MDBxMCAtNDEgMjkuNSAtNzAuNXQ3MC41IC0yOS41aDYwMHE0MSAwIDcwLjUgMjkuNSB0MjkuNSA3MC41djYwMHEwIDQxIC0yOS41IDcwLjV0LTcwLjUgMjkuNXpNMzUwIDkwMGg1MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTMwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYzMDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek00MDAgODAwdi0yMDBoNDAwdjIwMGgtNDAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyMzg7IiBkPSJNMTUwIDExMDBoMTAwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTB2LTIwMGg1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTB2LTIwMGg1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNTB2LTIwMGg1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXQtMTQuNSAtMzUuNSB0LTM1LjUgLTE0LjVoLTEwMDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjVoNTB2MjAwaC01MHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV0MTQuNSAzNS41dDM1LjUgMTQuNWg1MHYyMDBoLTUwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXQxNC41IDM1LjV0MzUuNSAxNC41aDUwdjIwMGgtNTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41dDE0LjUgMzUuNXQzNS41IDE0LjV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTIzOTsiIGQ9Ik02NTAgMTE4N3E4NyAtNjcgMTE4LjUgLTE1NnQwIC0xNzh0LTExOC41IC0xNTVxLTg3IDY2IC0xMTguNSAxNTV0MCAxNzh0MTE4LjUgMTU2ek0zMDAgODAwcTEyNCAwIDIxMiAtODh0ODggLTIxMnEtMTI0IDAgLTIxMiA4OHQtODggMjEyek0xMDAwIDgwMHEwIC0xMjQgLTg4IC0yMTJ0LTIxMiAtODhxMCAxMjQgODggMjEydDIxMiA4OHpNMzAwIDUwMHExMjQgMCAyMTIgLTg4dDg4IC0yMTJxLTEyNCAwIC0yMTIgODh0LTg4IDIxMnogTTEwMDAgNTAwcTAgLTEyNCAtODggLTIxMnQtMjEyIC04OHEwIDEyNCA4OCAyMTJ0MjEyIDg4ek03MDAgMTk5di0xNDRxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjV0LTM1LjUgMTQuNXQtMTQuNSAzNS41djE0MnE0MCAtNCA0MyAtNHExNyAwIDU3IDZ6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTI0MDsiIGQ9Ik03NDUgODc4bDY5IDE5cTI1IDYgNDUgLTEybDI5OCAtMjk1cTExIC0xMSAxNSAtMjYuNXQtMiAtMzAuNXEtNSAtMTQgLTE4IC0yMy41dC0yOCAtOS41aC04cTEgMCAxIC0xM3EwIC0yOSAtMiAtNTZ0LTguNSAtNjJ0LTIwIC02M3QtMzMgLTUzdC01MSAtMzl0LTcyLjUgLTE0aC0xNDZxLTE4NCAwIC0xODQgMjg4cTAgMjQgMTAgNDdxLTIwIDQgLTYyIDR0LTYzIC00cTExIC0yNCAxMSAtNDdxMCAtMjg4IC0xODQgLTI4OGgtMTQyIHEtNDggMCAtODQuNSAyMXQtNTYgNTF0LTMyIDcxLjV0LTE2IDc1dC0zLjUgNjguNXEwIDEzIDIgMTNoLTdxLTE1IDAgLTI3LjUgOS41dC0xOC41IDIzLjVxLTYgMTUgLTIgMzAuNXQxNSAyNS41bDI5OCAyOTZxMjAgMTggNDYgMTFsNzYgLTE5cTIwIC01IDMwLjUgLTIyLjV0NS41IC0zNy41dC0yMi41IC0zMXQtMzcuNSAtNWwtNTEgMTJsLTE4MiAtMTkzaDg5MWwtMTgyIDE5M2wtNDQgLTEycS0yMCAtNSAtMzcuNSA2dC0yMi41IDMxdDYgMzcuNSB0MzEgMjIuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjQxOyIgZD0iTTEyMDAgOTAwaC01MHEwIDIxIC00IDM3dC05LjUgMjYuNXQtMTggMTcuNXQtMjIgMTF0LTI4LjUgNS41dC0zMSAydC0zNyAwLjVoLTIwMHYtODUwcTAgLTIyIDI1IC0zNC41dDUwIC0xMy41bDI1IC0ydi0xMDBoLTQwMHYxMDBxNCAwIDExIDAuNXQyNCAzdDMwIDd0MjQgMTV0MTEgMjQuNXY4NTBoLTIwMHEtMjUgMCAtMzcgLTAuNXQtMzEgLTJ0LTI4LjUgLTUuNXQtMjIgLTExdC0xOCAtMTcuNXQtOS41IC0yNi41dC00IC0zN2gtNTB2MzAwIGgxMDAwdi0zMDB6TTUwMCA0NTBoLTI1cTAgMTUgLTQgMjQuNXQtOSAxNC41dC0xNyA3LjV0LTIwIDN0LTI1IDAuNWgtMTAwdi00MjVxMCAtMTEgMTIuNSAtMTcuNXQyNS41IC03LjVoMTJ2LTUwaC0yMDB2NTBxNTAgMCA1MCAyNXY0MjVoLTEwMHEtMTcgMCAtMjUgLTAuNXQtMjAgLTN0LTE3IC03LjV0LTkgLTE0LjV0LTQgLTI0LjVoLTI1djE1MGg1MDB2LTE1MHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjQyOyIgZD0iTTEwMDAgMzAwdjUwcS0yNSAwIC01NSAzMnEtMTQgMTQgLTI1IDMxdC0xNiAyN2wtNCAxMWwtMjg5IDc0N2gtNjlsLTMwMCAtNzU0cS0xOCAtMzUgLTM5IC01NnEtOSAtOSAtMjQuNSAtMTguNXQtMjYuNSAtMTQuNWwtMTEgLTV2LTUwaDI3M3Y1MHEtNDkgMCAtNzguNSAyMS41dC0xMS41IDY3LjVsNjkgMTc2aDI5M2w2MSAtMTY2cTEzIC0zNCAtMy41IC02Ni41dC01NS41IC0zMi41di01MGgzMTJ6TTQxMiA2OTFsMTM0IDM0MmwxMjEgLTM0MiBoLTI1NXpNMTEwMCAxNTB2LTEwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtMTAwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2MTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNWgxMDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyNDM7IiBkPSJNNTAgMTIwMGgxMTAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xMTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xMTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXYxMTAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNjExIDExMThoLTcwcS0xMyAwIC0xOCAtMTJsLTI5OSAtNzUzcS0xNyAtMzIgLTM1IC01MXEtMTggLTE4IC01NiAtMzRxLTEyIC01IC0xMiAtMTh2LTUwcTAgLTggNS41IC0xNHQxNC41IC02IGgyNzNxOCAwIDE0IDZ0NiAxNHY1MHEwIDggLTYgMTR0LTE0IDZxLTU1IDAgLTcxIDIzcS0xMCAxNCAwIDM5bDYzIDE2M2gyNjZsNTcgLTE1M3ExMSAtMzEgLTYgLTU1cS0xMiAtMTcgLTM2IC0xN3EtOCAwIC0xNCAtNnQtNiAtMTR2LTUwcTAgLTggNiAtMTR0MTQgLTZoMzEzcTggMCAxNCA2dDYgMTR2NTBxMCA3IC01LjUgMTN0LTEzLjUgN3EtMTcgMCAtNDIgMjVxLTI1IDI3IC00MCA2M2gtMWwtMjg4IDc0OHEtNSAxMiAtMTkgMTJ6TTYzOSA2MTEgaC0xOTdsMTAzIDI2NHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjQ0OyIgZD0iTTEyMDAgMTEwMGgtMTIwMHYxMDBoMTIwMHYtMTAwek01MCAxMDAwaDQwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtOTAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djkwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTY1MCAxMDAwaDQwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00MDAgcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek03MDAgOTAwdi0zMDBoMzAwdjMwMGgtMzAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyNDU7IiBkPSJNNTAgMTIwMGg0MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTkwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNDAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY5MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek02NTAgNzAwaDQwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djQwMCBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek03MDAgNjAwdi0zMDBoMzAwdjMwMGgtMzAwek0xMjAwIDBoLTEyMDB2MTAwaDEyMDB2LTEwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjQ2OyIgZD0iTTUwIDEwMDBoNDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0zNTBoMTAwdjE1MHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoNDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di0xNTBoMTAwdi0xMDBoLTEwMHYtMTUwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC00MDBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djE1MGgtMTAwdi0zNTBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTQwMCBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djgwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTcwMCA3MDB2LTMwMGgzMDB2MzAwaC0zMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTI0NzsiIGQ9Ik0xMDAgMGgtMTAwdjEyMDBoMTAwdi0xMjAwek0yNTAgMTEwMGg0MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtNDAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41ek0zMDAgMTAwMHYtMzAwaDMwMHYzMDBoLTMwMHpNMjUwIDUwMGg5MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMCBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTkwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NDAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjQ4OyIgZD0iTTYwMCAxMTAwaDE1MHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xNTB2LTEwMGg0NTBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMHEwIC0yMSAtMTQuNSAtMzUuNXQtMzUuNSAtMTQuNWgtOTAwcS0yMSAwIC0zNS41IDE0LjV0LTE0LjUgMzUuNXY0MDBxMCAyMSAxNC41IDM1LjV0MzUuNSAxNC41aDM1MHYxMDBoLTE1MHEtMjEgMCAtMzUuNSAxNC41IHQtMTQuNSAzNS41djQwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjVoMTUwdjEwMGgxMDB2LTEwMHpNNDAwIDEwMDB2LTMwMGgzMDB2MzAwaC0zMDB6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTI0OTsiIGQ9Ik0xMjAwIDBoLTEwMHYxMjAwaDEwMHYtMTIwMHpNNTUwIDExMDBoNDAwcTIxIDAgMzUuNSAtMTQuNXQxNC41IC0zNS41di00MDBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTQwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NDAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXpNNjAwIDEwMDB2LTMwMGgzMDB2MzAwaC0zMDB6TTUwIDUwMGg5MDBxMjEgMCAzNS41IC0xNC41dDE0LjUgLTM1LjV2LTQwMCBxMCAtMjEgLTE0LjUgLTM1LjV0LTM1LjUgLTE0LjVoLTkwMHEtMjEgMCAtMzUuNSAxNC41dC0xNC41IDM1LjV2NDAwcTAgMjEgMTQuNSAzNS41dDM1LjUgMTQuNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjUwOyIgZD0iTTg2NSA1NjVsLTQ5NCAtNDk0cS0yMyAtMjMgLTQxIC0yM3EtMTQgMCAtMjIgMTMuNXQtOCAzOC41djEwMDBxMCAyNSA4IDM4LjV0MjIgMTMuNXExOCAwIDQxIC0yM2w0OTQgLTQ5NHExNCAtMTQgMTQgLTM1dC0xNCAtMzV6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTI1MTsiIGQ9Ik0zMzUgNjM1bDQ5NCA0OTRxMjkgMjkgNTAgMjAuNXQyMSAtNDkuNXYtMTAwMHEwIC00MSAtMjEgLTQ5LjV0LTUwIDIwLjVsLTQ5NCA0OTRxLTE0IDE0IC0xNCAzNXQxNCAzNXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjUyOyIgZD0iTTEwMCA5MDBoMTAwMHE0MSAwIDQ5LjUgLTIxdC0yMC41IC01MGwtNDk0IC00OTRxLTE0IC0xNCAtMzUgLTE0dC0zNSAxNGwtNDk0IDQ5NHEtMjkgMjkgLTIwLjUgNTB0NDkuNSAyMXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjUzOyIgZD0iTTYzNSA4NjVsNDk0IC00OTRxMjkgLTI5IDIwLjUgLTUwdC00OS41IC0yMWgtMTAwMHEtNDEgMCAtNDkuNSAyMXQyMC41IDUwbDQ5NCA0OTRxMTQgMTQgMzUgMTR0MzUgLTE0eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyNTQ7IiBkPSJNNzAwIDc0MXYtMTgybC02OTIgLTMyM3YyMjFsNDEzIDE5M2wtNDEzIDE5M3YyMjF6TTEyMDAgMGgtODAwdjIwMGg4MDB2LTIwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjU1OyIgZD0iTTEyMDAgOTAwaC0yMDB2LTEwMGgyMDB2LTEwMGgtMzAwdjMwMGgyMDB2MTAwaC0yMDB2MTAwaDMwMHYtMzAwek0wIDcwMGg1MHEwIDIxIDQgMzd0OS41IDI2LjV0MTggMTcuNXQyMiAxMXQyOC41IDUuNXQzMSAydDM3IDAuNWgxMDB2LTU1MHEwIC0yMiAtMjUgLTM0LjV0LTUwIC0xMy41bC0yNSAtMnYtMTAwaDQwMHYxMDBxLTQgMCAtMTEgMC41dC0yNCAzdC0zMCA3dC0yNCAxNXQtMTEgMjQuNXY1NTBoMTAwcTI1IDAgMzcgLTAuNXQzMSAtMiB0MjguNSAtNS41dDIyIC0xMXQxOCAtMTcuNXQ5LjUgLTI2LjV0NCAtMzdoNTB2MzAwaC04MDB2LTMwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjU2OyIgZD0iTTgwMCA3MDBoLTUwcTAgMjEgLTQgMzd0LTkuNSAyNi41dC0xOCAxNy41dC0yMiAxMXQtMjguNSA1LjV0LTMxIDJ0LTM3IDAuNWgtMTAwdi01NTBxMCAtMjIgMjUgLTM0LjV0NTAgLTE0LjVsMjUgLTF2LTEwMGgtNDAwdjEwMHE0IDAgMTEgMC41dDI0IDN0MzAgN3QyNCAxNXQxMSAyNC41djU1MGgtMTAwcS0yNSAwIC0zNyAtMC41dC0zMSAtMnQtMjguNSAtNS41dC0yMiAtMTF0LTE4IC0xNy41dC05LjUgLTI2LjV0LTQgLTM3aC01MHYzMDAgaDgwMHYtMzAwek0xMTAwIDIwMGgtMjAwdi0xMDBoMjAwdi0xMDBoLTMwMHYzMDBoMjAwdjEwMGgtMjAwdjEwMGgzMDB2LTMwMHoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjU3OyIgZD0iTTcwMSAxMDk4aDE2MHExNiAwIDIxIC0xMXQtNyAtMjNsLTQ2NCAtNDY0bDQ2NCAtNDY0cTEyIC0xMiA3IC0yM3QtMjEgLTExaC0xNjBxLTEzIDAgLTIzIDlsLTQ3MSA0NzFxLTcgOCAtNyAxOHQ3IDE4bDQ3MSA0NzFxMTAgOSAyMyA5eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGUyNTg7IiBkPSJNMzM5IDEwOThoMTYwcTEzIDAgMjMgLTlsNDcxIC00NzFxNyAtOCA3IC0xOHQtNyAtMThsLTQ3MSAtNDcxcS0xMCAtOSAtMjMgLTloLTE2MHEtMTYgMCAtMjEgMTF0NyAyM2w0NjQgNDY0bC00NjQgNDY0cS0xMiAxMiAtNyAyM3QyMSAxMXoiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjU5OyIgZD0iTTEwODcgODgycTExIC01IDExIC0yMXYtMTYwcTAgLTEzIC05IC0yM2wtNDcxIC00NzFxLTggLTcgLTE4IC03dC0xOCA3bC00NzEgNDcxcS05IDEwIC05IDIzdjE2MHEwIDE2IDExIDIxdDIzIC03bDQ2NCAtNDY0bDQ2NCA0NjRxMTIgMTIgMjMgN3oiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlMjYwOyIgZD0iTTYxOCA5OTNsNDcxIC00NzFxOSAtMTAgOSAtMjN2LTE2MHEwIC0xNiAtMTEgLTIxdC0yMyA3bC00NjQgNDY0bC00NjQgLTQ2NHEtMTIgLTEyIC0yMyAtN3QtMTEgMjF2MTYwcTAgMTMgOSAyM2w0NzEgNDcxcTggNyAxOCA3dDE4IC03eiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeGY4ZmY7IiBkPSJNMTAwMCAxMjAwcTAgLTEyNCAtODggLTIxMnQtMjEyIC04OHEwIDEyNCA4OCAyMTJ0MjEyIDg4ek00NTAgMTAwMGgxMDBxMjEgMCA0MCAtMTR0MjYgLTMzbDc5IC0xOTRxNSAxIDE2IDNxMzQgNiA1NCA5LjV0NjAgN3Q2NS41IDF0NjEgLTEwdDU2LjUgLTIzdDQyLjUgLTQydDI5IC02NHQ1IC05MnQtMTkuNSAtMTIxLjVxLTEgLTcgLTMgLTE5LjV0LTExIC01MHQtMjAuNSAtNzN0LTMyLjUgLTgxLjV0LTQ2LjUgLTgzdC02NCAtNzAgdC04Mi41IC01MHEtMTMgLTUgLTQyIC01dC02NS41IDIuNXQtNDcuNSAyLjVxLTE0IDAgLTQ5LjUgLTMuNXQtNjMgLTMuNXQtNDMuNSA3cS01NyAyNSAtMTA0LjUgNzguNXQtNzUgMTExLjV0LTQ2LjUgMTEydC0yNiA5MGwtNyAzNXEtMTUgNjMgLTE4IDExNXQ0LjUgODguNXQyNiA2NHQzOS41IDQzLjV0NTIgMjUuNXQ1OC41IDEzdDYyLjUgMnQ1OS41IC00LjV0NTUuNSAtOGwtMTQ3IDE5MnEtMTIgMTggLTUuNSAzMHQyNy41IDEyeiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDFmNTExOyIgZD0iTTI1MCAxMjAwaDYwMHEyMSAwIDM1LjUgLTE0LjV0MTQuNSAtMzUuNXYtNDAwcTAgLTIxIC0xNC41IC0zNS41dC0zNS41IC0xNC41aC0xNTB2LTUwMGwtMjU1IC0xNzhxLTE5IC05IC0zMiAtMXQtMTMgMjl2NjUwaC0xNTBxLTIxIDAgLTM1LjUgMTQuNXQtMTQuNSAzNS41djQwMHEwIDIxIDE0LjUgMzUuNXQzNS41IDE0LjV6TTQwMCAxMTAwdi0xMDBoMzAwdjEwMGgtMzAweiIgLz4KPGdseXBoIHVuaWNvZGU9IiYjeDFmNmFhOyIgZD0iTTI1MCAxMjAwaDc1MHEzOSAwIDY5LjUgLTQwLjV0MzAuNSAtODQuNXYtOTMzbC03MDAgLTExN3Y5NTBsNjAwIDEyNWgtNzAwdi0xMDAwaC0xMDB2MTAyNXEwIDIzIDE1LjUgNDl0MzQuNSAyNnpNNTAwIDUyNXYtMTAwbDEwMCAyMHYxMDB6IiAvPgo8L2ZvbnQ+CjwvZGVmcz48L3N2Zz4g\";\n},function(e,t,n){function r(e,t){for(var n=0;n<e.length;n++){var r=e[n],o=d[r.id];if(o){o.refs++;for(var i=0;i<o.parts.length;i++)o.parts[i](r.parts[i]);for(;i<r.parts.length;i++)o.parts.push(s(r.parts[i],t))}else{for(var a=[],i=0;i<r.parts.length;i++)a.push(s(r.parts[i],t));d[r.id]={id:r.id,refs:1,parts:a}}}}function o(e){for(var t=[],n={},r=0;r<e.length;r++){var o=e[r],i=o[0],a=o[1],s=o[2],l=o[3],c={css:a,media:s,sourceMap:l};n[i]?n[i].parts.push(c):t.push(n[i]={id:i,parts:[c]})}return t}function i(){var e=document.createElement(\"style\"),t=g();return e.type=\"text/css\",t.appendChild(e),e}function a(){var e=document.createElement(\"link\"),t=g();return e.rel=\"stylesheet\",t.appendChild(e),e}function s(e,t){var n,r,o;if(t.singleton){var s=m++;n=f||(f=i()),r=l.bind(null,n,s,!1),o=l.bind(null,n,s,!0)}else e.sourceMap&&\"function\"==typeof URL&&\"function\"==typeof URL.createObjectURL&&\"function\"==typeof URL.revokeObjectURL&&\"function\"==typeof Blob&&\"function\"==typeof btoa?(n=a(),r=u.bind(null,n),o=function(){n.parentNode.removeChild(n),n.href&&URL.revokeObjectURL(n.href)}):(n=i(),r=c.bind(null,n),o=function(){n.parentNode.removeChild(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}function l(e,t,n,r){var o=n?\"\":r.css;if(e.styleSheet)e.styleSheet.cssText=A(t,o);else{var i=document.createTextNode(o),a=e.childNodes;a[t]&&e.removeChild(a[t]),a.length?e.insertBefore(i,a[t]):e.appendChild(i)}}function c(e,t){var n=t.css,r=t.media;t.sourceMap;if(r&&e.setAttribute(\"media\",r),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function u(e,t){var n=t.css,r=(t.media,t.sourceMap);r&&(n+=\"\\n/*# sourceMappingURL=data:application/json;base64,\"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+\" */\");var o=new Blob([n],{type:\"text/css\"}),i=e.href;e.href=URL.createObjectURL(o),i&&URL.revokeObjectURL(i)}var d={},p=function(e){var t;return function(){return\"undefined\"==typeof t&&(t=e.apply(this,arguments)),t}},h=p(function(){return/msie [6-9]\\b/.test(window.navigator.userAgent.toLowerCase())}),g=p(function(){return document.head||document.getElementsByTagName(\"head\")[0]}),f=null,m=0;e.exports=function(e,t){t=t||{},\"undefined\"==typeof t.singleton&&(t.singleton=h());var n=o(e);return r(n,t),function(e){for(var i=[],a=0;a<n.length;a++){var s=n[a],l=d[s.id];l.refs--,i.push(l)}if(e){var c=o(e);r(c,t)}for(var a=0;a<i.length;a++){var l=i[a];if(0===l.refs){for(var u=0;u<l.parts.length;u++)l.parts[u]();delete d[l.id]}}}};var A=function(){var e=[];return function(t,n){return e[t]=n,e.filter(Boolean).join(\"\\n\")}}()},function(e,t,n){var r=n(15);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\":root{--z-max:1000000100;--z-modal:1051;--z-modal-max:1059;--z-cm:999999;--z-menu:1001;--z-ctx-menu:99999999;--c-default:#000;--c-active:#fff;--c-btn:#fff;--c-heavy:#333;--c-editor:#eee;--c-forbidden:olive;--c-disabled:#aaa;--c-link:#337ab7;--c-link-hover:#23527c;--c-border:#ccc;--c-error:red;--c-risk:#f66;--c-danger:#ff3a90;--c-gray:gray;--c-thin:#555;--c-ok:#5bbd72;--c-has:#000093;--c-warn:#5c3b00;--c-warn-active:#ff0;--c-cobalt:#fff;--c-post:#4f91cf;--c-del:#ce5442;--c-head:#55b499;--c-options:#f0ad4e;--c-trace:#5bc0de;--c-patch:#8f58af;--c-put:#e49b3b;--c-tag:#46b8da;--c-tagx:#eea236;--c-rule-filter:#845dc4;--c-rule-props:#ff9e59;--c-req:#0d8f0d;--c-res:#e75b0f;--c-tree-success:#3c763d;--c-tree-info:#31708f;--c-tree-warning:#8a6d3b;--c-tree-danger:#a94442;--c-tree-forbidden:#aa6708;--c-tree-mark:#3b88fd;--c-j0:#002b36;--c-j1:#073642;--c-j2:#586e75;--c-j3:#657b83;--c-j4:#839496;--c-j5:#93a1a1;--c-j6:#eee8d5;--c-j7:#fdf6e3;--c-j8:#dc322f;--c-j9:#cb4b16;--c-ja:#b58900;--c-jb:#859900;--c-jc:#2aa198;--c-jd:#268bd2;--c-je:#6c71c4;--c-jf:#d33682;--c-success:#3c763d;--c-primary:#2e6da4;--c-primary-hover:#23527c;--c-primary-active:#1d4566;--c-danger-hover:#ac2925;--c-danger-active:#761c19;--c-warning:#eea236;--c-warning-hover:#d58512;--c-warning-active:#985f0d;--c-info:#46b8da;--c-info-hover:#269abc;--c-info-active:#1b6d85;--b-success:#dff0d8;--b-success-hover:#d0e9c6;--b-warning:#f0ad4e;--b-warning-hover:#ec971f;--b-warning-active:#d58512;--b-info:#5bc0de;--b-info-hover:#31b0d5;--b-info-active:#269abc;--b-primary:#337ab7;--b-primary-hover:#286090;--b-primary-active:#204d74;--b-danger:#d9534f;--b-danger-hover:#c9302c;--b-danger-active:#ac2925;--b-alert-info:#d9edf7;--b-default:#fff;--b-error:#f2dede;--b-error-hover:#dab9b9;--b-active:#ddd;--b-gray:#ccc;--b-btn-hover:#e6e6e6;--b-btn-active:#d6e2fb;--b-hover:#f5f5f5;--b-heavy:#eee;--b-heavy-active:#e0e0e0;--b-disabled:#f5f5f5;--b-bar:#fafafa;--b-mark:#3b88fd;--b-title:#f1f3f4;--b-img:#0e0e0e;--b-frames:#ff8000;--b-prop:#e1e3e6;--b-ok:#5bbd72;--b-editor:#002240;--b-filtered:#ffffe0;--b-warn:#fffbe6;--b-req:#e2f7da;--b-req-hover:#cee7c0;--b-blink:#ffeb3b;--b-tree-active:#8fbc8f;--b-tl-dns:#8cd2c6;--b-tl-req:#fdfdb2;--b-tl-res:#fbb361;--b-tl-load:#7eabe1;--b-list-hover:#e8e8e8;--b-list-warn:#f8efcf;--b-list-info:#c5e2f2;--b-list-danger-hover:#e8c9c9;--b-list-mark:#2978f0;--b-modal:rgba(0,0,0,.5);--b-cobalt:#002240;--s-rgba5:rgba(0,0,0,.05);--s-rgba8:rgba(0,0,0,.08);--s-rgba12:rgba(0,0,0,.12);--s-rgba15:rgba(0,0,0,.15);--s-rgba20:rgba(0,0,0,.2);--s-fc:rgba(102,175,233,.6);--b-rgba50:rgba(0,0,0,.5);--b-rgba70:rgba(0,0,0,.7);--b-rgba15:rgba(0,0,0,.15);--b-rgba30:rgba(0,0,0,.3)}.main,body,html{margin:0;padding:0;width:100%;height:100%;background:var(--b-default);color:var(--c-heavy)}#container{min-width:1078px;min-height:35pc}::-webkit-scrollbar{width:10px;height:7px}::-webkit-scrollbar-button{width:10px;height:1px}::-webkit-scrollbar-thumb{background-clip:padding-box;background-color:var(--b-rgba50);border-radius:8px;min-height:30px}::-webkit-scrollbar-thumb:hover{background-clip:padding-box;background-color:var(--b-rgba70);border-radius:8px}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border-left:2px solid transparent;border-right:2px solid transparent}::-webkit-scrollbar-track:hover{background-clip:padding-box;background-color:var(--s-rgba15)}body::-webkit-scrollbar{height:0}.cm-error-rule{text-decoration:line-through!important;color:#af2018!important;font-style:italic}pre{background:transparent;border:none;padding:0;margin:0;font-size:9pt;font-family:inherit;display:block;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}.n-monospace{font-family:monospace}body .CodeMirror pre{font-family:consolas,monospace}input[type=checkbox],input[type=radio]{margin:0;vertical-align:text-bottom}.glyphicon-ban-circle{color:var(--c-risk)}.w-filter-input{width:100%;background:var(--b-img);height:30px;color:var(--c-cobalt);padding:2px 5px;border:none;border-top:1px solid var(--c-border);border-bottom:1px solid var(--c-border)}.w-shadow{border:1px solid var(--c-border)!important;box-shadow:0 6px 9pt var(--s-rgba20)!important;border-radius:1px!important}textarea[readonly]{outline:0}textarea{resize:none;display:block;font-size:9pt}.hide{display:none!important}.visible{visibility:visible!important}.w-hidden{visibility:hidden!important}.box,.v-box{display:-webkit-box;display:-moz-box;display:box}.v-box{-moz-box-orient:vertical;-webkit-box-orient:vertical;box-orient:vertical}.fill{-moz-box-flex:1;-webkit-box-flex:1;box-flex:1}.box>.fill{width:0}.v-box>.fill{height:0}.table{table-layout:fixed;margin:0!important}.table .w-empty{font-style:italic;border-bottom:none!important;padding:20px 0!important}.table .w-empty,.w-empty-data{text-align:center;color:var(--c-disabled)}.w-empty-data{position:absolute;width:75pt;top:50%;left:50%;margin:-20px 0 0 -50px}.w-expand-collapse{margin-left:5px;height:20px;line-height:14px;font-size:9pt;color:var(--c-default)}.modal-dialog .modal-body,.modal-dialog .modal-footer{padding:10px}.modal-footer .btn{padding:5px 10px}.cm-s-ambiance ::-webkit-scrollbar-thumb,.cm-s-blackboard ::-webkit-scrollbar-thumb,.cm-s-cobalt ::-webkit-scrollbar-thumb,.cm-s-dark ::-webkit-scrollbar-thumb,.cm-s-erlang-dark ::-webkit-scrollbar-thumb,.cm-s-lesser-dark ::-webkit-scrollbar-thumb,.cm-s-midnight ::-webkit-scrollbar-thumb,.cm-s-monokai ::-webkit-scrollbar-thumb,.cm-s-night ::-webkit-scrollbar-thumb,.cm-s-twilight ::-webkit-scrollbar-thumb,.cm-s-vibrant-ink ::-webkit-scrollbar-thumb,.cm-s-xq-dark ::-webkit-scrollbar-thumb{background-color:hsla(0,0%,100%,.5)}.cm-s-ambiance ::-webkit-scrollbar-thumb:hover,.cm-s-blackboard ::-webkit-scrollbar-thumb:hover,.cm-s-cobalt ::-webkit-scrollbar-thumb:hover,.cm-s-dark ::-webkit-scrollbar-thumb:hover,.cm-s-erlang-dark ::-webkit-scrollbar-thumb:hover,.cm-s-lesser-dark ::-webkit-scrollbar-thumb:hover,.cm-s-midnight ::-webkit-scrollbar-thumb:hover,.cm-s-monokai ::-webkit-scrollbar-thumb:hover,.cm-s-night ::-webkit-scrollbar-thumb:hover,.cm-s-twilight ::-webkit-scrollbar-thumb:hover,.cm-s-vibrant-ink ::-webkit-scrollbar-thumb:hover,.cm-s-xq-dark ::-webkit-scrollbar-thumb:hover{background-color:hsla(0,0%,100%,.7)}.cm-s-ambiance ::-webkit-scrollbar-track:hover,.cm-s-blackboard ::-webkit-scrollbar-track:hover,.cm-s-cobalt ::-webkit-scrollbar-track:hover,.cm-s-dark ::-webkit-scrollbar-track:hover,.cm-s-erlang-dark ::-webkit-scrollbar-track:hover,.cm-s-lesser-dark ::-webkit-scrollbar-track:hover,.cm-s-midnight ::-webkit-scrollbar-track:hover,.cm-s-monokai ::-webkit-scrollbar-track:hover,.cm-s-night ::-webkit-scrollbar-track:hover,.cm-s-twilight ::-webkit-scrollbar-track:hover,.cm-s-vibrant-ink ::-webkit-scrollbar-track:hover,.cm-s-xq-dark ::-webkit-scrollbar-track:hover{background-color:hsla(0,0%,100%,.15)}.w-hide{display:none!important}.w-offline-status .w-left-menu a,.w-offline-status .w-menu a{color:var(--c-disabled)!important}body .cm-s-cobalt span.cm-number{color:#ff1605}.w-hide-no-value .w-no-value{display:none!important}.w-detail .w-dock-btn{border:none;background:transparent;padding:4px 0;width:20px;text-align:center;position:absolute;right:0;top:0;outline:0}.w-detail .w-dock-btn .glyphicon{margin:0!important}.w-detail .w-dock-btn:hover{background:var(--b-hover)}.btn.active,.btn:active{box-shadow:none!important}.CodeMirror-hints{z-index:var(--z-cm)!important}.w-mark td,.w-mark th{background-color:var(--b-mark)!important;color:var(--c-active)!important}.ReactVirtualized__Grid.ReactVirtualized__List{outline:0}.w-not-allowed{cursor:not-allowed}a{cursor:pointer}.w-delete{color:var(--c-risk)!important}.w-delete:hover{color:var(--c-error)!important}.w-editor-dialog .modal-dialog{width:60pc}.w-editor-dialog .modal-body{background:var(--b-bar)}.w-editor-dialog textarea{width:940px;height:520px;padding:5px;border-radius:3px;border:1px solid var(--c-border)}.w-big-editor-dialog .modal-dialog{width:750pt}.w-big-editor-dialog textarea{width:980px;height:550px}.w-win-dialog{z-index:calc(var(--z-modal-max) + 1)}.w-win-dialog .modal-dialog{width:5in}.w-win-dialog pre{font-size:15px;padding:9pt 10px;line-height:2;margin:0;word-break:break-word}.w-win-dialog .modal-body{margin:0}.w-gray{color:var(--c-gray);margin-left:5px}.w-tabs>li>a{padding:5px 10px}.w-tabs>li>a>span{margin-right:5px}.modal.in{overflow-x:hidden;overflow-y:auto}.glyphicon-cloud{vertical-align:text-bottom;padding-bottom:1px;margin-right:3px}.glyphicon-gift{margin-right:3px}.glyphicon-folder-open{margin-right:6px}.w-fake-iframe{display:inline-block;vertical-align:top}.w-fix-drag{position:relative}.w-fix-drag:after{content:'';position:absolute;top:0;left:0;right:0;bottom:0}.w-fix-drag:hover:after{display:none}.modal-header h4{font-size:15px;font-weight:500;display:inline}.w-align-items{display:flex!important;align-items:center}.flex-1{flex:1}.w-align-items input,.w-fmt-btn .glyphicon{margin-right:5px}.w-cell-img{max-height:17px;max-width:20px;margin:-3px 6px 0 0}.inline-align-items{display:inline-flex;align-items:center}.w-shake-horizontal{animation:shakeHorizontal .5s ease-in-out}@keyframes shakeHorizontal{0%,to{transform:translateX(0)}20%,60%{transform:translateX(-5px)}40%,80%{transform:translateX(5px)}}select.form-control{box-shadow:none}.w-bold{font-weight:700!important}.w-help-icon{color:var(--c-heavy);font-size:14px;margin-right:5px}.w-help-icon:hover{color:var(--c-link);text-decoration:none}\",\"\"])},function(e,t,n){var r=n(17);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".table-hover>tbody>tr:hover{background-color:var(--b-hover)}.nav-tabs{border-bottom:1px solid var(--c-border)}.nav-tabs>li>a:hover{background-color:var(--b-heavy);border-color:var(--c-border)}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:var(--c-thin);background:var(--b-default);border:1px solid var(--c-border);border-bottom-color:transparent}.modal-header{border-bottom:1px solid var(--c-border)}.modal-content{position:relative;background-color:var(--b-default);border:1px solid var(--c-border)}.modal-footer{border-top:1px solid var(--c-border)}.form-control{background-color:var(--b-default);border:1px solid var(--c-border);color:var(--c-thin)}.alert-danger{color:var(--c-danger);background-color:var(--b-error);border:none}.alert-warning{color:var(--c-tree-warning);background-color:var(--b-warn);border:none}.alert-info{color:var(--c-tree-info);background-color:var(--b-alert-info);border:none}.alert-success{color:var(--c-success);background-color:var(--b-success);border:none}.table>tbody>tr>td,.table>tbody>tr>th,.table>thead>tr>td,.table>thead>tr>th{border-top:1px solid var(--c-border)}.table>thead>tr>th{border-bottom:1px solid var(--c-border)}legend,pre{color:var(--c-heavy)}a{color:var(--c-link)}a:focus,a:hover{color:var(--c-link-hover)}.close{color:var(--c-gray);text-shadow:none}.close,.close:focus,.close:hover{filter:alpha(opacity=100);opacity:1}.close:focus,.close:hover{color:var(--c-default)}.btn-default{color:var(--c-heavy);background-color:var(--b-default);border-color:var(--c-border)}.dropdown-menu{background-color:var(--c-default);border:1px solid var(--c-border);box-shadow:0 6px 9pt var(--s-rgba15)}.btn-default.active,.btn-default:active,.btn-default:focus,.btn-default:hover,.w-tabs-sm .btn-default.active:focus,.w-tabs-sm .btn-default.active:hover,.w-tabs-sm .btn-default:active:focus,.w-tabs-sm .btn-default:active:hover{color:var(--c-heavy);background-color:var(--b-btn-hover);border-color:var(--c-border)}.btn-default.active:focus,.btn-default.active:hover,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:var(--c-heavy);background-color:var(--b-btn-active);border-color:var(--c-border)}.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover{background-color:var(--b-default);border-color:var(--c-border)}.btn-primary{color:var(--c-btn);background-color:var(--b-primary);border-color:var(--c-primary)}.btn-primary.active,.btn-primary:active,.btn-primary:focus,.btn-primary:hover{color:var(--c-btn);background-color:var(--b-primary-hover);border-color:var(--c-primary)}.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active:focus,.btn-primary:active:hover{color:var(--c-btn);background-color:var(--b-primary-active);border-color:var(--c-primary)}.btn-danger{color:var(--c-btn);background-color:var(--b-danger);border-color:var(--c-danger)}.btn-danger.active,.btn-danger:active,.btn-danger:focus,.btn-danger:hover{color:var(--c-btn);background-color:var(--b-danger-hover);border-color:var(--c-danger-hover)}.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active:focus,.btn-danger:active:hover{color:var(--c-btn);background-color:var(--b-danger-active);border-color:var(--c-danger-active)}.btn-warning{color:var(--c-btn);background-color:var(--b-warning);border-color:var(--c-warning)}.btn-warning.active,.btn-warning:active,.btn-warning:focus,.btn-warning:hover{color:var(--c-btn);background-color:var(--b-warning-hover);border-color:var(--c-warning-hover)}.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active:focus,.btn-warning:active:hover{color:var(--c-btn);background-color:var(--b-warning-active);border-color:var(--c-warning-active)}.btn-info{color:var(--c-btn);background-color:var(--b-info);border-color:var(--c-info)}.btn-info.active,.btn-info:active,.btn-info:focus,.btn-info:hover{color:var(--c-btn);background-color:var(--b-info-hover);border-color:var(--c-info-hover)}.btn-info.active:focus,.btn-info.active:hover,.btn-info:active:focus,.btn-info:active:hover{color:var(--c-btn);background-color:var(--b-info-active);border-color:var(--c-info-active)}.table>tbody>tr.success>td,.table>tbody>tr.success>th{background-color:var(--b-success)}.table>tbody>tr.active>td,.table>tbody>tr.active>th{background-color:var(--b-hover)}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th{background-color:var(--b-error)}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th{background-color:var(--b-warn)}.table>tbody>tr.info>td,.table>tbody>tr.info>th{background:var(--b-alert-info)}.dropdown-menu{background-color:var(--b-bar)}.dropdown-menu .divider{background:var(--c-border)}.dropdown-menu>li>a{color:var(--c-heavy)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:var(--c-heavy);text-decoration:none;background-color:var(--b-hover)}.modal-content{border:1px solid var(--c-border);box-shadow:0 5px 15px var(--s-rgba50)}@media (min-width:768px){.modal-content{box-shadow:0 5px 15px var(--s-rgba50)}}.modal-backdrop.in{filter:alpha(opacity=100);opacity:1;background:var(--b-modal)}select.form-control[disabled]{background:var(--b-disabled)}.form-control{background:field}.w-json-tree label{font-weight:400}\",\"\"])},function(e,t,n){var r,o;/*!\n\t * jQuery JavaScript Library v4.0.0\n\t * https://jquery.com/\n\t *\n\t * Copyright OpenJS Foundation and other contributors\n\t * Released under the MIT license\n\t * https://jquery.com/license/\n\t *\n\t * Date: 2026-01-18T00:20Z\n\t */\n!function(t,n){\"use strict\";\"object\"==typeof e&&\"object\"==typeof e.exports?e.exports=n(t,!0):n(t)}(\"undefined\"!=typeof window?window:this,function(n,i){\"use strict\";function a(e){return null==e?e+\"\":\"object\"==typeof e?Ke[Ze.call(e)]||\"object\":typeof e}function s(e){return null!=e&&e===e.window}function l(e){var t=!!e&&e.length,n=a(e);return\"function\"==typeof e||s(e)?!1:\"array\"===n||0===t||\"number\"==typeof t&&t>0&&t-1 in e}function c(e,t,n){n=n||rt;var r,o=n.createElement(\"script\");o.text=e;for(r in ot)t&&t[r]&&(o[r]=t[r]);n.head.appendChild(o).parentNode&&o.parentNode.removeChild(o)}function u(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function d(){function e(n,r){return t.push(n+\" \")>st.expr.cacheLength&&delete e[t.shift()],e[n+\" \"]=r}var t=[];return e}function p(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}function h(e){return e.replace(xt,Tt)}function g(e){st.error(\"Syntax error, unrecognized expression: \"+e)}function f(e,t){var n,r,o,i,a,s,l,c=Nt[e+\" \"];if(c)return t?0:c.slice(0);for(a=e,s=[],l=st.expr.preFilter;a;){(!n||(r=Ct.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),s.push(o=[])),n=!1,(r=gt.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(pt,\" \")}),a=a.slice(n.length));for(i in bt)!(r=st.expr.match[i].exec(a))||l[i]&&!(r=l[i](r))||(n=r.shift(),o.push({value:n,type:i,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?g(e):Nt(e,s).slice(0)}function m(e){for(var t=0,n=e.length,r=\"\";n>t;t++)r+=e[t].value;return r}function A(e,t,n,r,o,i,s){var l=0,c=e.length,u=null==n;if(\"object\"===a(n)){o=!0;for(l in n)A(e,t,l,n[l],!0,i,s)}else if(void 0!==r&&(o=!0,\"function\"!=typeof r&&(s=!0),u&&(s?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(st(e),n)})),t))for(;c>l;l++)t(e[l],n,s?r:r.call(e[l],l,t(e[l],n)));return o?e:u?t.call(e):c?t(e[0],n):i}function M(e,t){return t?\"\\x00\"===e?\"�\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e}function w(e,t){if(e===t)return St=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n?e==rt||e.ownerDocument==rt&&st.contains(rt,e)?-1:t==rt||t.ownerDocument==rt&&st.contains(rt,t)?1:0:4&n?-1:1)}function v(e,t,n,r){var o,i,a,s,l,c,u,d=t&&t.ownerDocument,h=t?t.nodeType:9;if(n=n||[],\"string\"!=typeof e||!e||1!==h&&9!==h&&11!==h)return n;if(!r&&(N(t),t=t||Bt,zt)){if(11!==h&&(l=_t.exec(e)))if(o=l[1]){if(9===h)return(a=t.getElementById(o))&&qe.call(n,a),n;if(d&&(a=d.getElementById(o))&&st.contains(t,a))return qe.call(n,a),n}else{if(l[2])return qe.apply(n,t.getElementsByTagName(e)),n;if((o=l[3])&&t.getElementsByClassName)return qe.apply(n,t.getElementsByClassName(o)),n}if(!(Vt[e+\" \"]||dt&&dt.test(e))){if(u=e,d=t,1===h&&(ft.test(e)||gt.test(e))){for(d=mt.test(e)&&p(t.parentNode)||t,(d!=t||ut)&&((s=t.getAttribute(\"id\"))?s=st.escapeSelector(s):t.setAttribute(\"id\",s=st.expando)),c=f(e),i=c.length;i--;)c[i]=(s?\"#\"+s:\":scope\")+\" \"+m(c[i]);u=c.join(\",\")}try{return qe.apply(n,d.querySelectorAll(u)),n}catch(g){Vt(e,!0)}finally{s===st.expando&&t.removeAttribute(\"id\")}}}return R(e.replace(pt,\"$1\"),t,n,r)}function b(e){return e[st.expando]=!0,e}function y(e){return function(t){return u(t,\"input\")&&t.type===e}}function x(e){return function(t){return(u(t,\"input\")||u(t,\"button\"))&&t.type===e}}function T(e){return function(t){return\"form\"in t?t.parentNode&&t.disabled===!1?\"label\"in t?\"label\"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Jt(t)===e:t.disabled===e:\"label\"in t?t.disabled===e:!1}}function C(e){return b(function(t){return t=+t,b(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function N(e){var t,n=e?e.ownerDocument||e:rt;n!=Bt&&9===n.nodeType&&(Bt=n,Rt=Bt.documentElement,zt=!st.isXMLDoc(Bt),ut&&rt!=Bt&&(t=Bt.defaultView)&&t.top!==t&&t.addEventListener(\"unload\",qt))}function I(){}function E(e,t,n){var r=t.dir,o=t.next,i=o||r,a=n&&\"parentNode\"===i,s=Ot++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||a)return e(t,n,o);return!1}:function(t,n,l){var c,d,p=[Qt,s];if(l){for(;t=t[r];)if((1===t.nodeType||a)&&e(t,n,l))return!0}else for(;t=t[r];)if(1===t.nodeType||a)if(d=t[st.expando]||(t[st.expando]={}),o&&u(t,o))t=t[r]||t;else{if((c=d[i])&&c[0]===Qt&&c[1]===s)return p[2]=c[2];if(d[i]=p,p[2]=e(t,n,l))return!0}return!1}}function D(e){return e.length>1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function S(e,t,n){for(var r=0,o=t.length;o>r;r++)v(e,t[r],n);return n}function L(e,t,n,r,o){for(var i,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(i=e[s])&&(!n||n(i,r,o))&&(a.push(i),c&&t.push(s));return a}function k(e,t,n,r,o,i){return r&&!r[st.expando]&&(r=k(r)),o&&!o[st.expando]&&(o=k(o,i)),b(function(i,a,s,l){var c,u,d,p,h=[],g=[],f=a.length,m=i||S(t||\"*\",s.nodeType?[s]:s,[]),A=!e||!i&&t?m:L(m,h,e,s,l);if(n?(p=o||(i?e:f||r)?[]:a,n(A,p,s,l)):p=A,r)for(c=L(p,g),r(c,[],s,l),u=c.length;u--;)(d=c[u])&&(p[g[u]]=!(A[g[u]]=d));if(i){if(o||e){if(o){for(c=[],u=p.length;u--;)(d=p[u])&&c.push(A[u]=d);o(null,p=[],c,l)}for(u=p.length;u--;)(d=p[u])&&(c=o?Je.call(i,d):h[u])>-1&&(i[c]=!(a[c]=d))}}else p=L(p===a?p.splice(f,p.length):p),o?o(null,a,p,l):qe.apply(a,p)})}function j(e){for(var t,n,r,o=e.length,i=st.expr.relative[e[0].type],a=i||st.expr.relative[\" \"],s=i?1:0,l=E(function(e){return e===t},a,!0),c=E(function(e){return Je.call(t,e)>-1},a,!0),u=[function(e,n,r){var o=!i&&(r||n!=Ut)||((t=n).nodeType?l(e,n,r):c(e,n,r));return t=null,o}];o>s;s++)if(n=st.expr.relative[e[s].type])u=[E(D(u),n)];else{if(n=st.expr.filter[e[s].type].apply(null,e[s].matches),n[st.expando]){for(r=++s;o>r&&!st.expr.relative[e[r].type];r++);return k(s>1&&D(u),s>1&&m(e.slice(0,s-1).concat({value:\" \"===e[s-2].type?\"*\":\"\"})).replace(pt,\"$1\"),n,r>s&&j(e.slice(s,r)),o>r&&j(e=e.slice(r)),o>r&&m(e))}u.push(n)}return D(u)}function U(e,t){var n=t.length>0,r=e.length>0,o=function(o,i,a,s,l){var c,u,d,p=0,h=\"0\",g=o&&[],f=[],m=Ut,A=o||r&&st.expr.find.TAG(\"*\",l),M=Qt+=null==m?1:Math.random()||.1;for(l&&(Ut=i==Bt||i||l);null!=(c=A[h]);h++){if(r&&c){for(u=0,i||c.ownerDocument==Bt||(N(c),a=!zt);d=e[u++];)if(d(c,i||Bt,a)){qe.call(s,c);break}l&&(Qt=M)}n&&((c=!d&&c)&&p--,o&&g.push(c))}if(p+=h,n&&h!==p){for(u=0;d=t[u++];)d(g,f,i,a);if(o){if(p>0)for(;h--;)g[h]||f[h]||(f[h]=lt.call(s));f=L(f)}qe.apply(s,f),l&&!o&&f.length>0&&p+t.length>1&&st.uniqueSort(s)}return l&&(Qt=M,Ut=m),g};return n?b(o):o}function B(e,t){var n,r=[],o=[],i=Ft[e+\" \"];if(!i){for(t||(t=f(e)),n=t.length;n--;)i=j(t[n]),i[st.expando]?r.push(i):o.push(i);i=Ft(e,U(o,r)),i.selector=e}return i}function R(e,t,n,r){var o,i,a,s,l,c=\"function\"==typeof e&&e,u=!r&&f(e=c.selector||e);if(n=n||[],1===u.length){if(i=u[0]=u[0].slice(0),i.length>2&&\"ID\"===(a=i[0]).type&&9===t.nodeType&&zt&&st.expr.relative[i[1].type]){if(t=(st.expr.find.ID(h(a.matches[0]),t)||[])[0],!t)return n;c&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=Gt.needsContext.test(e)?0:i.length;o--&&(a=i[o],!st.expr.relative[s=a.type]);)if((l=st.expr.find[s])&&(r=l(h(a.matches[0]),mt.test(i[0].type)&&p(t.parentNode)||t))){if(i.splice(o,1),e=r.length&&m(i),!e)return qe.apply(n,r),n;break}}return(c||B(e,u))(r,t,!zt,n,!t||mt.test(e)&&p(t.parentNode)||t),n}function z(e,t,n){for(var r=[],o=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(o&&st(e).is(n))break;r.push(e)}return r}function Q(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}function O(e){return\"<\"===e[0]&&\">\"===e[e.length-1]&&e.length>=3}function H(e,t,n){return\"function\"==typeof t?st.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?st.grep(e,function(e){return e===t!==n}):\"string\"!=typeof t?st.grep(e,function(e){return Je.call(t,e)>-1!==n}):st.filter(t,e,n)}function F(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function V(e){var t={};return st.each(e.match(Et)||[],function(e,n){t[n]=!0}),t}function P(e){return e}function Y(e){throw e}function G(e,t,n,r){var o;try{e&&\"function\"==typeof(o=e.promise)?o.call(e).done(t).fail(n):e&&\"function\"==typeof(o=e.then)?o.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n(e)}}function W(){rt.removeEventListener(\"DOMContentLoaded\",W),n.removeEventListener(\"load\",W),st.ready()}function X(e,t){return t.toUpperCase()}function _(e){return e.replace(sn,X)}function q(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType}function J(){this.expando=st.expando+J.uid++}function K(e){return\"true\"===e?!0:\"false\"===e?!1:\"null\"===e?null:e===+e+\"\"?+e:un.test(e)?JSON.parse(e):e}function Z(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r=\"data-\"+t.replace(dn,\"-$&\").toLowerCase(),n=e.getAttribute(r),\"string\"==typeof n){try{n=K(n)}catch(o){}cn.set(e,t,n)}else n=void 0;return n}function $(e,t){return e=t||e,\"none\"===e.style.display||\"\"===e.style.display&&\"none\"===st.css(e,\"display\")}function ee(e){return fn.test(e)&&mn.test(e[0].toUpperCase()+e.slice(1))}function te(e,t,n,r){var o,i,a=20,s=r?function(){return r.cur()}:function(){return st.css(e,t,\"\")},l=s(),c=n&&n[3]||(ee(t)?\"px\":\"\"),u=e.nodeType&&(!ee(t)||\"px\"!==c&&+l)&&hn.exec(st.css(e,t));if(u&&u[3]!==c){for(l/=2,c=c||u[3],u=+l||1;a--;)st.style(e,t,u+c),(1-i)*(1-(i=s()/l||.5))<=0&&(a=0),u/=i;u=2*u,st.style(e,t,u+c),n=n||[]}return n&&(u=+u||+l||0,o=n[1]?u+(n[1]+1)*n[2]:+n[2],r&&(r.unit=c,r.start=u,r.end=o)),o}function ne(e){return _(e.replace(An,\"ms-\"))}function re(e){var t,n=e.ownerDocument,r=e.nodeName,o=Mn[r];return o?o:(t=n.body.appendChild(n.createElement(r)),o=st.css(t,\"display\"),t.parentNode.removeChild(t),\"none\"===o&&(o=\"block\"),Mn[r]=o,o)}function oe(e,t){for(var n,r,o=[],i=0,a=e.length;a>i;i++)r=e[i],r.style&&(n=r.style.display,t?(\"none\"===n&&(o[i]=ln.get(r,\"display\")||null,o[i]||(r.style.display=\"\")),\"\"===r.style.display&&$(r)&&(o[i]=re(r))):\"none\"!==n&&(o[i]=\"none\",ln.set(r,\"display\",n)));for(i=0;a>i;i++)null!=o[i]&&(e[i].style.display=o[i]);return e}function ie(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?Ge.slice.call(e.getElementsByTagName(t||\"*\")):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&u(e,t)?st.merge([e],n):n}function ae(e,t){for(var n=0,r=e.length;r>n;n++)ln.set(e[n],\"globalEval\",!t||ln.get(t[n],\"globalEval\"))}function se(e,t,n,r,o){for(var i,s,c,u,d,p,h=t.createDocumentFragment(),g=[],f=0,m=e.length;m>f;f++)if(i=e[f],i||0===i)if(\"object\"===a(i)&&(i.nodeType||l(i)))st.merge(g,i.nodeType?[i]:i);else if(Tn.test(i)){for(s=s||h.appendChild(t.createElement(\"div\")),c=(bn.exec(i)||[\"\",\"\"])[1].toLowerCase(),u=yn[c]||Ge,p=u.length;--p>-1;)s=s.appendChild(t.createElement(u[p]));s.innerHTML=st.htmlPrefilter(i),st.merge(g,s.childNodes),s=h.firstChild,s.textContent=\"\"}else g.push(t.createTextNode(i));for(h.textContent=\"\",f=0;i=g[f++];)if(r&&st.inArray(i,r)>-1)o&&o.push(i);else if(d=wn(i),s=ie(h.appendChild(i),\"script\"),d&&ae(s),n)for(p=0;i=s[p++];)xn.test(i.type||\"\")&&n.push(i);return h}function le(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function ce(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function ue(e,t,n,r){t=_e(t);var o,i,a,s,l,u,d=0,p=e.length,h=p-1,g=t[0],f=\"function\"==typeof g;if(f)return e.each(function(o){var i=e.eq(o);t[0]=g.call(this,o,i.html()),ue(i,t,n,r)});if(p&&(o=se(t,e[0].ownerDocument,!1,e,r),i=o.firstChild,1===o.childNodes.length&&(o=i),i||r)){for(a=st.map(ie(o,\"script\"),le),s=a.length;p>d;d++)l=o,d!==h&&(l=st.clone(l,!0,!0),s&&st.merge(a,ie(l,\"script\"))),n.call(e[d],l,d);if(s)for(u=a[a.length-1].ownerDocument,st.map(a,ce),d=0;s>d;d++)l=a[d],xn.test(l.type||\"\")&&!ln.get(l,\"globalEval\")&&st.contains(u,l)&&(l.src&&\"module\"!==(l.type||\"\").toLowerCase()?st._evalUrl&&!l.noModule&&st._evalUrl(l.src,{nonce:l.nonce,crossOrigin:l.crossOrigin},u):c(l.textContent,l,u))}return e}function de(){return!0}function pe(){return!1}function he(e,t,n,r,o,i){var a,s;if(\"object\"==typeof t){\"string\"!=typeof n&&(r=r||n,n=void 0);for(s in t)he(e,s,n,r,t[s],i);return e}if(null==r&&null==o?(o=n,r=n=void 0):null==o&&(\"string\"==typeof n?(o=r,r=void 0):(o=r,r=n,n=void 0)),o===!1)o=pe;else if(!o)return e;return 1===i&&(a=o,o=function(e){return st().off(e),a.apply(this,arguments)},o.guid=a.guid||(a.guid=st.guid++)),e.each(function(){st.event.add(this,t,o,r,n)})}function ge(e,t,n){return n?(ln.set(e,t,!1),void st.event.add(e,t,{namespace:!1,handler:function(e){var n,r=ln.get(this,t);if(1&e.isTrigger&&this[t]){if(r.length)(st.event.special[t]||{}).delegateType&&e.stopPropagation();else if(r=Xe.call(arguments),ln.set(this,t,r),this[t](),n=ln.get(this,t),ln.set(this,t,!1),r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n&&n.value}else r.length&&(ln.set(this,t,{value:st.event.trigger(r[0],r.slice(1),this)}),e.stopPropagation(),e.isImmediatePropagationStopped=de)}})):void(void 0===ln.get(e,t)&&st.event.add(e,t,de))}function fe(e,t){return u(e,\"table\")&&u(11!==t.nodeType?t:t.firstChild,\"tr\")?st(e).children(\"tbody\")[0]||e:e}function me(e,t){var n,r,o,i=ln.get(e,\"events\");if(1===t.nodeType){if(i){ln.remove(t,\"handle events\");for(n in i)for(r=0,o=i[n].length;o>r;r++)st.event.add(t,n,i[n][r])}cn.hasData(e)&&cn.set(t,st.extend({},cn.get(e)))}}function Ae(e,t,n){for(var r,o=t?st.filter(t,e):e,i=0;null!=(r=o[i]);i++)n||1!==r.nodeType||st.cleanData(ie(r)),r.parentNode&&(n&&wn(r)&&ae(ie(r,\"script\")),r.parentNode.removeChild(r));return e}function Me(e){var t=e.ownerDocument.defaultView;return t||(t=n),t.getComputedStyle(e)}function we(e,t,n){var r,o,i={};for(o in t)i[o]=e.style[o],e.style[o]=t[o];r=n.call(e);for(o in t)e.style[o]=i[o];return r}function ve(e,t,n){var r,o=Ln.test(t);return n=n||Me(e),n&&(r=n.getPropertyValue(t)||n[t],o&&r&&(r=r.replace(pt,\"$1\")||void 0),\"\"!==r||wn(e)||(r=st.style(e,t))),void 0!==r?r+\"\":r}function be(e){for(var t=e[0].toUpperCase()+e.slice(1),n=kn.length;n--;)if(e=kn[n]+t,e in jn)return e}function ye(e){return e in jn?e:be(e)||e}function xe(){if(Un&&Un.style){var e,t=rt.createElement(\"col\"),r=rt.createElement(\"tr\"),o=rt.createElement(\"td\");if(Un.style.cssText=\"position:absolute;left:-11111px;border-collapse:separate;border-spacing:0\",r.style.cssText=\"box-sizing:content-box;border:1px solid;height:1px\",o.style.cssText=\"height:9px;width:9px;padding:0\",t.span=2,At.appendChild(Un).appendChild(t).parentNode.appendChild(r).appendChild(o).parentNode.appendChild(o.cloneNode(!0)),0===Un.offsetWidth)return void At.removeChild(Un);e=n.getComputedStyle(r),Dn=ut||18===Math.round(parseFloat(n.getComputedStyle(t).width)),En=Math.round(parseFloat(e.height)+parseFloat(e.borderTopWidth)+parseFloat(e.borderBottomWidth))===r.offsetHeight,At.removeChild(Un),Un=null}}function Te(e,t,n){var r=hn.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||\"px\"):t}function Ce(e,t,n,r,o,i){var a=\"width\"===t?1:0,s=0,l=0,c=0;if(n===(r?\"border\":\"content\"))return 0;for(;4>a;a+=2)\"margin\"===n&&(c+=st.css(e,n+gn[a],!0,o)),r?(\"content\"===n&&(l-=st.css(e,\"padding\"+gn[a],!0,o)),\"margin\"!==n&&(l-=st.css(e,\"border\"+gn[a]+\"Width\",!0,o))):(l+=st.css(e,\"padding\"+gn[a],!0,o),\"padding\"!==n?l+=st.css(e,\"border\"+gn[a]+\"Width\",!0,o):s+=st.css(e,\"border\"+gn[a]+\"Width\",!0,o));return!r&&i>=0&&(l+=Math.max(0,Math.ceil(e[\"offset\"+t[0].toUpperCase()+t.slice(1)]-i-l-s-.5))||0),l+c}function Ne(e,t,n){var r=Me(e),o=ut||n,i=o&&\"border-box\"===st.css(e,\"boxSizing\",!1,r),a=i,s=ve(e,t,r),l=\"offset\"+t[0].toUpperCase()+t.slice(1);if(Sn.test(s)){if(!n)return s;s=\"auto\"}return(\"auto\"===s||ut&&i||!nt.reliableColDimensions()&&u(e,\"col\")||!nt.reliableTrDimensions()&&u(e,\"tr\"))&&e.getClientRects().length&&(i=\"border-box\"===st.css(e,\"boxSizing\",!1,r),a=l in e,a&&(s=e[l])),s=parseFloat(s)||0,s+Ce(e,t,n||(i?\"border\":\"content\"),a,r,s)+\"px\"}function Ie(e,t,n,r,o){return new Ie.prototype.init(e,t,n,r,o)}function Ee(){Qn&&(rt.hidden===!1&&n.requestAnimationFrame?n.requestAnimationFrame(Ee):n.setTimeout(Ee,13),st.fx.tick())}function De(){return n.setTimeout(function(){zn=void 0}),zn=Date.now()}function Se(e,t){var n,r=0,o={height:e};for(t=t?1:0;4>r;r+=2-t)n=gn[r],o[\"margin\"+n]=o[\"padding\"+n]=e;return t&&(o.opacity=o.width=e),o}function Le(e,t,n){for(var r,o=(Ue.tweeners[t]||[]).concat(Ue.tweeners[\"*\"]),i=0,a=o.length;a>i;i++)if(r=o[i].call(n,t,e))return r}function ke(e,t,n){var r,o,i,a,s,l,c,u,d=\"width\"in t||\"height\"in t,p=this,h={},g=e.style,f=e.nodeType&&$(e),m=ln.get(e,\"fxshow\");n.queue||(a=st._queueHooks(e,\"fx\"),null==a.unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,st.queue(e,\"fx\").length||a.empty.fire()})}));for(r in t)if(o=t[r],On.test(o)){if(delete t[r],i=i||\"toggle\"===o,o===(f?\"hide\":\"show\")){if(\"show\"!==o||!m||void 0===m[r])continue;f=!0}h[r]=m&&m[r]||st.style(e,r)}if(l=!st.isEmptyObject(t),l||!st.isEmptyObject(h)){d&&1===e.nodeType&&(n.overflow=[g.overflow,g.overflowX,g.overflowY],c=m&&m.display,null==c&&(c=ln.get(e,\"display\")),u=st.css(e,\"display\"),\"none\"===u&&(c?u=c:(oe([e],!0),c=e.style.display||c,u=st.css(e,\"display\"),oe([e]))),(\"inline\"===u||\"inline-block\"===u&&null!=c)&&\"none\"===st.css(e,\"float\")&&(l||(p.done(function(){g.display=c}),null==c&&(u=g.display,c=\"none\"===u?\"\":u)),g.display=\"inline-block\")),n.overflow&&(g.overflow=\"hidden\",p.always(function(){g.overflow=n.overflow[0],g.overflowX=n.overflow[1],g.overflowY=n.overflow[2]})),l=!1;for(r in h)l||(m?\"hidden\"in m&&(f=m.hidden):m=ln.set(e,\"fxshow\",{display:c}),i&&(m.hidden=!f),f&&oe([e],!0),p.done(function(){f||oe([e]),ln.remove(e,\"fxshow\");for(r in h)st.style(e,r,h[r])})),l=Le(f?m[r]:0,r,p),r in m||(m[r]=l.start,f&&(l.end=l.start,l.start=0))}}function je(e,t){var n,r,o,i,a;for(n in e)if(r=ne(n),o=t[r],i=e[n],Array.isArray(i)&&(o=i[1],i=e[n]=i[0]),n!==r&&(e[r]=i,delete e[n]),a=st.cssHooks[r],a&&\"expand\"in a){i=a.expand(i),delete e[r];for(n in i)n in e||(e[n]=i[n],t[n]=o)}else t[r]=o}function Ue(e,t,n){var r,o,i=0,a=Ue.prefilters.length,s=st.Deferred().always(function(){delete l.elem}),l=function(){if(o)return!1;for(var t=zn||De(),n=Math.max(0,c.startTime+c.duration-t),r=1-(n/c.duration||0),i=0,a=c.tweens.length;a>i;i++)c.tweens[i].run(r);return s.notifyWith(e,[c,r,n]),1>r&&a?n:(a||s.notifyWith(e,[c,1,0]),s.resolveWith(e,[c]),!1)},c=s.promise({elem:e,props:st.extend({},t),opts:st.extend(!0,{specialEasing:{},easing:st.easing._default},n),originalProperties:t,originalOptions:n,startTime:zn||De(),duration:n.duration,tweens:[],createTween:function(t,n){var r=st.Tween(e,c.opts,t,n,c.opts.specialEasing[t]||c.opts.easing);return c.tweens.push(r),r},stop:function(t){var n=0,r=t?c.tweens.length:0;if(o)return this;for(o=!0;r>n;n++)c.tweens[n].run(1);return t?(s.notifyWith(e,[c,1,0]),s.resolveWith(e,[c,t])):s.rejectWith(e,[c,t]),this}}),u=c.props;for(je(u,c.opts.specialEasing);a>i;i++)if(r=Ue.prefilters[i].call(c,e,u,c.opts))return\"function\"==typeof r.stop&&(st._queueHooks(c.elem,c.opts.queue).stop=r.stop.bind(r)),r;return st.map(u,Le,c),\"function\"==typeof c.opts.start&&c.opts.start.call(e,c),c.progress(c.opts.progress).done(c.opts.done,c.opts.complete).fail(c.opts.fail).always(c.opts.always),st.fx.timer(st.extend(l,{elem:e,anim:c,queue:c.opts.queue})),c}function Be(e){var t=e.match(Et)||[];return t.join(\" \")}function Re(e){return e.getAttribute&&e.getAttribute(\"class\")||\"\"}function ze(e){return Array.isArray(e)?e:\"string\"==typeof e?e.match(Et)||[]:[]}function Qe(e,t,n,r){var o;if(Array.isArray(t))st.each(t,function(t,o){n||_n.test(e)?r(e,o):Qe(e+\"[\"+(\"object\"==typeof o&&null!=o?t:\"\")+\"]\",o,n,r)});else if(n||\"object\"!==a(t))r(e,t);else for(o in t)Qe(e+\"[\"+o+\"]\",t[o],n,r)}function Oe(e){return function(t,n){\"string\"!=typeof t&&(n=t,t=\"*\");var r,o=0,i=t.toLowerCase().match(Et)||[];if(\"function\"==typeof n)for(;r=i[o++];)\"+\"===r[0]?(r=r.slice(1)||\"*\",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function He(e,t,n,r){function o(s){var l;return i[s]=!0,st.each(e[s]||[],function(e,s){var c=s(t,n,r);return\"string\"!=typeof c||a||i[c]?a?!(l=c):void 0:(t.dataTypes.unshift(c),o(c),!1)}),l}var i={},a=e===ar;return o(t.dataTypes[0])||!i[\"*\"]&&o(\"*\")}function Fe(e,t){var n,r,o=st.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((o[n]?e:r||(r={}))[n]=t[n]);return r&&st.extend(!0,e,r),e}function Ve(e,t,n){for(var r,o,i,a,s=e.contents,l=e.dataTypes;\"*\"===l[0];)l.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader(\"Content-Type\"));if(r)for(o in s)if(s[o]&&s[o].test(r)){l.unshift(o);break}if(l[0]in n)i=l[0];else{for(o in n){if(!l[0]||e.converters[o+\" \"+l[0]]){i=o;break}a||(a=o)}i=i||a}return i?(i!==l[0]&&l.unshift(i),n[i]):void 0}function Pe(e,t,n,r){var o,i,a,s,l,c={},u=e.dataTypes.slice();if(u[1])for(a in e.converters)c[a.toLowerCase()]=e.converters[a];for(i=u.shift();i;)if(e.responseFields[i]&&(n[e.responseFields[i]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=i,i=u.shift())if(\"*\"===i)i=l;else if(\"*\"!==l&&l!==i){if(a=c[l+\" \"+i]||c[\"* \"+i],!a)for(o in c)if(s=o.split(\" \"),s[1]===i&&(a=c[l+\" \"+s[0]]||c[\"* \"+s[0]])){a===!0?a=c[o]:c[o]!==!0&&(i=s[0],u.unshift(s[1]));break}if(a!==!0)if(a&&e[\"throws\"])t=a(t);else try{t=a(t)}catch(d){return{state:\"parsererror\",error:a?d:\"No conversion from \"+l+\" to \"+i}}}return{state:\"success\",data:t}}function Ye(e){return e.scriptAttrs||!e.headers&&(e.crossDomain||e.async&&st.inArray(\"json\",e.dataTypes)<0)}if(!n.document)throw new Error(\"jQuery requires a window with a document\");var Ge=[],We=Object.getPrototypeOf,Xe=Ge.slice,_e=Ge.flat?function(e){return Ge.flat.call(e)}:function(e){return Ge.concat.apply([],e)},qe=Ge.push,Je=Ge.indexOf,Ke={},Ze=Ke.toString,$e=Ke.hasOwnProperty,et=$e.toString,tt=et.call(Object),nt={},rt=n.document,ot={type:!0,src:!0,nonce:!0,noModule:!0},it=\"4.0.0\",at=/HTML$/i,st=function(e,t){return new st.fn.init(e,t)};st.fn=st.prototype={jquery:it,constructor:st,length:0,toArray:function(){return Xe.call(this)},get:function(e){return null==e?Xe.call(this):0>e?this[e+this.length]:this[e]},pushStack:function(e){var t=st.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return st.each(this,e)},map:function(e){return this.pushStack(st.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(Xe.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(st.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(st.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()}},st.extend=st.fn.extend=function(){var e,t,n,r,o,i,a=arguments[0]||{},s=1,l=arguments.length,c=!1;for(\"boolean\"==typeof a&&(c=a,a=arguments[s]||{},s++),\"object\"!=typeof a&&\"function\"!=typeof a&&(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],\"__proto__\"!==t&&a!==r&&(c&&r&&(st.isPlainObject(r)||(o=Array.isArray(r)))?(n=a[t],i=o&&!Array.isArray(n)?[]:o||st.isPlainObject(n)?n:{},o=!1,a[t]=st.extend(c,i,r)):void 0!==r&&(a[t]=r));return a},st.extend({expando:\"jQuery\"+(it+Math.random()).replace(/\\D/g,\"\"),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return e&&\"[object Object]\"===Ze.call(e)?(t=We(e))?(n=$e.call(t,\"constructor\")&&t.constructor,\"function\"==typeof n&&et.call(n)===tt):!0:!1},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){c(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(l(e))for(n=e.length;n>r&&t.call(e[r],r,e[r])!==!1;r++);else for(r in e)if(t.call(e[r],r,e[r])===!1)break;return e},text:function(e){var t,n=\"\",r=0,o=e.nodeType;if(!o)for(;t=e[r++];)n+=st.text(t);return 1===o||11===o?e.textContent:9===o?e.documentElement.textContent:3===o||4===o?e.nodeValue:n},makeArray:function(e,t){var n=t||[];return null!=e&&(l(Object(e))?st.merge(n,\"string\"==typeof e?[e]:e):qe.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:Je.call(t,e,n)},isXMLDoc:function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!at.test(t||n&&n.nodeName||\"HTML\")},contains:function(e,t){var n=t&&t.parentNode;return e===n||!(!n||1!==n.nodeType||!(e.contains?e.contains(n):e.compareDocumentPosition&&16&e.compareDocumentPosition(n)))},merge:function(e,t){for(var n=+t.length,r=0,o=e.length;n>r;r++)e[o++]=t[r];return e.length=o,e},grep:function(e,t,n){for(var r,o=[],i=0,a=e.length,s=!n;a>i;i++)r=!t(e[i],i),r!==s&&o.push(e[i]);return o},map:function(e,t,n){var r,o,i=0,a=[];if(l(e))for(r=e.length;r>i;i++)o=t(e[i],i,n),null!=o&&a.push(o);else for(i in e)o=t(e[i],i,n),null!=o&&a.push(o);return _e(a)},guid:1,support:nt}),\"function\"==typeof Symbol&&(st.fn[Symbol.iterator]=Ge[Symbol.iterator]),st.each(\"Boolean Number String Function Array Date RegExp Object Error Symbol\".split(\" \"),function(e,t){Ke[\"[object \"+t+\"]\"]=t.toLowerCase()});var lt=Ge.pop,ct=\"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",ut=rt.documentMode,dt=ut&&new RegExp(\":enabled|:disabled|\\\\[\"+ct+\"*name\"+ct+\"*=\"+ct+\"*(?:''|\\\"\\\")\"),pt=new RegExp(\"^\"+ct+\"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\"+ct+\"+$\",\"g\"),ht=\"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\"+ct+\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\x00-\\\\x7f])+\",gt=new RegExp(\"^\"+ct+\"*([>+~]|\"+ct+\")\"+ct+\"*\"),ft=new RegExp(ct+\"|>\"),mt=/[+~]/,At=rt.documentElement,Mt=At.matches||At.msMatchesSelector,wt=\"\\\\[\"+ct+\"*(\"+ht+\")(?:\"+ct+\"*([*^$|!~]?=)\"+ct+\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\"+ht+\"))|)\"+ct+\"*\\\\]\",vt=\":(\"+ht+\")(?:\\\\((('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\"+wt+\")*)|.*)\\\\)|)\",bt={ID:new RegExp(\"^#(\"+ht+\")\"),CLASS:new RegExp(\"^\\\\.(\"+ht+\")\"),TAG:new RegExp(\"^(\"+ht+\"|[*])\"),ATTR:new RegExp(\"^\"+wt),PSEUDO:new RegExp(\"^\"+vt),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+ct+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+ct+\"*(?:([+-]|)\"+ct+\"*(\\\\d+)|))\"+ct+\"*\\\\)|)\",\"i\")},yt=new RegExp(vt),xt=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+ct+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),Tt=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320)},Ct=new RegExp(\"^\"+ct+\"*,\"+ct+\"*\"),Nt=d(),It={ATTR:function(e){return e[1]=h(e[1]),e[3]=h(e[3]||e[4]||e[5]||\"\"),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||g(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&g(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return bt.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&yt.test(n)&&(t=f(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},Et=/[^\\x20\\t\\r\\n\\f]+/g;st.fn.extend({attr:function(e,t){return A(this,st.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){st.removeAttr(this,e)})}}),st.extend({attr:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return\"undefined\"==typeof e.getAttribute?st.prop(e,t,n):(1===i&&st.isXMLDoc(e)||(o=st.attrHooks[t.toLowerCase()]),void 0!==n?null===n||n===!1&&0!==t.toLowerCase().indexOf(\"aria-\")?void st.removeAttr(e,t):o&&\"set\"in o&&void 0!==(r=o.set(e,n,t))?r:(e.setAttribute(t,n),n):o&&\"get\"in o&&null!==(r=o.get(e,t))?r:(r=e.getAttribute(t),null==r?void 0:r))},attrHooks:{},removeAttr:function(e,t){var n,r=0,o=t&&t.match(Et);if(o&&1===e.nodeType)for(;n=o[r++];)e.removeAttribute(n)}}),ut&&(st.attrHooks.type={set:function(e,t){if(\"radio\"===t&&u(e,\"input\")){var n=e.value;return e.setAttribute(\"type\",t),n&&(e.value=n),t}}});var Dt=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\x80-\\uFFFF\\w-]/g;st.escapeSelector=function(e){return(e+\"\").replace(Dt,M)};var St,Lt=Ge.sort,kt=Ge.splice;st.uniqueSort=function(e){var t,n=[],r=0,o=0;if(St=!1,Lt.call(e,w),St){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)kt.call(e,n[r],1)}return e},st.fn.uniqueSort=function(){return this.pushStack(st.uniqueSort(Xe.apply(this)))};var jt,Ut,Bt,Rt,zt,Qt=0,Ot=0,Ht=d(),Ft=d(),Vt=d(),Pt=new RegExp(ct+\"+\",\"g\"),Yt=new RegExp(\"^\"+ht+\"$\"),Gt=st.extend({needsContext:new RegExp(\"^\"+ct+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+ct+\"*((?:-\\\\d)?\\\\d*)\"+ct+\"*\\\\)|)(?=[^-]|$)\",\"i\")},bt),Wt=/^(?:input|select|textarea|button)$/i,Xt=/^h\\d$/i,_t=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,qt=function(){N()},Jt=E(function(e){return e.disabled===!0&&u(e,\"fieldset\")},{dir:\"parentNode\",next:\"legend\"});v.matches=function(e,t){return v(e,null,null,t)},v.matchesSelector=function(e,t){if(N(e),zt&&!Vt[t+\" \"]&&(!dt||!dt.test(t)))try{return Mt.call(e,t)}catch(n){Vt(t,!0)}return v(t,Bt,null,[e]).length>0},st.expr={cacheLength:50,createPseudo:b,match:Gt,find:{ID:function(e,t){if(\"undefined\"!=typeof t.getElementById&&zt){var n=t.getElementById(e);return n?[n]:[]}},TAG:function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},CLASS:function(e,t){return\"undefined\"!=typeof t.getElementsByClassName&&zt?t.getElementsByClassName(e):void 0}},relative:{\">\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:It,filter:{ID:function(e){var t=h(e);return function(e){return e.getAttribute(\"id\")===t}},TAG:function(e){var t=h(e).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return u(e,t)}},CLASS:function(e){var t=Ht[e+\" \"];return t||(t=new RegExp(\"(^|\"+ct+\")\"+e+\"(\"+ct+\"|$)\"))&&Ht(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(e,t,n){return function(r){var o=st.attr(r,e);return null==o?\"!=\"===t:t?(o+=\"\",\"=\"===t?o===n:\"!=\"===t?o!==n:\"^=\"===t?n&&0===o.indexOf(n):\"*=\"===t?n&&o.indexOf(n)>-1:\"$=\"===t?n&&o.slice(-n.length)===n:\"~=\"===t?(\" \"+o.replace(Pt,\" \")+\" \").indexOf(n)>-1:\"|=\"===t?o===n||o.slice(0,n.length+1)===n+\"-\":!1):!0}},CHILD:function(e,t,n,r,o){var i=\"nth\"!==e.slice(0,3),a=\"last\"!==e.slice(-4),s=\"of-type\"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,l){var c,d,p,h,g,f=i!==a?\"nextSibling\":\"previousSibling\",m=t.parentNode,A=s&&t.nodeName.toLowerCase(),M=!l&&!s,w=!1;if(m){if(i){for(;f;){for(p=t;p=p[f];)if(s?u(p,A):1===p.nodeType)return!1;g=f=\"only\"===e&&!g&&\"nextSibling\"}return!0}if(g=[a?m.firstChild:m.lastChild],a&&M){for(d=m[st.expando]||(m[st.expando]={}),c=d[e]||[],h=c[0]===Qt&&c[1],w=h&&c[2],p=h&&m.childNodes[h];p=++h&&p&&p[f]||(w=h=0)||g.pop();)if(1===p.nodeType&&++w&&p===t){d[e]=[Qt,h,w];break}}else if(M&&(d=t[st.expando]||(t[st.expando]={}),c=d[e]||[],h=c[0]===Qt&&c[1],w=h),w===!1)for(;(p=++h&&p&&p[f]||(w=h=0)||g.pop())&&((s?!u(p,A):1!==p.nodeType)||!++w||(M&&(d=p[st.expando]||(p[st.expando]={}),d[e]=[Qt,w]),p!==t)););return w-=o,w===r||w%r===0&&w/r>=0}}},PSEUDO:function(e,t){var n=st.expr.pseudos[e]||st.expr.setFilters[e.toLowerCase()]||g(\"unsupported pseudo: \"+e);return n[st.expando]?n(t):n}},pseudos:{not:b(function(e){var t=[],n=[],r=B(e.replace(pt,\"$1\"));return r[st.expando]?b(function(e,t,n,o){for(var i,a=r(e,null,o,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),t[0]=null,!n.pop()}}),has:b(function(e){return function(t){return v(e,t).length>0}}),contains:b(function(e){return e=h(e),function(t){return(t.textContent||st.text(t)).indexOf(e)>-1}}),lang:b(function(e){return Yt.test(e||\"\")||g(\"unsupported lang: \"+e),e=h(e).toLowerCase(),function(t){var n;do if(n=zt?t.lang:t.getAttribute(\"xml:lang\")||t.getAttribute(\"lang\"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+\"-\");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===Rt},focus:function(e){return e===Bt.activeElement&&Bt.hasFocus()&&!!(e.type||e.href||~e.tabIndex)},enabled:T(!1),disabled:T(!0),checked:function(e){return u(e,\"input\")&&!!e.checked||u(e,\"option\")&&!!e.selected},selected:function(e){return ut&&e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!st.expr.pseudos.empty(e);\n},header:function(e){return Xt.test(e.nodeName)},input:function(e){return Wt.test(e.nodeName)},button:function(e){return u(e,\"input\")&&\"button\"===e.type||u(e,\"button\")},text:function(e){return u(e,\"input\")&&\"text\"===e.type},first:C(function(){return[0]}),last:C(function(e,t){return[t-1]}),eq:C(function(e,t,n){return[0>n?n+t:n]}),even:C(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:C(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:C(function(e,t,n){var r;for(r=0>n?n+t:n>t?t:n;--r>=0;)e.push(r);return e}),gt:C(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},st.expr.pseudos.nth=st.expr.pseudos.eq;for(jt in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})st.expr.pseudos[jt]=y(jt);for(jt in{submit:!0,reset:!0})st.expr.pseudos[jt]=x(jt);I.prototype=st.expr.pseudos,st.expr.setFilters=new I,N(),st.find=v,v.compile=B,v.select=R,v.setDocument=N,v.tokenize=f;var Kt=st.expr.match.needsContext,Zt=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;st.filter=function(e,t,n){var r=t[0];return n&&(e=\":not(\"+e+\")\"),1===t.length&&1===r.nodeType?st.find.matchesSelector(r,e)?[r]:[]:st.find.matches(e,st.grep(t,function(e){return 1===e.nodeType}))},st.fn.extend({find:function(e){var t,n,r=this.length,o=this;if(\"string\"!=typeof e)return this.pushStack(st(e).filter(function(){for(t=0;r>t;t++)if(st.contains(o[t],this))return!0}));for(n=this.pushStack([]),t=0;r>t;t++)st.find(e,o[t],n);return r>1?st.uniqueSort(n):n},filter:function(e){return this.pushStack(H(this,e||[],!1))},not:function(e){return this.pushStack(H(this,e||[],!0))},is:function(e){return!!H(this,\"string\"==typeof e&&Kt.test(e)?st(e):e||[],!1).length}});var $t,en=/^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/,tn=st.fn.init=function(e,t){var n,r;if(!e)return this;if(e.nodeType)return this[0]=e,this.length=1,this;if(\"function\"==typeof e)return void 0!==$t.ready?$t.ready(e):e(st);if(n=e+\"\",O(n))n=[null,e,null];else{if(\"string\"!=typeof e)return st.makeArray(e,this);n=en.exec(e)}if(!n||!n[1]&&t)return!t||t.jquery?(t||$t).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof st?t[0]:t,st.merge(this,st.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:rt,!0)),Zt.test(n[1])&&st.isPlainObject(t))for(n in t)\"function\"==typeof this[n]?this[n](t[n]):this.attr(n,t[n]);return this}return r=rt.getElementById(n[2]),r&&(this[0]=r,this.length=1),this};tn.prototype=st.fn,$t=st(rt);var nn=/^(?:parents|prev(?:Until|All))/,rn={children:!0,contents:!0,next:!0,prev:!0};st.fn.extend({has:function(e){var t=st(e,this),n=t.length;return this.filter(function(){for(var e=0;n>e;e++)if(st.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,o=this.length,i=[],a=\"string\"!=typeof e&&st(e);if(!Kt.test(e))for(;o>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&st.find.matchesSelector(n,e))){i.push(n);break}return this.pushStack(i.length>1?st.uniqueSort(i):i)},index:function(e){return e?\"string\"==typeof e?Je.call(st(e),this[0]):Je.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(st.uniqueSort(st.merge(this.get(),st(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),st.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return z(e,\"parentNode\")},parentsUntil:function(e,t,n){return z(e,\"parentNode\",n)},next:function(e){return F(e,\"nextSibling\")},prev:function(e){return F(e,\"previousSibling\")},nextAll:function(e){return z(e,\"nextSibling\")},prevAll:function(e){return z(e,\"previousSibling\")},nextUntil:function(e,t,n){return z(e,\"nextSibling\",n)},prevUntil:function(e,t,n){return z(e,\"previousSibling\",n)},siblings:function(e){return Q((e.parentNode||{}).firstChild,e)},children:function(e){return Q(e.firstChild)},contents:function(e){return null!=e.contentDocument&&We(e.contentDocument)?e.contentDocument:(u(e,\"template\")&&(e=e.content||e),st.merge([],e.childNodes))}},function(e,t){st.fn[e]=function(n,r){var o=st.map(this,t,n);return\"Until\"!==e.slice(-5)&&(r=n),r&&\"string\"==typeof r&&(o=st.filter(r,o)),this.length>1&&(rn[e]||st.uniqueSort(o),nn.test(e)&&o.reverse()),this.pushStack(o)}}),st.Callbacks=function(e){e=\"string\"==typeof e?V(e):st.extend({},e);var t,n,r,o,i=[],s=[],l=-1,c=function(){for(o=o||e.once,r=t=!0;s.length;l=-1)for(n=s.shift();++l<i.length;)i[l].apply(n[0],n[1])===!1&&e.stopOnFalse&&(l=i.length,n=!1);e.memory||(n=!1),t=!1,o&&(i=n?[]:\"\")},u={add:function(){return i&&(n&&!t&&(l=i.length-1,s.push(n)),function r(t){st.each(t,function(t,n){\"function\"==typeof n?e.unique&&u.has(n)||i.push(n):n&&n.length&&\"string\"!==a(n)&&r(n)})}(arguments),n&&!t&&c()),this},remove:function(){return st.each(arguments,function(e,t){for(var n;(n=st.inArray(t,i,n))>-1;)i.splice(n,1),l>=n&&l--}),this},has:function(e){return e?st.inArray(e,i)>-1:i.length>0},empty:function(){return i&&(i=[]),this},disable:function(){return o=s=[],i=n=\"\",this},disabled:function(){return!i},lock:function(){return o=s=[],n||t||(i=n=\"\"),this},locked:function(){return!!o},fireWith:function(e,n){return o||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||c()),this},fire:function(){return u.fireWith(this,arguments),this},fired:function(){return!!r}};return u},st.extend({Deferred:function(e){var t=[[\"notify\",\"progress\",st.Callbacks(\"memory\"),st.Callbacks(\"memory\"),2],[\"resolve\",\"done\",st.Callbacks(\"once memory\"),st.Callbacks(\"once memory\"),0,\"resolved\"],[\"reject\",\"fail\",st.Callbacks(\"once memory\"),st.Callbacks(\"once memory\"),1,\"rejected\"]],r=\"pending\",o={state:function(){return r},always:function(){return i.done(arguments).fail(arguments),this},\"catch\":function(e){return o.then(null,e)},pipe:function(){var e=arguments;return st.Deferred(function(n){st.each(t,function(t,r){var o=\"function\"==typeof e[r[4]]&&e[r[4]];i[r[1]](function(){var e=o&&o.apply(this,arguments);e&&\"function\"==typeof e.promise?e.promise().progress(n.notify).done(n.resolve).fail(n.reject):n[r[0]+\"With\"](this,o?[e]:arguments)})}),e=null}).promise()},then:function(e,r,o){function i(e,t,r,o){return function(){var s=this,l=arguments,c=function(){var n,c;if(!(a>e)){if(n=r.apply(s,l),n===t.promise())throw new TypeError(\"Thenable self-resolution\");c=n&&(\"object\"==typeof n||\"function\"==typeof n)&&n.then,\"function\"==typeof c?o?c.call(n,i(a,t,P,o),i(a,t,Y,o)):(a++,c.call(n,i(a,t,P,o),i(a,t,Y,o),i(a,t,P,t.notifyWith))):(r!==P&&(s=void 0,l=[n]),(o||t.resolveWith)(s,l))}},u=o?c:function(){try{c()}catch(n){st.Deferred.exceptionHook&&st.Deferred.exceptionHook(n,u.error),e+1>=a&&(r!==Y&&(s=void 0,l=[n]),t.rejectWith(s,l))}};e?u():(st.Deferred.getErrorHook&&(u.error=st.Deferred.getErrorHook()),n.setTimeout(u))}}var a=0;return st.Deferred(function(n){t[0][3].add(i(0,n,\"function\"==typeof o?o:P,n.notifyWith)),t[1][3].add(i(0,n,\"function\"==typeof e?e:P)),t[2][3].add(i(0,n,\"function\"==typeof r?r:Y))}).promise()},promise:function(e){return null!=e?st.extend(e,o):o}},i={};return st.each(t,function(e,n){var a=n[2],s=n[5];o[n[1]]=a.add,s&&a.add(function(){r=s},t[3-e][2].disable,t[3-e][3].disable,t[0][2].lock,t[0][3].lock),a.add(n[3].fire),i[n[0]]=function(){return i[n[0]+\"With\"](this===i?void 0:this,arguments),this},i[n[0]+\"With\"]=a.fireWith}),o.promise(i),e&&e.call(i,i),i},when:function(e){var t=arguments.length,n=t,r=Array(n),o=Xe.call(arguments),i=st.Deferred(),a=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?Xe.call(arguments):n,--t||i.resolveWith(r,o)}};if(1>=t&&(G(e,i.done(a(n)).resolve,i.reject,!t),\"pending\"===i.state()||\"function\"==typeof(o[n]&&o[n].then)))return i.then();for(;n--;)G(o[n],a(n),i.reject);return i.promise()}});var on=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;st.Deferred.exceptionHook=function(e,t){e&&on.test(e.name)&&n.console.warn(\"jQuery.Deferred exception\",e,t)},st.readyException=function(e){n.setTimeout(function(){throw e})};var an=st.Deferred();st.fn.ready=function(e){return an.then(e)[\"catch\"](function(e){st.readyException(e)}),this},st.extend({isReady:!1,readyWait:1,ready:function(e){(e===!0?--st.readyWait:st.isReady)||(st.isReady=!0,e!==!0&&--st.readyWait>0||an.resolveWith(rt,[st]))}}),st.ready.then=an.then,\"loading\"!==rt.readyState?n.setTimeout(st.ready):(rt.addEventListener(\"DOMContentLoaded\",W),n.addEventListener(\"load\",W));var sn=/-([a-z])/g;J.uid=1,J.prototype={cache:function(e){var t=e[this.expando];return t||(t=Object.create(null),q(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,o=this.cache(e);if(\"string\"==typeof t)o[_(t)]=n;else for(r in t)o[_(r)]=t[r];return n},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][_(t)]},access:function(e,t,n){return void 0===t||t&&\"string\"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){Array.isArray(t)?t=t.map(_):(t=_(t),t=t in r?[t]:t.match(Et)||[]),n=t.length;for(;n--;)delete r[t[n]]}(void 0===t||st.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!st.isEmptyObject(t)}};var ln=new J,cn=new J,un=/^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,dn=/[A-Z]/g;st.extend({hasData:function(e){return cn.hasData(e)||ln.hasData(e)},data:function(e,t,n){return cn.access(e,t,n)},removeData:function(e,t){cn.remove(e,t)},_data:function(e,t,n){return ln.access(e,t,n)},_removeData:function(e,t){ln.remove(e,t)}}),st.fn.extend({data:function(e,t){var n,r,o,i=this[0],a=i&&i.attributes;if(void 0===e){if(this.length&&(o=cn.get(i),1===i.nodeType&&!ln.get(i,\"hasDataAttrs\"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf(\"data-\")&&(r=_(r.slice(5)),Z(i,r,o[r])));ln.set(i,\"hasDataAttrs\",!0)}return o}return\"object\"==typeof e?this.each(function(){cn.set(this,e)}):A(this,function(t){var n;if(i&&void 0===t){if(n=cn.get(i,e),void 0!==n)return n;if(n=Z(i,e),void 0!==n)return n}else this.each(function(){cn.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){cn.remove(this,e)})}}),st.extend({queue:function(e,t,n){var r;return e?(t=(t||\"fx\")+\"queue\",r=ln.get(e,t),n&&(!r||Array.isArray(n)?r=ln.set(e,t,st.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||\"fx\";var n=st.queue(e,t),r=n.length,o=n.shift(),i=st._queueHooks(e,t),a=function(){st.dequeue(e,t)};\"inprogress\"===o&&(o=n.shift(),r--),o&&(\"fx\"===t&&n.unshift(\"inprogress\"),delete i.stop,o.call(e,a,i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+\"queueHooks\";return ln.get(e,n)||ln.set(e,n,{empty:st.Callbacks(\"once memory\").add(function(){ln.remove(e,[t+\"queue\",n])})})}}),st.fn.extend({queue:function(e,t){var n=2;return\"string\"!=typeof e&&(t=e,e=\"fx\",n--),arguments.length<n?st.queue(this[0],e):void 0===t?this:this.each(function(){var n=st.queue(this,e,t);st._queueHooks(this,e),\"fx\"===e&&\"inprogress\"!==n[0]&&st.dequeue(this,e)})},dequeue:function(e){return this.each(function(){st.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||\"fx\",[])},promise:function(e,t){var n,r=1,o=st.Deferred(),i=this,a=this.length,s=function(){--r||o.resolveWith(i,[i])};for(\"string\"!=typeof e&&(t=e,e=void 0),e=e||\"fx\";a--;)n=ln.get(i[a],e+\"queueHooks\"),n&&n.empty&&(r++,n.empty.add(s));return s(),o.promise(t)}});var pn=/[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/.source,hn=new RegExp(\"^(?:([+-])=|)(\"+pn+\")([a-z%]*)$\",\"i\"),gn=[\"Top\",\"Right\",\"Bottom\",\"Left\"],fn=/^[a-z]/,mn=/^(?:Border(?:Top|Right|Bottom|Left)?(?:Width|)|(?:Margin|Padding)?(?:Top|Right|Bottom|Left)?|(?:Min|Max)?(?:Width|Height))$/,An=/^-ms-/,Mn={};st.fn.extend({show:function(){return oe(this,!0)},hide:function(){return oe(this)},toggle:function(e){return\"boolean\"==typeof e?e?this.show():this.hide():this.each(function(){$(this)?st(this).show():st(this).hide()})}});var wn=function(e){return st.contains(e.ownerDocument,e)||e.getRootNode(vn)===e.ownerDocument},vn={composed:!0};At.getRootNode||(wn=function(e){return st.contains(e.ownerDocument,e)});var bn=/<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i,yn={thead:[\"table\"],col:[\"colgroup\",\"table\"],tr:[\"tbody\",\"table\"],td:[\"tr\",\"tbody\",\"table\"]};yn.tbody=yn.tfoot=yn.colgroup=yn.caption=yn.thead,yn.th=yn.td;var xn=/^$|^module$|\\/(?:java|ecma)script/i,Tn=/<|&#?\\w+;/,Cn=/^(?:checkbox|radio)$/i,Nn=/^([^.]*)(?:\\.(.+)|)/;st.event={add:function(e,t,n,r,o){var i,a,s,l,c,u,d,p,h,g,f,m=ln.get(e);if(q(e))for(n.handler&&(i=n,n=i.handler,o=i.selector),o&&st.find.matchesSelector(At,o),n.guid||(n.guid=st.guid++),(l=m.events)||(l=m.events=Object.create(null)),(a=m.handle)||(a=m.handle=function(t){return\"undefined\"!=typeof st&&st.event.triggered!==t.type?st.event.dispatch.apply(e,arguments):void 0}),t=(t||\"\").match(Et)||[\"\"],c=t.length;c--;)s=Nn.exec(t[c])||[],h=f=s[1],g=(s[2]||\"\").split(\".\").sort(),h&&(d=st.event.special[h]||{},h=(o?d.delegateType:d.bindType)||h,d=st.event.special[h]||{},u=st.extend({type:h,origType:f,data:r,handler:n,guid:n.guid,selector:o,needsContext:o&&st.expr.match.needsContext.test(o),namespace:g.join(\".\")},i),(p=l[h])||(p=l[h]=[],p.delegateCount=0,d.setup&&d.setup.call(e,r,g,a)!==!1||e.addEventListener&&e.addEventListener(h,a)),d.add&&(d.add.call(e,u),u.handler.guid||(u.handler.guid=n.guid)),o?p.splice(p.delegateCount++,0,u):p.push(u))},remove:function(e,t,n,r,o){var i,a,s,l,c,u,d,p,h,g,f,m=ln.hasData(e)&&ln.get(e);if(m&&(l=m.events)){for(t=(t||\"\").match(Et)||[\"\"],c=t.length;c--;)if(s=Nn.exec(t[c])||[],h=f=s[1],g=(s[2]||\"\").split(\".\").sort(),h){for(d=st.event.special[h]||{},h=(r?d.delegateType:d.bindType)||h,p=l[h]||[],s=s[2]&&new RegExp(\"(^|\\\\.)\"+g.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"),a=i=p.length;i--;)u=p[i],!o&&f!==u.origType||n&&n.guid!==u.guid||s&&!s.test(u.namespace)||r&&r!==u.selector&&(\"**\"!==r||!u.selector)||(p.splice(i,1),u.selector&&p.delegateCount--,d.remove&&d.remove.call(e,u));a&&!p.length&&(d.teardown&&d.teardown.call(e,g,m.handle)!==!1||st.removeEvent(e,h,m.handle),delete l[h])}else for(h in l)st.event.remove(e,h+t[c],n,r,!0);st.isEmptyObject(l)&&ln.remove(e,\"handle events\")}},dispatch:function(e){var t,n,r,o,i,a,s=new Array(arguments.length),l=st.event.fix(e),c=(ln.get(this,\"events\")||Object.create(null))[l.type]||[],u=st.event.special[l.type]||{};for(s[0]=l,t=1;t<arguments.length;t++)s[t]=arguments[t];if(l.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,l)!==!1){for(a=st.event.handlers.call(this,l,c),t=0;(o=a[t++])&&!l.isPropagationStopped();)for(l.currentTarget=o.elem,n=0;(i=o.handlers[n++])&&!l.isImmediatePropagationStopped();)(!l.rnamespace||i.namespace===!1||l.rnamespace.test(i.namespace))&&(l.handleObj=i,l.data=i.data,r=((st.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,s),void 0!==r&&(l.result=r)===!1&&(l.preventDefault(),l.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,l),l.result}},handlers:function(e,t){var n,r,o,i,a,s=[],l=t.delegateCount,c=e.target;if(l&&!(\"click\"===e.type&&e.button>=1))for(;c!==this;c=c.parentNode||this)if(1===c.nodeType&&(\"click\"!==e.type||c.disabled!==!0)){for(i=[],a={},n=0;l>n;n++)r=t[n],o=r.selector+\" \",void 0===a[o]&&(a[o]=r.needsContext?st(o,this).index(c)>-1:st.find(o,this,null,[c]).length),a[o]&&i.push(r);i.length&&s.push({elem:c,handlers:i})}return c=this,l<t.length&&s.push({elem:c,handlers:t.slice(l)}),s},addProp:function(e,t){Object.defineProperty(st.Event.prototype,e,{enumerable:!0,configurable:!0,get:\"function\"==typeof t?function(){return this.originalEvent?t(this.originalEvent):void 0}:function(){return this.originalEvent?this.originalEvent[e]:void 0},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[st.expando]?e:new st.Event(e)},special:st.extend(Object.create(null),{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return Cn.test(t.type)&&t.click&&u(t,\"input\")&&ge(t,\"click\",!0),!1},trigger:function(e){var t=this||e;return Cn.test(t.type)&&t.click&&u(t,\"input\")&&ge(t,\"click\"),!0},_default:function(e){var t=e.target;return Cn.test(t.type)&&t.click&&u(t,\"input\")&&ln.get(t,\"click\")||u(t,\"a\")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.preventDefault()}}})},st.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},st.Event=function(e,t){return this instanceof st.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented?de:pe,this.target=e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&st.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),void(this[st.expando]=!0)):new st.Event(e,t)},st.Event.prototype={constructor:st.Event,isDefaultPrevented:pe,isPropagationStopped:pe,isImmediatePropagationStopped:pe,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=de,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=de,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=de,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},st.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,\"char\":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:!0},st.event.addProp),st.each({focus:\"focusin\",blur:\"focusout\"},function(e,t){function n(e){var t=st.event.fix(e);t.type=\"focusin\"===e.type?\"focus\":\"blur\",t.isSimulated=!0,t.target===t.currentTarget&&ln.get(this,\"handle\")(t)}st.event.special[e]={setup:function(){return ge(this,e,!0),ut?void this.addEventListener(t,n):!1},trigger:function(){return ge(this,e),!0},teardown:function(){return ut?void this.removeEventListener(t,n):!1},_default:function(t){return ln.get(t.target,e)},delegateType:t}}),st.each({mouseenter:\"mouseover\",mouseleave:\"mouseout\",pointerenter:\"pointerover\",pointerleave:\"pointerout\"},function(e,t){st.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,o=e.relatedTarget,i=e.handleObj;return(!o||o!==r&&!st.contains(r,o))&&(e.type=i.origType,n=i.handler.apply(this,arguments),e.type=t),n}}}),st.fn.extend({on:function(e,t,n,r){return he(this,e,t,n,r)},one:function(e,t,n,r){return he(this,e,t,n,r,1)},off:function(e,t,n){var r,o;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,st(e.delegateTarget).off(r.namespace?r.origType+\".\"+r.namespace:r.origType,r.selector,r.handler),this;if(\"object\"==typeof e){for(o in e)this.off(o,t,e[o]);return this}return(t===!1||\"function\"==typeof t)&&(n=t,t=void 0),n===!1&&(n=pe),this.each(function(){st.event.remove(this,e,n,t)})}});var In=/<script|<style|<link/i;st.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,o,i,a,s=e.cloneNode(!0),l=wn(e);if(ut&&(1===e.nodeType||11===e.nodeType)&&!st.isXMLDoc(e))for(a=ie(s),i=ie(e),r=0,o=i.length;o>r;r++)u(a[r],\"textarea\")&&(a[r].defaultValue=i[r].defaultValue);if(t)if(n)for(i=i||ie(e),a=a||ie(s),r=0,o=i.length;o>r;r++)me(i[r],a[r]);else me(e,s);return a=ie(s,\"script\"),a.length>0&&ae(a,!l&&ie(e,\"script\")),s},cleanData:function(e){for(var t,n,r,o=st.event.special,i=0;void 0!==(n=e[i]);i++)if(q(n)){if(t=n[ln.expando]){if(t.events)for(r in t.events)o[r]?st.event.remove(n,r):st.removeEvent(n,r,t.handle);n[ln.expando]=void 0}n[cn.expando]&&(n[cn.expando]=void 0)}}}),st.fn.extend({detach:function(e){return Ae(this,e,!0)},remove:function(e){return Ae(this,e)},text:function(e){return A(this,function(e){return void 0===e?st.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=e)})},null,e,arguments.length)},append:function(){return ue(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=fe(this,e);t.appendChild(e)}})},prepend:function(){return ue(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=fe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return ue(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return ue(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(st.cleanData(ie(e,!1)),e.textContent=\"\");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return st.clone(this,e,t)})},html:function(e){return A(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if(\"string\"==typeof e&&!In.test(e)&&!yn[(bn.exec(e)||[\"\",\"\"])[1].toLowerCase()]){e=st.htmlPrefilter(e);try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(st.cleanData(ie(t,!1)),t.innerHTML=e);t=0}catch(o){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return ue(this,arguments,function(t){var n=this.parentNode;st.inArray(this,e)<0&&(st.cleanData(ie(this)),n&&n.replaceChild(t,this))},e)}}),st.each({appendTo:\"append\",prependTo:\"prepend\",insertBefore:\"before\",insertAfter:\"after\",replaceAll:\"replaceWith\"},function(e,t){st.fn[e]=function(e){for(var n,r=[],o=st(e),i=o.length-1,a=0;i>=a;a++)n=a===i?this:this.clone(!0),st(o[a])[t](n),qe.apply(r,n);return this.pushStack(r)}});var En,Dn,Sn=new RegExp(\"^(\"+pn+\")(?!px)[a-z%]+$\",\"i\"),Ln=/^--/,kn=[\"Webkit\",\"Moz\",\"ms\"],jn=rt.createElement(\"div\").style,Un=rt.createElement(\"table\");st.extend(nt,{reliableTrDimensions:function(){return xe(),En},reliableColDimensions:function(){return xe(),Dn}});var Bn={position:\"absolute\",visibility:\"hidden\",display:\"block\"},Rn={letterSpacing:\"0\",fontWeight:\"400\"};st.extend({cssHooks:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,i,a,s=ne(t),l=Ln.test(t),c=e.style;return l||(t=ye(s)),a=st.cssHooks[t]||st.cssHooks[s],void 0===n?a&&\"get\"in a&&void 0!==(o=a.get(e,!1,r))?o:c[t]:(i=typeof n,\"string\"===i&&(o=hn.exec(n))&&o[1]&&(n=te(e,t,o),i=\"number\"),null!=n&&n===n&&(\"number\"===i&&(n+=o&&o[3]||(ee(s)?\"px\":\"\")),ut&&\"\"===n&&0===t.indexOf(\"background\")&&(c[t]=\"inherit\"),a&&\"set\"in a&&void 0===(n=a.set(e,n,r))||(l?c.setProperty(t,n):c[t]=n)),void 0)}},css:function(e,t,n,r){var o,i,a,s=ne(t),l=Ln.test(t);return l||(t=ye(s)),a=st.cssHooks[t]||st.cssHooks[s],a&&\"get\"in a&&(o=a.get(e,!0,n)),void 0===o&&(o=ve(e,t,r)),\"normal\"===o&&t in Rn&&(o=Rn[t]),\"\"===n||n?(i=parseFloat(o),n===!0||isFinite(i)?i||0:o):o}}),st.each([\"height\",\"width\"],function(e,t){st.cssHooks[t]={get:function(e,n,r){return n?\"none\"===st.css(e,\"display\")?we(e,Bn,function(){return Ne(e,t,r)}):Ne(e,t,r):void 0},set:function(e,n,r){var o,i=Me(e),a=r&&\"border-box\"===st.css(e,\"boxSizing\",!1,i),s=r?Ce(e,t,r,a,i):0;return s&&(o=hn.exec(n))&&\"px\"!==(o[3]||\"px\")&&(e.style[t]=n,n=st.css(e,t)),Te(e,n,s)}}}),st.each({margin:\"\",padding:\"\",border:\"Width\"},function(e,t){st.cssHooks[e+t]={expand:function(n){for(var r=0,o={},i=\"string\"==typeof n?n.split(\" \"):[n];4>r;r++)o[e+gn[r]+t]=i[r]||i[r-2]||i[0];return o}},\"margin\"!==e&&(st.cssHooks[e+t].set=Te)}),st.fn.extend({css:function(e,t){return A(this,function(e,t,n){var r,o,i={},a=0;if(Array.isArray(t)){for(r=Me(e),o=t.length;o>a;a++)i[t[a]]=st.css(e,t[a],!1,r);return i}return void 0!==n?st.style(e,t,n):st.css(e,t)},e,t,arguments.length>1)}}),st.Tween=Ie,Ie.prototype={constructor:Ie,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||st.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(ee(n)?\"px\":\"\")},cur:function(){var e=Ie.propHooks[this.prop];return e&&e.get?e.get(this):Ie.propHooks._default.get(this)},run:function(e){var t,n=Ie.propHooks[this.prop];return this.options.duration?this.pos=t=st.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):Ie.propHooks._default.set(this),this}},Ie.prototype.init.prototype=Ie.prototype,Ie.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=st.css(e.elem,e.prop,\"\"),t&&\"auto\"!==t?t:0)},set:function(e){st.fx.step[e.prop]?st.fx.step[e.prop](e):1!==e.elem.nodeType||!st.cssHooks[e.prop]&&null==e.elem.style[ye(e.prop)]?e.elem[e.prop]=e.now:st.style(e.elem,e.prop,e.now+e.unit)}}},st.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:\"swing\"},st.fx=Ie.prototype.init,st.fx.step={};var zn,Qn,On=/^(?:toggle|show|hide)$/,Hn=/queueHooks$/;st.Animation=st.extend(Ue,{tweeners:{\"*\":[function(e,t){var n=this.createTween(e,t);return te(n.elem,e,hn.exec(t),n),n}]},tweener:function(e,t){\"function\"==typeof e?(t=e,e=[\"*\"]):e=e.match(Et);for(var n,r=0,o=e.length;o>r;r++)n=e[r],Ue.tweeners[n]=Ue.tweeners[n]||[],Ue.tweeners[n].unshift(t)},prefilters:[ke],prefilter:function(e,t){t?Ue.prefilters.unshift(e):Ue.prefilters.push(e)}}),st.speed=function(e,t,n){var r=e&&\"object\"==typeof e?st.extend({},e):{complete:n||t||\"function\"==typeof e&&e,duration:e,easing:n&&t||t&&\"function\"!=typeof t&&t};return st.fx.off?r.duration=0:\"number\"!=typeof r.duration&&(r.duration in st.fx.speeds?r.duration=st.fx.speeds[r.duration]:r.duration=st.fx.speeds._default),(null==r.queue||r.queue===!0)&&(r.queue=\"fx\"),r.old=r.complete,r.complete=function(){\"function\"==typeof r.old&&r.old.call(this),r.queue&&st.dequeue(this,r.queue)},r},st.fn.extend({fadeTo:function(e,t,n,r){return this.filter($).css(\"opacity\",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var o=st.isEmptyObject(e),i=st.speed(t,n,r),a=function(){var t=Ue(this,st.extend({},e),i);(o||ln.get(this,\"finish\"))&&t.stop(!0)};return a.finish=a,o||i.queue===!1?this.each(a):this.queue(i.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return\"string\"!=typeof e&&(n=t,t=e,e=void 0),t&&this.queue(e||\"fx\",[]),this.each(function(){var t=!0,o=null!=e&&e+\"queueHooks\",i=st.timers,a=ln.get(this);if(o)a[o]&&a[o].stop&&r(a[o]);else for(o in a)a[o]&&a[o].stop&&Hn.test(o)&&r(a[o]);for(o=i.length;o--;)i[o].elem!==this||null!=e&&i[o].queue!==e||(i[o].anim.stop(n),t=!1,i.splice(o,1));(t||!n)&&st.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||\"fx\"),this.each(function(){var t,n=ln.get(this),r=n[e+\"queue\"],o=n[e+\"queueHooks\"],i=st.timers,a=r?r.length:0;for(n.finish=!0,st.queue(this,e,[]),o&&o.stop&&o.stop.call(this,!0),t=i.length;t--;)i[t].elem===this&&i[t].queue===e&&(i[t].anim.stop(!0),i.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),st.each([\"toggle\",\"show\",\"hide\"],function(e,t){var n=st.fn[t];st.fn[t]=function(e,r,o){return null==e||\"boolean\"==typeof e?n.apply(this,arguments):this.animate(Se(t,!0),e,r,o)}}),st.each({slideDown:Se(\"show\"),slideUp:Se(\"hide\"),slideToggle:Se(\"toggle\"),fadeIn:{opacity:\"show\"},fadeOut:{opacity:\"hide\"},fadeToggle:{opacity:\"toggle\"}},function(e,t){st.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),st.timers=[],st.fx.tick=function(){var e,t=0,n=st.timers;for(zn=Date.now();t<n.length;t++)e=n[t],e()||n[t]!==e||n.splice(t--,1);n.length||st.fx.stop(),zn=void 0},st.fx.timer=function(e){st.timers.push(e),st.fx.start()},st.fx.start=function(){Qn||(Qn=!0,Ee())},st.fx.stop=function(){Qn=null},st.fx.speeds={slow:600,fast:200,_default:400},st.fn.delay=function(e,t){return e=st.fx?st.fx.speeds[e]||e:e,t=t||\"fx\",this.queue(t,function(t,r){var o=n.setTimeout(t,e);r.stop=function(){n.clearTimeout(o)}})};var Fn=/^(?:input|select|textarea|button)$/i,Vn=/^(?:a|area)$/i;st.fn.extend({prop:function(e,t){return A(this,st.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[st.propFix[e]||e]})}}),st.extend({prop:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return 1===i&&st.isXMLDoc(e)||(t=st.propFix[t]||t,o=st.propHooks[t]),void 0!==n?o&&\"set\"in o&&void 0!==(r=o.set(e,n,t))?r:e[t]=n:o&&\"get\"in o&&null!==(r=o.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=e.getAttribute(\"tabindex\");return t?parseInt(t,10):Fn.test(e.nodeName)||Vn.test(e.nodeName)&&e.href?0:-1}}},propFix:{\"for\":\"htmlFor\",\"class\":\"className\"}}),ut&&(st.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),st.each([\"tabIndex\",\"readOnly\",\"maxLength\",\"cellSpacing\",\"cellPadding\",\"rowSpan\",\"colSpan\",\"useMap\",\"frameBorder\",\"contentEditable\"],function(){st.propFix[this.toLowerCase()]=this}),st.fn.extend({addClass:function(e){var t,n,r,o,i,a;return\"function\"==typeof e?this.each(function(t){st(this).addClass(e.call(this,t,Re(this)))}):(t=ze(e),t.length?this.each(function(){if(r=Re(this),n=1===this.nodeType&&\" \"+Be(r)+\" \"){for(i=0;i<t.length;i++)o=t[i],n.indexOf(\" \"+o+\" \")<0&&(n+=o+\" \");a=Be(n),r!==a&&this.setAttribute(\"class\",a)}}):this)},removeClass:function(e){var t,n,r,o,i,a;return\"function\"==typeof e?this.each(function(t){st(this).removeClass(e.call(this,t,Re(this)))}):arguments.length?(t=ze(e),t.length?this.each(function(){if(r=Re(this),n=1===this.nodeType&&\" \"+Be(r)+\" \"){for(i=0;i<t.length;i++)for(o=t[i];n.indexOf(\" \"+o+\" \")>-1;)n=n.replace(\" \"+o+\" \",\" \");a=Be(n),r!==a&&this.setAttribute(\"class\",a)}}):this):this.attr(\"class\",\"\")},toggleClass:function(e,t){var n,r,o,i;return\"function\"==typeof e?this.each(function(n){st(this).toggleClass(e.call(this,n,Re(this),t),t)}):\"boolean\"==typeof t?t?this.addClass(e):this.removeClass(e):(n=ze(e),n.length?this.each(function(){for(i=st(this),o=0;o<n.length;o++)r=n[o],i.hasClass(r)?i.removeClass(r):i.addClass(r)}):this)},hasClass:function(e){var t,n,r=0;for(t=\" \"+e+\" \";n=this[r++];)if(1===n.nodeType&&(\" \"+Be(Re(n))+\" \").indexOf(t)>-1)return!0;return!1}}),st.fn.extend({val:function(e){var t,n,r,o=this[0];{if(arguments.length)return r=\"function\"==typeof e,this.each(function(n){var o;1===this.nodeType&&(o=r?e.call(this,n,st(this).val()):e,null==o?o=\"\":\"number\"==typeof o?o+=\"\":Array.isArray(o)&&(o=st.map(o,function(e){return null==e?\"\":e+\"\"})),t=st.valHooks[this.type]||st.valHooks[this.nodeName.toLowerCase()],t&&\"set\"in t&&void 0!==t.set(this,o,\"value\")||(this.value=o))});if(o)return t=st.valHooks[o.type]||st.valHooks[o.nodeName.toLowerCase()],t&&\"get\"in t&&void 0!==(n=t.get(o,\"value\"))?n:(n=o.value,null==n?\"\":n)}}}),st.extend({valHooks:{select:{get:function(e){var t,n,r,o=e.options,i=e.selectedIndex,a=\"select-one\"===e.type,s=a?null:[],l=a?i+1:o.length;for(r=0>i?l:a?i:0;l>r;r++)if(n=o[r],n.selected&&!n.disabled&&(!n.parentNode.disabled||!u(n.parentNode,\"optgroup\"))){if(t=st(n).val(),a)return t;s.push(t)}return s},set:function(e,t){for(var n,r,o=e.options,i=st.makeArray(t),a=o.length;a--;)r=o[a],(r.selected=st.inArray(st(r).val(),i)>-1)&&(n=!0);return n||(e.selectedIndex=-1),i}}}}),ut&&(st.valHooks.option={get:function(e){var t=e.getAttribute(\"value\");return null!=t?t:Be(st.text(e))}}),st.each([\"radio\",\"checkbox\"],function(){st.valHooks[this]={set:function(e,t){return Array.isArray(t)?e.checked=st.inArray(st(e).val(),t)>-1:void 0}}});var Pn=/^(?:focusinfocus|focusoutblur)$/,Yn=function(e){e.stopPropagation()};st.extend(st.event,{trigger:function(e,t,r,o){var i,a,l,c,u,d,p,h,g=[r||rt],f=$e.call(e,\"type\")?e.type:e,m=$e.call(e,\"namespace\")?e.namespace.split(\".\"):[];if(a=h=l=r=r||rt,3!==r.nodeType&&8!==r.nodeType&&!Pn.test(f+st.event.triggered)&&(f.indexOf(\".\")>-1&&(m=f.split(\".\"),f=m.shift(),m.sort()),u=f.indexOf(\":\")<0&&\"on\"+f,e=e[st.expando]?e:new st.Event(f,\"object\"==typeof e&&e),e.isTrigger=o?2:3,e.namespace=m.join(\".\"),e.rnamespace=e.namespace?new RegExp(\"(^|\\\\.)\"+m.join(\"\\\\.(?:.*\\\\.|)\")+\"(\\\\.|$)\"):null,e.result=void 0,e.target||(e.target=r),t=null==t?[e]:st.makeArray(t,[e]),\np=st.event.special[f]||{},o||!p.trigger||p.trigger.apply(r,t)!==!1)){if(!o&&!p.noBubble&&!s(r)){for(c=p.delegateType||f,Pn.test(c+f)||(a=a.parentNode);a;a=a.parentNode)g.push(a),l=a;l===(r.ownerDocument||rt)&&g.push(l.defaultView||l.parentWindow||n)}for(i=0;(a=g[i++])&&!e.isPropagationStopped();)h=a,e.type=i>1?c:p.bindType||f,d=(ln.get(a,\"events\")||Object.create(null))[e.type]&&ln.get(a,\"handle\"),d&&d.apply(a,t),d=u&&a[u],d&&d.apply&&q(a)&&(e.result=d.apply(a,t),e.result===!1&&e.preventDefault());return e.type=f,o||e.isDefaultPrevented()||p._default&&p._default.apply(g.pop(),t)!==!1||!q(r)||u&&\"function\"==typeof r[f]&&!s(r)&&(l=r[u],l&&(r[u]=null),st.event.triggered=f,e.isPropagationStopped()&&h.addEventListener(f,Yn),r[f](),e.isPropagationStopped()&&h.removeEventListener(f,Yn),st.event.triggered=void 0,l&&(r[u]=l)),e.result}},simulate:function(e,t,n){var r=st.extend(new st.Event,n,{type:e,isSimulated:!0});st.event.trigger(r,null,t)}}),st.fn.extend({trigger:function(e,t){return this.each(function(){st.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?st.event.trigger(e,t,n,!0):void 0}});var Gn=n.location,Wn={guid:Date.now()},Xn=/\\?/;st.parseXML=function(e){var t,r;if(!e||\"string\"!=typeof e)return null;try{t=(new n.DOMParser).parseFromString(e,\"text/xml\")}catch(o){}return r=t&&t.getElementsByTagName(\"parsererror\")[0],(!t||r)&&st.error(\"Invalid XML: \"+(r?st.map(r.childNodes,function(e){return e.textContent}).join(\"\\n\"):e)),t};var _n=/\\[\\]$/,qn=/\\r?\\n/g,Jn=/^(?:submit|button|image|reset|file)$/i,Kn=/^(?:input|select|textarea|keygen)/i;st.param=function(e,t){var n,r=[],o=function(e,t){var n=\"function\"==typeof t?t():t;r[r.length]=encodeURIComponent(e)+\"=\"+encodeURIComponent(null==n?\"\":n)};if(null==e)return\"\";if(Array.isArray(e)||e.jquery&&!st.isPlainObject(e))st.each(e,function(){o(this.name,this.value)});else for(n in e)Qe(n,e[n],t,o);return r.join(\"&\")},st.fn.extend({serialize:function(){return st.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=st.prop(this,\"elements\");return e?st.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!st(this).is(\":disabled\")&&Kn.test(this.nodeName)&&!Jn.test(e)&&(this.checked||!Cn.test(e))}).map(function(e,t){var n=st(this).val();return null==n?null:Array.isArray(n)?st.map(n,function(e){return{name:t.name,value:e.replace(qn,\"\\r\\n\")}}):{name:t.name,value:n.replace(qn,\"\\r\\n\")}}).get()}});var Zn=/%20/g,$n=/#.*$/,er=/([?&])_=[^&]*/,tr=/^(.*?):[ \\t]*([^\\r\\n]*)$/gm,nr=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,rr=/^(?:GET|HEAD)$/,or=/^\\/\\//,ir={},ar={},sr=\"*/\".concat(\"*\"),lr=rt.createElement(\"a\");lr.href=Gn.href,st.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Gn.href,type:\"GET\",isLocal:nr.test(Gn.protocol),global:!0,processData:!0,async:!0,contentType:\"application/x-www-form-urlencoded; charset=UTF-8\",accepts:{\"*\":sr,text:\"text/plain\",html:\"text/html\",xml:\"application/xml, text/xml\",json:\"application/json, text/javascript\"},contents:{xml:/\\bxml\\b/,html:/\\bhtml/,json:/\\bjson\\b/},responseFields:{xml:\"responseXML\",text:\"responseText\",json:\"responseJSON\"},converters:{\"* text\":String,\"text html\":!0,\"text json\":JSON.parse,\"text xml\":st.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Fe(Fe(e,st.ajaxSettings),t):Fe(st.ajaxSettings,e)},ajaxPrefilter:Oe(ir),ajaxTransport:Oe(ar),ajax:function(e,t){function r(e,t,r,s){var c,p,h,v,b,y=t;u||(u=!0,l&&n.clearTimeout(l),o=void 0,a=s||\"\",x.readyState=e>0?4:0,c=e>=200&&300>e||304===e,r&&(v=Ve(g,x,r)),!c&&st.inArray(\"script\",g.dataTypes)>-1&&st.inArray(\"json\",g.dataTypes)<0&&(g.converters[\"text script\"]=function(){}),v=Pe(g,v,x,c),c?(g.ifModified&&(b=x.getResponseHeader(\"Last-Modified\"),b&&(st.lastModified[i]=b),b=x.getResponseHeader(\"etag\"),b&&(st.etag[i]=b)),204===e||\"HEAD\"===g.type?y=\"nocontent\":304===e?y=\"notmodified\":(y=v.state,p=v.data,h=v.error,c=!h)):(h=y,(e||!y)&&(y=\"error\",0>e&&(e=0))),x.status=e,x.statusText=(t||y)+\"\",c?A.resolveWith(f,[p,y,x]):A.rejectWith(f,[x,y,h]),x.statusCode(w),w=void 0,d&&m.trigger(c?\"ajaxSuccess\":\"ajaxError\",[x,g,c?p:h]),M.fireWith(f,[x,y]),d&&(m.trigger(\"ajaxComplete\",[x,g]),--st.active||st.event.trigger(\"ajaxStop\")))}\"object\"==typeof e&&(t=e,e=void 0),t=t||{};var o,i,a,s,l,c,u,d,p,h,g=st.ajaxSetup({},t),f=g.context||g,m=g.context&&(f.nodeType||f.jquery)?st(f):st.event,A=st.Deferred(),M=st.Callbacks(\"once memory\"),w=g.statusCode||{},v={},b={},y=\"canceled\",x={readyState:0,getResponseHeader:function(e){var t;if(u){if(!s)for(s={};t=tr.exec(a);)s[t[1].toLowerCase()+\" \"]=(s[t[1].toLowerCase()+\" \"]||[]).concat(t[2]);t=s[e.toLowerCase()+\" \"]}return null==t?null:t.join(\", \")},getAllResponseHeaders:function(){return u?a:null},setRequestHeader:function(e,t){return null==u&&(e=b[e.toLowerCase()]=b[e.toLowerCase()]||e,v[e]=t),this},overrideMimeType:function(e){return null==u&&(g.mimeType=e),this},statusCode:function(e){var t;if(e)if(u)x.always(e[x.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||y;return o&&o.abort(t),r(0,t),this}};if(A.promise(x),g.url=((e||g.url||Gn.href)+\"\").replace(or,Gn.protocol+\"//\"),g.type=t.method||t.type||g.method||g.type,g.dataTypes=(g.dataType||\"*\").toLowerCase().match(Et)||[\"\"],null==g.crossDomain){c=rt.createElement(\"a\");try{c.href=g.url,c.href=c.href,g.crossDomain=lr.protocol+\"//\"+lr.host!=c.protocol+\"//\"+c.host}catch(T){g.crossDomain=!0}}if(He(ir,g,t,x),g.data&&g.processData&&\"string\"!=typeof g.data&&(g.data=st.param(g.data,g.traditional)),u)return x;d=st.event&&g.global,d&&0===st.active++&&st.event.trigger(\"ajaxStart\"),g.type=g.type.toUpperCase(),g.hasContent=!rr.test(g.type),i=g.url.replace($n,\"\"),g.hasContent?g.data&&g.processData&&0===(g.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&(g.data=g.data.replace(Zn,\"+\")):(h=g.url.slice(i.length),g.data&&(g.processData||\"string\"==typeof g.data)&&(i+=(Xn.test(i)?\"&\":\"?\")+g.data,delete g.data),g.cache===!1&&(i=i.replace(er,\"$1\"),h=(Xn.test(i)?\"&\":\"?\")+\"_=\"+Wn.guid++ +h),g.url=i+h),g.ifModified&&(st.lastModified[i]&&x.setRequestHeader(\"If-Modified-Since\",st.lastModified[i]),st.etag[i]&&x.setRequestHeader(\"If-None-Match\",st.etag[i])),(g.data&&g.hasContent&&g.contentType!==!1||t.contentType)&&x.setRequestHeader(\"Content-Type\",g.contentType),x.setRequestHeader(\"Accept\",g.dataTypes[0]&&g.accepts[g.dataTypes[0]]?g.accepts[g.dataTypes[0]]+(\"*\"!==g.dataTypes[0]?\", \"+sr+\"; q=0.01\":\"\"):g.accepts[\"*\"]);for(p in g.headers)x.setRequestHeader(p,g.headers[p]);if(g.beforeSend&&(g.beforeSend.call(f,x,g)===!1||u))return x.abort();if(y=\"abort\",M.add(g.complete),x.done(g.success),x.fail(g.error),o=He(ar,g,t,x)){if(x.readyState=1,d&&m.trigger(\"ajaxSend\",[x,g]),u)return x;g.async&&g.timeout>0&&(l=n.setTimeout(function(){x.abort(\"timeout\")},g.timeout));try{u=!1,o.send(v,r)}catch(T){if(u)throw T;r(-1,T)}}else r(-1,\"No Transport\");return x},getJSON:function(e,t,n){return st.get(e,t,n,\"json\")},getScript:function(e,t){return st.get(e,void 0,t,\"script\")}}),st.each([\"get\",\"post\"],function(e,t){st[t]=function(e,n,r,o){return(\"function\"==typeof n||null===n)&&(o=o||r,r=n,n=void 0),st.ajax(st.extend({url:e,type:t,dataType:o,data:n,success:r},st.isPlainObject(e)&&e))}}),st.ajaxPrefilter(function(e){var t;for(t in e.headers)\"content-type\"===t.toLowerCase()&&(e.contentType=e.headers[t]||\"\")}),st._evalUrl=function(e,t,n){return st.ajax({url:e,type:\"GET\",dataType:\"script\",cache:!0,async:!1,global:!1,scriptAttrs:t.crossOrigin?{crossOrigin:t.crossOrigin}:void 0,converters:{\"text script\":function(){}},dataFilter:function(e){st.globalEval(e,t,n)}})},st.fn.extend({wrapAll:function(e){var t;return this[0]&&(\"function\"==typeof e&&(e=e.call(this[0])),t=st(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return\"function\"==typeof e?this.each(function(t){st(this).wrapInner(e.call(this,t))}):this.each(function(){var t=st(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=\"function\"==typeof e;return this.each(function(n){st(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not(\"body\").each(function(){st(this).replaceWith(this.childNodes)}),this}}),st.expr.pseudos.hidden=function(e){return!st.expr.pseudos.visible(e)},st.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},st.ajaxSettings.xhr=function(){return new n.XMLHttpRequest};var cr={0:200};st.ajaxTransport(function(e){var t;return{send:function(n,r){var o,i=e.xhr();if(i.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(o in e.xhrFields)i[o]=e.xhrFields[o];e.mimeType&&i.overrideMimeType&&i.overrideMimeType(e.mimeType),e.crossDomain||n[\"X-Requested-With\"]||(n[\"X-Requested-With\"]=\"XMLHttpRequest\");for(o in n)i.setRequestHeader(o,n[o]);t=function(e){return function(){t&&(t=i.onload=i.onerror=i.onabort=i.ontimeout=null,\"abort\"===e?i.abort():\"error\"===e?r(i.status,i.statusText):r(cr[i.status]||i.status,i.statusText,\"text\"===(i.responseType||\"text\")?{text:i.responseText}:{binary:i.response},i.getAllResponseHeaders()))}},i.onload=t(),i.onabort=i.onerror=i.ontimeout=t(\"error\"),t=t(\"abort\");try{i.send(e.hasContent&&e.data||null)}catch(a){if(t)throw a}},abort:function(){t&&t()}}}),st.ajaxSetup({accepts:{script:\"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"},converters:{\"text script\":function(e){return st.globalEval(e),e}}}),st.ajaxPrefilter(\"script\",function(e){void 0===e.cache&&(e.cache=!1),Ye(e)&&(e.type=\"GET\")}),st.ajaxTransport(\"script\",function(e){if(Ye(e)){var t,n;return{send:function(r,o){t=st(\"<script>\").attr(e.scriptAttrs||{}).prop({charset:e.scriptCharset,src:e.url}).on(\"load error\",n=function(e){t.remove(),n=null,e&&o(\"error\"===e.type?404:200,e.type)}),rt.head.appendChild(t[0])},abort:function(){n&&n()}}}});var ur=[],dr=/(=)\\?(?=&|$)|\\?\\?/;st.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=ur.pop()||st.expando+\"_\"+Wn.guid++;return this[e]=!0,e}}),st.ajaxPrefilter(\"jsonp\",function(e,t,r){var o,i,a,s=e.jsonp!==!1&&(dr.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&dr.test(e.data)&&\"data\");return o=e.jsonpCallback=\"function\"==typeof e.jsonpCallback?e.jsonpCallback():e.jsonpCallback,s?e[s]=e[s].replace(dr,\"$1\"+o):e.jsonp!==!1&&(e.url+=(Xn.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+o),e.converters[\"script json\"]=function(){return a||st.error(o+\" was not called\"),a[0]},e.dataTypes[0]=\"json\",i=n[o],n[o]=function(){a=arguments},r.always(function(){void 0===i?st(n).removeProp(o):n[o]=i,e[o]&&(e.jsonpCallback=t.jsonpCallback,ur.push(o)),a&&\"function\"==typeof i&&i(a[0]),a=i=void 0}),\"script\"}),st.ajaxPrefilter(function(e,t){\"string\"==typeof e.data||st.isPlainObject(e.data)||Array.isArray(e.data)||\"processData\"in t||(e.processData=!1),e.data instanceof n.FormData&&(e.contentType=!1)}),st.parseHTML=function(e,t,r){if(\"string\"!=typeof e&&!O(e+\"\"))return[];\"boolean\"==typeof t&&(r=t,t=!1);var o,i;return t||(t=(new n.DOMParser).parseFromString(\"\",\"text/html\")),o=Zt.exec(e),i=!r&&[],o?[t.createElement(o[1])]:(o=se([e],t,i),i&&i.length&&st(i).remove(),st.merge([],o.childNodes))},st.fn.load=function(e,t,n){var r,o,i,a=this,s=e.indexOf(\" \");return s>-1&&(r=Be(e.slice(s)),e=e.slice(0,s)),\"function\"==typeof t?(n=t,t=void 0):t&&\"object\"==typeof t&&(o=\"POST\"),a.length>0&&st.ajax({url:e,type:o||\"GET\",dataType:\"html\",data:t}).done(function(e){i=arguments,a.html(r?st(\"<div>\").append(st.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,i||[e.responseText,t,e])})}),this},st.expr.pseudos.animated=function(e){return st.grep(st.timers,function(t){return e===t.elem}).length},st.offset={setOffset:function(e,t,n){var r,o,i,a,s,l,c,u=st.css(e,\"position\"),d=st(e),p={};\"static\"===u&&(e.style.position=\"relative\"),s=d.offset(),i=st.css(e,\"top\"),l=st.css(e,\"left\"),c=(\"absolute\"===u||\"fixed\"===u)&&(i+l).indexOf(\"auto\")>-1,c?(r=d.position(),a=r.top,o=r.left):(a=parseFloat(i)||0,o=parseFloat(l)||0),\"function\"==typeof t&&(t=t.call(e,n,st.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+o),\"using\"in t?t.using.call(e,p):d.css(p)}},st.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){st.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],o={top:0,left:0};if(\"fixed\"===st.css(r,\"position\"))t=r.getBoundingClientRect();else{for(t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;e&&e!==n.documentElement&&\"static\"===st.css(e,\"position\");)e=e.offsetParent||n.documentElement;e&&e!==r&&1===e.nodeType&&\"static\"!==st.css(e,\"position\")&&(o=st(e).offset(),o.top+=st.css(e,\"borderTopWidth\",!0),o.left+=st.css(e,\"borderLeftWidth\",!0))}return{top:t.top-o.top-st.css(r,\"marginTop\",!0),left:t.left-o.left-st.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&\"static\"===st.css(e,\"position\");)e=e.offsetParent;return e||At})}}),st.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(e,t){var n=\"pageYOffset\"===t;st.fn[e]=function(r){return A(this,function(e,r,o){var i;return s(e)?i=e:9===e.nodeType&&(i=e.defaultView),void 0===o?i?i[t]:e[r]:void(i?i.scrollTo(n?i.pageXOffset:o,n?o:i.pageYOffset):e[r]=o)},e,r,arguments.length)}}),st.each({Height:\"height\",Width:\"width\"},function(e,t){st.each({padding:\"inner\"+e,content:t,\"\":\"outer\"+e},function(n,r){st.fn[r]=function(o,i){var a=arguments.length&&(n||\"boolean\"!=typeof o),l=n||(o===!0||i===!0?\"margin\":\"border\");return A(this,function(t,n,o){var i;return s(t)?0===r.indexOf(\"outer\")?t[\"inner\"+e]:t.document.documentElement[\"client\"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body[\"scroll\"+e],i[\"scroll\"+e],t.body[\"offset\"+e],i[\"offset\"+e],i[\"client\"+e])):void 0===o?st.css(t,n,l):st.style(t,n,o,l)},t,a?o:void 0,a)}})}),st.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){st.fn[t]=function(e){return this.on(t,e)}}),st.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.on(\"mouseenter\",e).on(\"mouseleave\",t||e)}}),st.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,t){st.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),st.proxy=function(e,t){var n,r,o;return\"string\"==typeof t&&(n=e[t],t=e,e=n),\"function\"!=typeof e?void 0:(r=Xe.call(arguments,2),o=function(){return e.apply(t||this,r.concat(Xe.call(arguments)))},o.guid=e.guid=e.guid||st.guid++,o)},st.holdReady=function(e){e?st.readyWait++:st.ready(!0)},st.expr[\":\"]=st.expr.filters=st.expr.pseudos,r=[],o=function(){return st}.apply(t,r),!(void 0!==o&&(e.exports=o));var pr=n.jQuery,hr=n.$;return st.noConflict=function(e){return n.$===st&&(n.$=hr),e&&n.jQuery===st&&(n.jQuery=pr),st},\"undefined\"==typeof i&&(n.jQuery=n.$=st),st})},function(e,t){/*!\n\t * Bootstrap v3.3.5 (http://getbootstrap.com)\n\t * Copyright 2011-2015 Twitter, Inc.\n\t * Licensed under the MIT license\n\t */\nif(\"undefined\"==typeof jQuery)throw new Error(\"Bootstrap's JavaScript requires jQuery\");+function(e){\"use strict\";var t=e.fn.jquery.split(\" \")[0].split(\".\");if(t[0]<2&&t[1]<9||1==t[0]&&9==t[1]&&t[2]<1)throw new Error(\"Bootstrap's JavaScript requires jQuery version 1.9.1 or higher\")}(jQuery),+function(e){\"use strict\";function t(){var e=document.createElement(\"bootstrap\"),t={WebkitTransition:\"webkitTransitionEnd\",MozTransition:\"transitionend\",OTransition:\"oTransitionEnd otransitionend\",transition:\"transitionend\"};for(var n in t)if(void 0!==e.style[n])return{end:t[n]};return!1}e.fn.emulateTransitionEnd=function(t){var n=!1,r=this;e(this).one(\"bsTransitionEnd\",function(){n=!0});var o=function(){n||e(r).trigger(e.support.transition.end)};return setTimeout(o,t),this},e(function(){e.support.transition=t(),e.support.transition&&(e.event.special.bsTransitionEnd={bindType:e.support.transition.end,delegateType:e.support.transition.end,handle:function(t){return e(t.target).is(this)?t.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var n=e(this),o=n.data(\"bs.alert\");o||n.data(\"bs.alert\",o=new r(this)),\"string\"==typeof t&&o[t].call(n)})}var n='[data-dismiss=\"alert\"]',r=function(t){e(t).on(\"click\",n,this.close)};r.VERSION=\"3.3.5\",r.TRANSITION_DURATION=150,r.prototype.close=function(t){function n(){a.detach().trigger(\"closed.bs.alert\").remove()}var o=e(this),i=o.attr(\"data-target\");i||(i=o.attr(\"href\"),i=i&&i.replace(/.*(?=#[^\\s]*$)/,\"\"));var a=e(i);t&&t.preventDefault(),a.length||(a=o.closest(\".alert\")),a.trigger(t=e.Event(\"close.bs.alert\")),t.isDefaultPrevented()||(a.removeClass(\"in\"),e.support.transition&&a.hasClass(\"fade\")?a.one(\"bsTransitionEnd\",n).emulateTransitionEnd(r.TRANSITION_DURATION):n())};var o=e.fn.alert;e.fn.alert=t,e.fn.alert.Constructor=r,e.fn.alert.noConflict=function(){return e.fn.alert=o,this},e(document).on(\"click.bs.alert.data-api\",n,r.prototype.close)}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.button\"),i=\"object\"==typeof t&&t;o||r.data(\"bs.button\",o=new n(this,i)),\"toggle\"==t?o.toggle():t&&o.setState(t)})}var n=function(t,r){this.$element=e(t),this.options=e.extend({},n.DEFAULTS,r),this.isLoading=!1};n.VERSION=\"3.3.5\",n.DEFAULTS={loadingText:\"loading...\"},n.prototype.setState=function(t){var n=\"disabled\",r=this.$element,o=r.is(\"input\")?\"val\":\"html\",i=r.data();t+=\"Text\",null==i.resetText&&r.data(\"resetText\",r[o]()),setTimeout(e.proxy(function(){r[o](null==i[t]?this.options[t]:i[t]),\"loadingText\"==t?(this.isLoading=!0,r.addClass(n).attr(n,n)):this.isLoading&&(this.isLoading=!1,r.removeClass(n).removeAttr(n))},this),0)},n.prototype.toggle=function(){var e=!0,t=this.$element.closest('[data-toggle=\"buttons\"]');if(t.length){var n=this.$element.find(\"input\");\"radio\"==n.prop(\"type\")?(n.prop(\"checked\")&&(e=!1),t.find(\".active\").removeClass(\"active\"),this.$element.addClass(\"active\")):\"checkbox\"==n.prop(\"type\")&&(n.prop(\"checked\")!==this.$element.hasClass(\"active\")&&(e=!1),this.$element.toggleClass(\"active\")),n.prop(\"checked\",this.$element.hasClass(\"active\")),e&&n.trigger(\"change\")}else this.$element.attr(\"aria-pressed\",!this.$element.hasClass(\"active\")),this.$element.toggleClass(\"active\")};var r=e.fn.button;e.fn.button=t,e.fn.button.Constructor=n,e.fn.button.noConflict=function(){return e.fn.button=r,this},e(document).on(\"click.bs.button.data-api\",'[data-toggle^=\"button\"]',function(n){var r=e(n.target);r.hasClass(\"btn\")||(r=r.closest(\".btn\")),t.call(r,\"toggle\"),e(n.target).is('input[type=\"radio\"]')||e(n.target).is('input[type=\"checkbox\"]')||n.preventDefault()}).on(\"focus.bs.button.data-api blur.bs.button.data-api\",'[data-toggle^=\"button\"]',function(t){e(t.target).closest(\".btn\").toggleClass(\"focus\",/^focus(in)?$/.test(t.type))})}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.carousel\"),i=e.extend({},n.DEFAULTS,r.data(),\"object\"==typeof t&&t),a=\"string\"==typeof t?t:i.slide;o||r.data(\"bs.carousel\",o=new n(this,i)),\"number\"==typeof t?o.to(t):a?o[a]():i.interval&&o.pause().cycle()})}var n=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(\".carousel-indicators\"),this.options=n,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on(\"keydown.bs.carousel\",e.proxy(this.keydown,this)),\"hover\"==this.options.pause&&!(\"ontouchstart\"in document.documentElement)&&this.$element.on(\"mouseenter.bs.carousel\",e.proxy(this.pause,this)).on(\"mouseleave.bs.carousel\",e.proxy(this.cycle,this))};n.VERSION=\"3.3.5\",n.TRANSITION_DURATION=600,n.DEFAULTS={interval:5e3,pause:\"hover\",wrap:!0,keyboard:!0},n.prototype.keydown=function(e){if(!/input|textarea/i.test(e.target.tagName)){switch(e.which){case 37:this.prev();break;case 39:this.next();break;default:return}e.preventDefault()}},n.prototype.cycle=function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},n.prototype.getItemIndex=function(e){return this.$items=e.parent().children(\".item\"),this.$items.index(e||this.$active)},n.prototype.getItemForDirection=function(e,t){var n=this.getItemIndex(t),r=\"prev\"==e&&0===n||\"next\"==e&&n==this.$items.length-1;if(r&&!this.options.wrap)return t;var o=\"prev\"==e?-1:1,i=(n+o)%this.$items.length;return this.$items.eq(i)},n.prototype.to=function(e){var t=this,n=this.getItemIndex(this.$active=this.$element.find(\".item.active\"));return e>this.$items.length-1||0>e?void 0:this.sliding?this.$element.one(\"slid.bs.carousel\",function(){t.to(e)}):n==e?this.pause().cycle():this.slide(e>n?\"next\":\"prev\",this.$items.eq(e))},n.prototype.pause=function(t){return t||(this.paused=!0),this.$element.find(\".next, .prev\").length&&e.support.transition&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},n.prototype.next=function(){return this.sliding?void 0:this.slide(\"next\")},n.prototype.prev=function(){return this.sliding?void 0:this.slide(\"prev\")},n.prototype.slide=function(t,r){var o=this.$element.find(\".item.active\"),i=r||this.getItemForDirection(t,o),a=this.interval,s=\"next\"==t?\"left\":\"right\",l=this;if(i.hasClass(\"active\"))return this.sliding=!1;var c=i[0],u=e.Event(\"slide.bs.carousel\",{relatedTarget:c,direction:s});if(this.$element.trigger(u),!u.isDefaultPrevented()){if(this.sliding=!0,a&&this.pause(),this.$indicators.length){this.$indicators.find(\".active\").removeClass(\"active\");var d=e(this.$indicators.children()[this.getItemIndex(i)]);d&&d.addClass(\"active\")}var p=e.Event(\"slid.bs.carousel\",{relatedTarget:c,direction:s});return e.support.transition&&this.$element.hasClass(\"slide\")?(i.addClass(t),i[0].offsetWidth,o.addClass(s),i.addClass(s),o.one(\"bsTransitionEnd\",function(){i.removeClass([t,s].join(\" \")).addClass(\"active\"),o.removeClass([\"active\",s].join(\" \")),l.sliding=!1,setTimeout(function(){l.$element.trigger(p)},0)}).emulateTransitionEnd(n.TRANSITION_DURATION)):(o.removeClass(\"active\"),i.addClass(\"active\"),this.sliding=!1,this.$element.trigger(p)),a&&this.cycle(),this}};var r=e.fn.carousel;e.fn.carousel=t,e.fn.carousel.Constructor=n,e.fn.carousel.noConflict=function(){return e.fn.carousel=r,this};var o=function(n){var r,o=e(this),i=e(o.attr(\"data-target\")||(r=o.attr(\"href\"))&&r.replace(/.*(?=#[^\\s]+$)/,\"\"));if(i.hasClass(\"carousel\")){var a=e.extend({},i.data(),o.data()),s=o.attr(\"data-slide-to\");s&&(a.interval=!1),t.call(i,a),s&&i.data(\"bs.carousel\").to(s),n.preventDefault()}};e(document).on(\"click.bs.carousel.data-api\",\"[data-slide]\",o).on(\"click.bs.carousel.data-api\",\"[data-slide-to]\",o),e(window).on(\"load\",function(){e('[data-ride=\"carousel\"]').each(function(){var n=e(this);t.call(n,n.data())})})}(jQuery),+function(e){\"use strict\";function t(t){var n,r=t.attr(\"data-target\")||(n=t.attr(\"href\"))&&n.replace(/.*(?=#[^\\s]+$)/,\"\");return e(r)}function n(t){return this.each(function(){var n=e(this),o=n.data(\"bs.collapse\"),i=e.extend({},r.DEFAULTS,n.data(),\"object\"==typeof t&&t);!o&&i.toggle&&/show|hide/.test(t)&&(i.toggle=!1),o||n.data(\"bs.collapse\",o=new r(this,i)),\"string\"==typeof t&&o[t]()})}var r=function(t,n){this.$element=e(t),this.options=e.extend({},r.DEFAULTS,n),this.$trigger=e('[data-toggle=\"collapse\"][href=\"#'+t.id+'\"],[data-toggle=\"collapse\"][data-target=\"#'+t.id+'\"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};r.VERSION=\"3.3.5\",r.TRANSITION_DURATION=350,r.DEFAULTS={toggle:!0},r.prototype.dimension=function(){var e=this.$element.hasClass(\"width\");return e?\"width\":\"height\"},r.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass(\"in\")){var t,o=this.$parent&&this.$parent.children(\".panel\").children(\".in, .collapsing\");if(!(o&&o.length&&(t=o.data(\"bs.collapse\"),t&&t.transitioning))){var i=e.Event(\"show.bs.collapse\");if(this.$element.trigger(i),!i.isDefaultPrevented()){o&&o.length&&(n.call(o,\"hide\"),t||o.data(\"bs.collapse\",null));var a=this.dimension();this.$element.removeClass(\"collapse\").addClass(\"collapsing\")[a](0).attr(\"aria-expanded\",!0),this.$trigger.removeClass(\"collapsed\").attr(\"aria-expanded\",!0),this.transitioning=1;var s=function(){this.$element.removeClass(\"collapsing\").addClass(\"collapse in\")[a](\"\"),this.transitioning=0,this.$element.trigger(\"shown.bs.collapse\")};if(!e.support.transition)return s.call(this);var l=e.camelCase([\"scroll\",a].join(\"-\"));this.$element.one(\"bsTransitionEnd\",e.proxy(s,this)).emulateTransitionEnd(r.TRANSITION_DURATION)[a](this.$element[0][l])}}}},r.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass(\"in\")){var t=e.Event(\"hide.bs.collapse\");if(this.$element.trigger(t),!t.isDefaultPrevented()){var n=this.dimension();this.$element[n](this.$element[n]())[0].offsetHeight,this.$element.addClass(\"collapsing\").removeClass(\"collapse in\").attr(\"aria-expanded\",!1),this.$trigger.addClass(\"collapsed\").attr(\"aria-expanded\",!1),this.transitioning=1;var o=function(){this.transitioning=0,this.$element.removeClass(\"collapsing\").addClass(\"collapse\").trigger(\"hidden.bs.collapse\")};return e.support.transition?void this.$element[n](0).one(\"bsTransitionEnd\",e.proxy(o,this)).emulateTransitionEnd(r.TRANSITION_DURATION):o.call(this)}}},r.prototype.toggle=function(){this[this.$element.hasClass(\"in\")?\"hide\":\"show\"]()},r.prototype.getParent=function(){return e(this.options.parent).find('[data-toggle=\"collapse\"][data-parent=\"'+this.options.parent+'\"]').each(e.proxy(function(n,r){var o=e(r);this.addAriaAndCollapsedClass(t(o),o)},this)).end()},r.prototype.addAriaAndCollapsedClass=function(e,t){var n=e.hasClass(\"in\");e.attr(\"aria-expanded\",n),t.toggleClass(\"collapsed\",!n).attr(\"aria-expanded\",n)};var o=e.fn.collapse;e.fn.collapse=n,e.fn.collapse.Constructor=r,e.fn.collapse.noConflict=function(){return e.fn.collapse=o,this},e(document).on(\"click.bs.collapse.data-api\",'[data-toggle=\"collapse\"]',function(r){var o=e(this);o.attr(\"data-target\")||r.preventDefault();var i=t(o),a=i.data(\"bs.collapse\"),s=a?\"toggle\":o.data();n.call(i,s)})}(jQuery),+function(e){\"use strict\";function t(t){var n=t.attr(\"data-target\");n||(n=t.attr(\"href\"),n=n&&/#[A-Za-z]/.test(n)&&n.replace(/.*(?=#[^\\s]*$)/,\"\"));var r=n&&e(n);return r&&r.length?r:t.parent()}function n(n){n&&3===n.which||(e(o).remove(),e(i).each(function(){var r=e(this),o=t(r),i={relatedTarget:this};o.hasClass(\"open\")&&(n&&\"click\"==n.type&&/input|textarea/i.test(n.target.tagName)&&e.contains(o[0],n.target)||(o.trigger(n=e.Event(\"hide.bs.dropdown\",i)),n.isDefaultPrevented()||(r.attr(\"aria-expanded\",\"false\"),o.removeClass(\"open\").trigger(\"hidden.bs.dropdown\",i))))}))}function r(t){return this.each(function(){var n=e(this),r=n.data(\"bs.dropdown\");r||n.data(\"bs.dropdown\",r=new a(this)),\"string\"==typeof t&&r[t].call(n)})}var o=\".dropdown-backdrop\",i='[data-toggle=\"dropdown\"]',a=function(t){e(t).on(\"click.bs.dropdown\",this.toggle)};a.VERSION=\"3.3.5\",a.prototype.toggle=function(r){var o=e(this);if(!o.is(\".disabled, :disabled\")){var i=t(o),a=i.hasClass(\"open\");if(n(),!a){\"ontouchstart\"in document.documentElement&&!i.closest(\".navbar-nav\").length&&e(document.createElement(\"div\")).addClass(\"dropdown-backdrop\").insertAfter(e(this)).on(\"click\",n);var s={relatedTarget:this};if(i.trigger(r=e.Event(\"show.bs.dropdown\",s)),r.isDefaultPrevented())return;o.trigger(\"focus\").attr(\"aria-expanded\",\"true\"),i.toggleClass(\"open\").trigger(\"shown.bs.dropdown\",s)}return!1}},a.prototype.keydown=function(n){if(/(38|40|27|32)/.test(n.which)&&!/input|textarea/i.test(n.target.tagName)){var r=e(this);if(n.preventDefault(),n.stopPropagation(),!r.is(\".disabled, :disabled\")){var o=t(r),a=o.hasClass(\"open\");if(!a&&27!=n.which||a&&27==n.which)return 27==n.which&&o.find(i).trigger(\"focus\"),r.trigger(\"click\");var s=\" li:not(.disabled):visible a\",l=o.find(\".dropdown-menu\"+s);if(l.length){var c=l.index(n.target);38==n.which&&c>0&&c--,40==n.which&&c<l.length-1&&c++,~c||(c=0),l.eq(c).trigger(\"focus\")}}}};var s=e.fn.dropdown;e.fn.dropdown=r,e.fn.dropdown.Constructor=a,e.fn.dropdown.noConflict=function(){return e.fn.dropdown=s,this},e(document).on(\"click.bs.dropdown.data-api\",n).on(\"click.bs.dropdown.data-api\",\".dropdown form\",function(e){e.stopPropagation()}).on(\"click.bs.dropdown.data-api\",i,a.prototype.toggle).on(\"keydown.bs.dropdown.data-api\",i,a.prototype.keydown).on(\"keydown.bs.dropdown.data-api\",\".dropdown-menu\",a.prototype.keydown)}(jQuery),+function(e){\"use strict\";function t(t,r){return this.each(function(){var o=e(this),i=o.data(\"bs.modal\"),a=e.extend({},n.DEFAULTS,o.data(),\"object\"==typeof t&&t);i||o.data(\"bs.modal\",i=new n(this,a)),\"string\"==typeof t?i[t](r):a.show&&i.show(r)})}var n=function(t,n){this.options=n,this.$body=e(document.body),this.$element=e(t),this.$dialog=this.$element.find(\".modal-dialog\"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(\".modal-content\").load(this.options.remote,e.proxy(function(){this.$element.trigger(\"loaded.bs.modal\")},this))};n.VERSION=\"3.3.5\",n.TRANSITION_DURATION=300,n.BACKDROP_TRANSITION_DURATION=150,n.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},n.prototype.toggle=function(e){return this.isShown?this.hide():this.show(e)},n.prototype.show=function(t){var r=this,o=e.Event(\"show.bs.modal\",{relatedTarget:t});this.$element.trigger(o),this.isShown||o.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass(\"modal-open\"),this.escape(),this.resize(),this.$element.on(\"click.dismiss.bs.modal\",'[data-dismiss=\"modal\"]',e.proxy(this.hide,this)),this.$dialog.on(\"mousedown.dismiss.bs.modal\",function(){r.$element.one(\"mouseup.dismiss.bs.modal\",function(t){e(t.target).is(r.$element)&&(r.ignoreBackdropClick=!0)})}),this.backdrop(function(){var o=e.support.transition&&r.$element.hasClass(\"fade\");r.$element.parent().length||r.$element.appendTo(r.$body),r.$element.show().scrollTop(0),r.adjustDialog(),o&&r.$element[0].offsetWidth,r.$element.addClass(\"in\"),r.enforceFocus();var i=e.Event(\"shown.bs.modal\",{relatedTarget:t});o?r.$dialog.one(\"bsTransitionEnd\",function(){r.$element.trigger(\"focus\").trigger(i)}).emulateTransitionEnd(n.TRANSITION_DURATION):r.$element.trigger(\"focus\").trigger(i)}))},n.prototype.hide=function(t){t&&t.preventDefault(),t=e.Event(\"hide.bs.modal\"),this.$element.trigger(t),this.isShown&&!t.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),e(document).off(\"focusin.bs.modal\"),this.$element.removeClass(\"in\").off(\"click.dismiss.bs.modal\").off(\"mouseup.dismiss.bs.modal\"),this.$dialog.off(\"mousedown.dismiss.bs.modal\"),e.support.transition&&this.$element.hasClass(\"fade\")?this.$element.one(\"bsTransitionEnd\",e.proxy(this.hideModal,this)).emulateTransitionEnd(n.TRANSITION_DURATION):this.hideModal())},n.prototype.enforceFocus=function(){e(document).off(\"focusin.bs.modal\").on(\"focusin.bs.modal\",e.proxy(function(e){this.$element[0]===e.target||this.$element.has(e.target).length||this.$element.trigger(\"focus\")},this))},n.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on(\"keydown.dismiss.bs.modal\",e.proxy(function(e){27==e.which&&this.hide()},this)):this.isShown||this.$element.off(\"keydown.dismiss.bs.modal\")},n.prototype.resize=function(){this.isShown?e(window).on(\"resize.bs.modal\",e.proxy(this.handleUpdate,this)):e(window).off(\"resize.bs.modal\")},n.prototype.hideModal=function(){var e=this;this.$element.hide(),this.backdrop(function(){e.$body.removeClass(\"modal-open\"),e.resetAdjustments(),e.resetScrollbar(),e.$element.trigger(\"hidden.bs.modal\")})},n.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},n.prototype.backdrop=function(t){var r=this,o=this.$element.hasClass(\"fade\")?\"fade\":\"\";if(this.isShown&&this.options.backdrop){var i=e.support.transition&&o;if(this.$backdrop=e(document.createElement(\"div\")).addClass(\"modal-backdrop \"+o).appendTo(this.$body),this.$element.on(\"click.dismiss.bs.modal\",e.proxy(function(e){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(e.target===e.currentTarget&&(\"static\"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass(\"in\"),!t)return;i?this.$backdrop.one(\"bsTransitionEnd\",t).emulateTransitionEnd(n.BACKDROP_TRANSITION_DURATION):t()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass(\"in\");var a=function(){r.removeBackdrop(),t&&t()};e.support.transition&&this.$element.hasClass(\"fade\")?this.$backdrop.one(\"bsTransitionEnd\",a).emulateTransitionEnd(n.BACKDROP_TRANSITION_DURATION):a()}else t&&t()},n.prototype.handleUpdate=function(){this.adjustDialog()},n.prototype.adjustDialog=function(){var e=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&e?this.scrollbarWidth:\"\",paddingRight:this.bodyIsOverflowing&&!e?this.scrollbarWidth:\"\"})},n.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:\"\",paddingRight:\"\"})},n.prototype.checkScrollbar=function(){var e=window.innerWidth;if(!e){var t=document.documentElement.getBoundingClientRect();e=t.right-Math.abs(t.left)}this.bodyIsOverflowing=document.body.clientWidth<e,this.scrollbarWidth=this.measureScrollbar()},n.prototype.setScrollbar=function(){var e=parseInt(this.$body.css(\"padding-right\")||0,10);this.originalBodyPad=document.body.style.paddingRight||\"\",this.bodyIsOverflowing&&this.$body.css(\"padding-right\",e+this.scrollbarWidth)},n.prototype.resetScrollbar=function(){this.$body.css(\"padding-right\",this.originalBodyPad)},n.prototype.measureScrollbar=function(){var e=document.createElement(\"div\");e.className=\"modal-scrollbar-measure\",this.$body.append(e);var t=e.offsetWidth-e.clientWidth;return this.$body[0].removeChild(e),t};var r=e.fn.modal;e.fn.modal=t,e.fn.modal.Constructor=n,e.fn.modal.noConflict=function(){return e.fn.modal=r,this},e(document).on(\"click.bs.modal.data-api\",'[data-toggle=\"modal\"]',function(n){var r=e(this),o=r.attr(\"href\"),i=e(r.attr(\"data-target\")||o&&o.replace(/.*(?=#[^\\s]+$)/,\"\")),a=i.data(\"bs.modal\")?\"toggle\":e.extend({remote:!/#/.test(o)&&o},i.data(),r.data());r.is(\"a\")&&n.preventDefault(),i.one(\"show.bs.modal\",function(e){e.isDefaultPrevented()||i.one(\"hidden.bs.modal\",function(){r.is(\":visible\")&&r.trigger(\"focus\")})}),t.call(i,a,this)})}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.tooltip\"),i=\"object\"==typeof t&&t;(o||!/destroy|hide/.test(t))&&(o||r.data(\"bs.tooltip\",o=new n(this,i)),\"string\"==typeof t&&o[t]())})}var n=function(e,t){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init(\"tooltip\",e,t)};n.VERSION=\"3.3.5\",n.TRANSITION_DURATION=150,n.DEFAULTS={animation:!0,placement:\"top\",selector:!1,template:'<div class=\"tooltip\" role=\"tooltip\"><div class=\"tooltip-arrow\"></div><div class=\"tooltip-inner\"></div></div>',trigger:\"hover focus\",title:\"\",delay:0,html:!1,container:!1,viewport:{selector:\"body\",padding:0}},n.prototype.init=function(t,n,r){if(this.enabled=!0,this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.$viewport=this.options.viewport&&e(e.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error(\"`selector` option must be specified when initializing \"+this.type+\" on the window.document object!\");for(var o=this.options.trigger.split(\" \"),i=o.length;i--;){var a=o[i];if(\"click\"==a)this.$element.on(\"click.\"+this.type,this.options.selector,e.proxy(this.toggle,this));else if(\"manual\"!=a){var s=\"hover\"==a?\"mouseenter\":\"focusin\",l=\"hover\"==a?\"mouseleave\":\"focusout\";this.$element.on(s+\".\"+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(l+\".\"+this.type,this.options.selector,e.proxy(this.leave,this))}}this.options.selector?this._options=e.extend({},this.options,{trigger:\"manual\",selector:\"\"}):this.fixTitle()},n.prototype.getDefaults=function(){return n.DEFAULTS},n.prototype.getOptions=function(t){return t=e.extend({},this.getDefaults(),this.$element.data(),t),t.delay&&\"number\"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),t},n.prototype.getDelegateOptions=function(){var t={},n=this.getDefaults();return this._options&&e.each(this._options,function(e,r){n[e]!=r&&(t[e]=r)}),t},n.prototype.enter=function(t){var n=t instanceof this.constructor?t:e(t.currentTarget).data(\"bs.\"+this.type);return n||(n=new this.constructor(t.currentTarget,this.getDelegateOptions()),e(t.currentTarget).data(\"bs.\"+this.type,n)),t instanceof e.Event&&(n.inState[\"focusin\"==t.type?\"focus\":\"hover\"]=!0),n.tip().hasClass(\"in\")||\"in\"==n.hoverState?void(n.hoverState=\"in\"):(clearTimeout(n.timeout),n.hoverState=\"in\",n.options.delay&&n.options.delay.show?void(n.timeout=setTimeout(function(){\"in\"==n.hoverState&&n.show()},n.options.delay.show)):n.show())},n.prototype.isInStateTrue=function(){for(var e in this.inState)if(this.inState[e])return!0;return!1},n.prototype.leave=function(t){var n=t instanceof this.constructor?t:e(t.currentTarget).data(\"bs.\"+this.type);return n||(n=new this.constructor(t.currentTarget,this.getDelegateOptions()),e(t.currentTarget).data(\"bs.\"+this.type,n)),t instanceof e.Event&&(n.inState[\"focusout\"==t.type?\"focus\":\"hover\"]=!1),n.isInStateTrue()?void 0:(clearTimeout(n.timeout),n.hoverState=\"out\",n.options.delay&&n.options.delay.hide?void(n.timeout=setTimeout(function(){\"out\"==n.hoverState&&n.hide()},n.options.delay.hide)):n.hide())},n.prototype.show=function(){var t=e.Event(\"show.bs.\"+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(t);var r=e.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(t.isDefaultPrevented()||!r)return;var o=this,i=this.tip(),a=this.getUID(this.type);this.setContent(),i.attr(\"id\",a),this.$element.attr(\"aria-describedby\",a),this.options.animation&&i.addClass(\"fade\");var s=\"function\"==typeof this.options.placement?this.options.placement.call(this,i[0],this.$element[0]):this.options.placement,l=/\\s?auto?\\s?/i,c=l.test(s);c&&(s=s.replace(l,\"\")||\"top\"),i.detach().css({top:0,left:0,display:\"block\"}).addClass(s).data(\"bs.\"+this.type,this),this.options.container?i.appendTo(this.options.container):i.insertAfter(this.$element),this.$element.trigger(\"inserted.bs.\"+this.type);var u=this.getPosition(),d=i[0].offsetWidth,p=i[0].offsetHeight;if(c){var h=s,g=this.getPosition(this.$viewport);s=\"bottom\"==s&&u.bottom+p>g.bottom?\"top\":\"top\"==s&&u.top-p<g.top?\"bottom\":\"right\"==s&&u.right+d>g.width?\"left\":\"left\"==s&&u.left-d<g.left?\"right\":s,i.removeClass(h).addClass(s)}var f=this.getCalculatedOffset(s,u,d,p);this.applyPlacement(f,s);var m=function(){var e=o.hoverState;o.$element.trigger(\"shown.bs.\"+o.type),o.hoverState=null,\"out\"==e&&o.leave(o)};e.support.transition&&this.$tip.hasClass(\"fade\")?i.one(\"bsTransitionEnd\",m).emulateTransitionEnd(n.TRANSITION_DURATION):m()}},n.prototype.applyPlacement=function(t,n){var r=this.tip(),o=r[0].offsetWidth,i=r[0].offsetHeight,a=parseInt(r.css(\"margin-top\"),10),s=parseInt(r.css(\"margin-left\"),10);isNaN(a)&&(a=0),isNaN(s)&&(s=0),t.top+=a,t.left+=s,e.offset.setOffset(r[0],e.extend({using:function(e){r.css({top:Math.round(e.top),left:Math.round(e.left)})}},t),0),r.addClass(\"in\");var l=r[0].offsetWidth,c=r[0].offsetHeight;\"top\"==n&&c!=i&&(t.top=t.top+i-c);var u=this.getViewportAdjustedDelta(n,t,l,c);u.left?t.left+=u.left:t.top+=u.top;var d=/top|bottom/.test(n),p=d?2*u.left-o+l:2*u.top-i+c,h=d?\"offsetWidth\":\"offsetHeight\";r.offset(t),this.replaceArrow(p,r[0][h],d)},n.prototype.replaceArrow=function(e,t,n){this.arrow().css(n?\"left\":\"top\",50*(1-e/t)+\"%\").css(n?\"top\":\"left\",\"\")},n.prototype.setContent=function(){var e=this.tip(),t=this.getTitle();e.find(\".tooltip-inner\")[this.options.html?\"html\":\"text\"](t),e.removeClass(\"fade in top bottom left right\")},n.prototype.hide=function(t){function r(){\"in\"!=o.hoverState&&i.detach(),o.$element.removeAttr(\"aria-describedby\").trigger(\"hidden.bs.\"+o.type),t&&t()}var o=this,i=e(this.$tip),a=e.Event(\"hide.bs.\"+this.type);return this.$element.trigger(a),a.isDefaultPrevented()?void 0:(i.removeClass(\"in\"),e.support.transition&&i.hasClass(\"fade\")?i.one(\"bsTransitionEnd\",r).emulateTransitionEnd(n.TRANSITION_DURATION):r(),this.hoverState=null,this)},n.prototype.fixTitle=function(){var e=this.$element;(e.attr(\"title\")||\"string\"!=typeof e.attr(\"data-original-title\"))&&e.attr(\"data-original-title\",e.attr(\"title\")||\"\").attr(\"title\",\"\")},n.prototype.hasContent=function(){return this.getTitle()},n.prototype.getPosition=function(t){t=t||this.$element;var n=t[0],r=\"BODY\"==n.tagName,o=n.getBoundingClientRect();null==o.width&&(o=e.extend({},o,{width:o.right-o.left,height:o.bottom-o.top}));var i=r?{top:0,left:0}:t.offset(),a={scroll:r?document.documentElement.scrollTop||document.body.scrollTop:t.scrollTop()},s=r?{width:e(window).width(),height:e(window).height()}:null;return e.extend({},o,a,s,i)},n.prototype.getCalculatedOffset=function(e,t,n,r){return\"bottom\"==e?{top:t.top+t.height,left:t.left+t.width/2-n/2}:\"top\"==e?{top:t.top-r,left:t.left+t.width/2-n/2}:\"left\"==e?{top:t.top+t.height/2-r/2,left:t.left-n}:{top:t.top+t.height/2-r/2,left:t.left+t.width}},n.prototype.getViewportAdjustedDelta=function(e,t,n,r){var o={top:0,left:0};if(!this.$viewport)return o;var i=this.options.viewport&&this.options.viewport.padding||0,a=this.getPosition(this.$viewport);if(/right|left/.test(e)){var s=t.top-i-a.scroll,l=t.top+i-a.scroll+r;s<a.top?o.top=a.top-s:l>a.top+a.height&&(o.top=a.top+a.height-l)}else{var c=t.left-i,u=t.left+i+n;c<a.left?o.left=a.left-c:u>a.right&&(o.left=a.left+a.width-u)}return o},n.prototype.getTitle=function(){var e,t=this.$element,n=this.options;return e=t.attr(\"data-original-title\")||(\"function\"==typeof n.title?n.title.call(t[0]):n.title)},n.prototype.getUID=function(e){do e+=~~(1e6*Math.random());while(document.getElementById(e));return e},n.prototype.tip=function(){if(!this.$tip&&(this.$tip=e(this.options.template),1!=this.$tip.length))throw new Error(this.type+\" `template` option must consist of exactly 1 top-level element!\");return this.$tip},n.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(\".tooltip-arrow\")},n.prototype.enable=function(){this.enabled=!0},n.prototype.disable=function(){this.enabled=!1},n.prototype.toggleEnabled=function(){this.enabled=!this.enabled},n.prototype.toggle=function(t){var n=this;t&&(n=e(t.currentTarget).data(\"bs.\"+this.type),n||(n=new this.constructor(t.currentTarget,this.getDelegateOptions()),e(t.currentTarget).data(\"bs.\"+this.type,n))),t?(n.inState.click=!n.inState.click,n.isInStateTrue()?n.enter(n):n.leave(n)):n.tip().hasClass(\"in\")?n.leave(n):n.enter(n)},n.prototype.destroy=function(){var e=this;clearTimeout(this.timeout),this.hide(function(){e.$element.off(\".\"+e.type).removeData(\"bs.\"+e.type),e.$tip&&e.$tip.detach(),e.$tip=null,e.$arrow=null,e.$viewport=null})};var r=e.fn.tooltip;e.fn.tooltip=t,e.fn.tooltip.Constructor=n,e.fn.tooltip.noConflict=function(){return e.fn.tooltip=r,this}}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.popover\"),i=\"object\"==typeof t&&t;(o||!/destroy|hide/.test(t))&&(o||r.data(\"bs.popover\",o=new n(this,i)),\"string\"==typeof t&&o[t]())})}var n=function(e,t){this.init(\"popover\",e,t)};if(!e.fn.tooltip)throw new Error(\"Popover requires tooltip.js\");n.VERSION=\"3.3.5\",n.DEFAULTS=e.extend({},e.fn.tooltip.Constructor.DEFAULTS,{placement:\"right\",trigger:\"click\",content:\"\",template:'<div class=\"popover\" role=\"tooltip\"><div class=\"arrow\"></div><h3 class=\"popover-title\"></h3><div class=\"popover-content\"></div></div>'}),n.prototype=e.extend({},e.fn.tooltip.Constructor.prototype),n.prototype.constructor=n,n.prototype.getDefaults=function(){return n.DEFAULTS},n.prototype.setContent=function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(\".popover-title\")[this.options.html?\"html\":\"text\"](t),e.find(\".popover-content\").children().detach().end()[this.options.html?\"string\"==typeof n?\"html\":\"append\":\"text\"](n),e.removeClass(\"fade top bottom left right in\"),e.find(\".popover-title\").html()||e.find(\".popover-title\").hide()},n.prototype.hasContent=function(){return this.getTitle()||this.getContent()},n.prototype.getContent=function(){var e=this.$element,t=this.options;return e.attr(\"data-content\")||(\"function\"==typeof t.content?t.content.call(e[0]):t.content)},n.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(\".arrow\")};var r=e.fn.popover;e.fn.popover=t,e.fn.popover.Constructor=n,e.fn.popover.noConflict=function(){return e.fn.popover=r,this}}(jQuery),+function(e){\"use strict\";function t(n,r){this.$body=e(document.body),this.$scrollElement=e(e(n).is(document.body)?window:n),this.options=e.extend({},t.DEFAULTS,r),this.selector=(this.options.target||\"\")+\" .nav li > a\",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on(\"scroll.bs.scrollspy\",e.proxy(this.process,this)),this.refresh(),this.process()}function n(n){return this.each(function(){var r=e(this),o=r.data(\"bs.scrollspy\"),i=\"object\"==typeof n&&n;o||r.data(\"bs.scrollspy\",o=new t(this,i)),\"string\"==typeof n&&o[n]()})}t.VERSION=\"3.3.5\",t.DEFAULTS={offset:10},t.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},t.prototype.refresh=function(){var t=this,n=\"offset\",r=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),e.isWindow(this.$scrollElement[0])||(n=\"position\",r=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var t=e(this),o=t.data(\"target\")||t.attr(\"href\"),i=/^#./.test(o)&&e(o);return i&&i.length&&i.is(\":visible\")&&[[i[n]().top+r,o]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},t.prototype.process=function(){var e,t=this.$scrollElement.scrollTop()+this.options.offset,n=this.getScrollHeight(),r=this.options.offset+n-this.$scrollElement.height(),o=this.offsets,i=this.targets,a=this.activeTarget;if(this.scrollHeight!=n&&this.refresh(),t>=r)return a!=(e=i[i.length-1])&&this.activate(e);if(a&&t<o[0])return this.activeTarget=null,this.clear();for(e=o.length;e--;)a!=i[e]&&t>=o[e]&&(void 0===o[e+1]||t<o[e+1])&&this.activate(i[e])},t.prototype.activate=function(t){this.activeTarget=t,this.clear();var n=this.selector+'[data-target=\"'+t+'\"],'+this.selector+'[href=\"'+t+'\"]',r=e(n).parents(\"li\").addClass(\"active\");r.parent(\".dropdown-menu\").length&&(r=r.closest(\"li.dropdown\").addClass(\"active\")),\nr.trigger(\"activate.bs.scrollspy\")},t.prototype.clear=function(){e(this.selector).parentsUntil(this.options.target,\".active\").removeClass(\"active\")};var r=e.fn.scrollspy;e.fn.scrollspy=n,e.fn.scrollspy.Constructor=t,e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=r,this},e(window).on(\"load.bs.scrollspy.data-api\",function(){e('[data-spy=\"scroll\"]').each(function(){var t=e(this);n.call(t,t.data())})})}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.tab\");o||r.data(\"bs.tab\",o=new n(this)),\"string\"==typeof t&&o[t]()})}var n=function(t){this.element=e(t)};n.VERSION=\"3.3.5\",n.TRANSITION_DURATION=150,n.prototype.show=function(){var t=this.element,n=t.closest(\"ul:not(.dropdown-menu)\"),r=t.data(\"target\");if(r||(r=t.attr(\"href\"),r=r&&r.replace(/.*(?=#[^\\s]*$)/,\"\")),!t.parent(\"li\").hasClass(\"active\")){var o=n.find(\".active:last a\"),i=e.Event(\"hide.bs.tab\",{relatedTarget:t[0]}),a=e.Event(\"show.bs.tab\",{relatedTarget:o[0]});if(o.trigger(i),t.trigger(a),!a.isDefaultPrevented()&&!i.isDefaultPrevented()){var s=e(r);this.activate(t.closest(\"li\"),n),this.activate(s,s.parent(),function(){o.trigger({type:\"hidden.bs.tab\",relatedTarget:t[0]}),t.trigger({type:\"shown.bs.tab\",relatedTarget:o[0]})})}}},n.prototype.activate=function(t,r,o){function i(){a.removeClass(\"active\").find(\"> .dropdown-menu > .active\").removeClass(\"active\").end().find('[data-toggle=\"tab\"]').attr(\"aria-expanded\",!1),t.addClass(\"active\").find('[data-toggle=\"tab\"]').attr(\"aria-expanded\",!0),s?(t[0].offsetWidth,t.addClass(\"in\")):t.removeClass(\"fade\"),t.parent(\".dropdown-menu\").length&&t.closest(\"li.dropdown\").addClass(\"active\").end().find('[data-toggle=\"tab\"]').attr(\"aria-expanded\",!0),o&&o()}var a=r.find(\"> .active\"),s=o&&e.support.transition&&(a.length&&a.hasClass(\"fade\")||!!r.find(\"> .fade\").length);a.length&&s?a.one(\"bsTransitionEnd\",i).emulateTransitionEnd(n.TRANSITION_DURATION):i(),a.removeClass(\"in\")};var r=e.fn.tab;e.fn.tab=t,e.fn.tab.Constructor=n,e.fn.tab.noConflict=function(){return e.fn.tab=r,this};var o=function(n){n.preventDefault(),t.call(e(this),\"show\")};e(document).on(\"click.bs.tab.data-api\",'[data-toggle=\"tab\"]',o).on(\"click.bs.tab.data-api\",'[data-toggle=\"pill\"]',o)}(jQuery),+function(e){\"use strict\";function t(t){return this.each(function(){var r=e(this),o=r.data(\"bs.affix\"),i=\"object\"==typeof t&&t;o||r.data(\"bs.affix\",o=new n(this,i)),\"string\"==typeof t&&o[t]()})}var n=function(t,r){this.options=e.extend({},n.DEFAULTS,r),this.$target=e(this.options.target).on(\"scroll.bs.affix.data-api\",e.proxy(this.checkPosition,this)).on(\"click.bs.affix.data-api\",e.proxy(this.checkPositionWithEventLoop,this)),this.$element=e(t),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};n.VERSION=\"3.3.5\",n.RESET=\"affix affix-top affix-bottom\",n.DEFAULTS={offset:0,target:window},n.prototype.getState=function(e,t,n,r){var o=this.$target.scrollTop(),i=this.$element.offset(),a=this.$target.height();if(null!=n&&\"top\"==this.affixed)return n>o?\"top\":!1;if(\"bottom\"==this.affixed)return null!=n?o+this.unpin<=i.top?!1:\"bottom\":e-r>=o+a?!1:\"bottom\";var s=null==this.affixed,l=s?o:i.top,c=s?a:t;return null!=n&&n>=o?\"top\":null!=r&&l+c>=e-r?\"bottom\":!1},n.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(n.RESET).addClass(\"affix\");var e=this.$target.scrollTop(),t=this.$element.offset();return this.pinnedOffset=t.top-e},n.prototype.checkPositionWithEventLoop=function(){setTimeout(e.proxy(this.checkPosition,this),1)},n.prototype.checkPosition=function(){if(this.$element.is(\":visible\")){var t=this.$element.height(),r=this.options.offset,o=r.top,i=r.bottom,a=Math.max(e(document).height(),e(document.body).height());\"object\"!=typeof r&&(i=o=r),\"function\"==typeof o&&(o=r.top(this.$element)),\"function\"==typeof i&&(i=r.bottom(this.$element));var s=this.getState(a,t,o,i);if(this.affixed!=s){null!=this.unpin&&this.$element.css(\"top\",\"\");var l=\"affix\"+(s?\"-\"+s:\"\"),c=e.Event(l+\".bs.affix\");if(this.$element.trigger(c),c.isDefaultPrevented())return;this.affixed=s,this.unpin=\"bottom\"==s?this.getPinnedOffset():null,this.$element.removeClass(n.RESET).addClass(l).trigger(l.replace(\"affix\",\"affixed\")+\".bs.affix\")}\"bottom\"==s&&this.$element.offset({top:a-t-i})}};var r=e.fn.affix;e.fn.affix=t,e.fn.affix.Constructor=n,e.fn.affix.noConflict=function(){return e.fn.affix=r,this},e(window).on(\"load\",function(){e('[data-spy=\"affix\"]').each(function(){var n=e(this),r=n.data();r.offset=r.offset||{},null!=r.offsetBottom&&(r.offset.bottom=r.offsetBottom),null!=r.offsetTop&&(r.offset.top=r.offsetTop),t.call(n,r)})})}(jQuery)},function(e,t,n){var r=n(21);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-menu{height:34px;border-bottom:1px solid var(--c-border);background:var(--b-title);padding-right:5pc;position:relative;z-index:var(--z-menu);white-space:nowrap}.w-menu a{text-decoration:none!important;color:var(--c-heavy);padding:0 8px;line-height:26px;height:26px;display:inline-block;height:31px;line-height:31px;overflow:hidden}.w-detail .glyphicon,.w-menu .glyphicon{margin-right:3px}.w-menu .glyphicon-folder-close,.w-menu .glyphicon-folder-open{margin-right:6px}.w-menu .w-online{position:absolute;top:0;right:0}.w-left-menu a:hover,.w-left-menu a:hover .w-disabled,.w-menu .w-online,.w-menu a:hover{color:var(--c-link)}.w-left-menu a:hover,.w-menu a:hover{background:var(--b-btn-hover)}.w-menu .w-offline{color:var(--c-disabled)!important;cursor:default}.w-frames-action .w-disabled,.w-menu .w-disabled{color:var(--c-disabled)!important;cursor:not-allowed!important}.w-left-menu .w-disabled{color:var(--c-disabled)}.w-menu .w-menu-enable .glyphicon-cog{color:var(--c-risk)!important}.w-menu .w-new-version-icon{position:absolute;top:2px;left:17px;width:5px;height:5px;border-radius:5px;background:var(--c-error)}.w-menu .w-menu-wrapper{display:inline-block;position:relative;height:30px}.w-show-update-tips{font-weight:700}.w-input-menu-item{display:block;position:absolute;background:var(--b-default);z-index:101;top:33px;display:none;white-space:nowrap}.w-input-menu-item,.w-input-menu-item input{border:1px solid var(--c-border);border-radius:2px}.w-input-menu-item input{width:246px;height:2pc;border-radius:0!important;padding:0 5px;vertical-align:middle}.w-input-menu-item .btn{height:2pc;padding:0 9pt;vertical-align:middle;border-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;margin-left:1px;border-color:var(--c-border)}.w-create-rules-input{left:310px}.w-create-values-input{left:300px}.w-edit-rules-input{left:382px}.w-edit-values-input{left:372px}.w-show-left-menu .w-create-rules-input{left:210px}.w-show-left-menu .w-create-values-input{left:200px}.w-show-left-menu .w-edit-rules-input{left:282px}.w-show-left-menu .w-edit-values-input{left:17pc}.w-menu-wrapper .w-menu-item{display:none!important}.w-menu-wrapper-show .w-menu-item{display:block!important}.w-values-list .glyphicon-ok{display:none!important}.w-values-list a{font-weight:400!important}.w-disabled .glyphicon-ok,.w-disabled .glyphicon-stop{color:var(--c-disabled)!important}.w-rules-settings-dialog .modal-dialog{width:25pc}.w-values-settings-dialog .modal-dialog{width:300px}.w-show-update-tips-dialog .modal-dialog{width:21pc}.w-show-update-tips-dialog .modal-body p{margin-bottom:5px}.w-https-dialog .modal-dialog{width:342px}.w-https-help{margin-left:150px}.w-https-dialog .modal-dialog div{white-space:nowrap}.w-https-dialog a{display:inline-block}.w-download-rootca,.w-https-help{line-height:30px}.w-https-settings label{font-weight:400}.w-https-settings p{margin:10px 0}.w-is-link{cursor:pointer;text-decoration:underline}.w-choose-filte-type .modal-dialog{width:435px}.w-choose-filte-type-label{white-space:nowrap}.w-choose-filte-type-label .form-control{margin-left:10px;width:260px;display:inline-block;font-weight:400}.w-choose-filte-type-label select.form-control{margin:0 5px;width:5pc}.w-confirm-import-dialog .modal-dialog{width:20pc;font-weight:700}.w-switch-layout{padding:0 15px!important;margin-right:0!important}.w-left-menu{background:var(--b-title);border-left:1px solid var(--c-border);border-right:1px solid var(--c-border);display:none}.w-show-left-menu .w-left-menu{display:block;border-left:none}.w-show-left-menu .w-nav-menu{display:none}.w-left-menu a{display:block;position:relative;padding:10px 0 8px;width:47px;overflow:hidden;text-align:center;text-decoration:none!important;color:var(--c-default)}.w-left-menu a span{margin-right:5px}.w-left-menu a .w-left-menu-name{display:block;font-style:normal;font-size:9pt;zoom:1;transform:scale(0.8,0.8);width:65px;margin-left:-10px;white-space:nowrap}.w-left-menu-tips{padding:6px 10px;display:none;left:47px;top:3px;position:absolute;z-index:1;border-radius:4px}.w-left-menu a:hover .w-left-menu-tips{display:block;color:var(--c-default);background-color:var(--b-title);font-weight:700;border:1px solid var(--c-border)}.w-left-menu a .w-left-menu-tips:hover,.w-menu .w-export-menu,.w-menu .w-import-menu,.w-menu .w-remove-menu-list{display:none}.w-show-left-menu .w-export-menu,.w-show-left-menu .w-import-menu,.w-show-left-menu .w-remove-menu-list{display:inline-block}.w-menu-changed{position:absolute;top:2px;left:5px;color:var(--c-error)}.w-replay-count-dialog .modal-content{width:236px!important;white-space:nowrap}.w-replay-count-dialog .modal-dialog{width:236px!important}.w-replay-count-dialog .modal-content label{width:auto}.w-replay-count-dialog input{display:inline-block;width:5pc;text-align:center;margin:0 10px}.w-import-remote-dialog .modal-content{width:525px}.w-import-remote-dialog input{width:500px;height:30px;line-height:30px;padding:5px;border:1px solid var(--c-border)}.w-enable-https-btn,.w-https-menu .glyphicon-lock{color:var(--c-forbidden)}.w-enable-https-btn{cursor:pointer}.w-hover-left-menu{position:absolute;z-index:1;height:auto;border-bottom:1px solid var(--c-border);min-height:0}.w-menu .w-tree-view-active{color:var(--c-link)}.w-menu-selected{background-color:var(--b-active)!important;color:var(--c-default)!important}.w-https-dialog select{border:1px solid var(--c-border);border-radius:3px;height:24px;font-size:9pt;padding-left:5px}.w-root-ca-type{width:92px;float:right;margin:3px 15px 0 0}.w-root-ca-url-wrap{display:flex;align-items:center;margin:-3px 0 10px}.w-root-ca-url-wrap .glyphicon{margin-top:-2px}.w-root-ca-url{display:block;width:292px}.w-fieldset pre{max-height:220px;overflow:auto;padding:10px}.w-fieldset{border:1px solid var(--c-border)}.w-fieldset legend{font-size:9pt;padding:0 5px;border:none;width:auto;margin:0 10px;font-weight:700}.w-fieldset legend .glyphicon{margin-left:5px;cursor:pointer}@media screen and (max-width:1105px){.w-menu.w-top a{padding:0 7px}}.w-show-left-menu .w-help-name,.w-show-left-menu .w-https-name{display:inline!important}.w-va-mdl{vertical-align:middle}.w-mrl-5{margin-left:5px}.w-json-bar{position:absolute;top:5px;left:10px;font-size:18px}.w-json-bar .w-disabled{color:var(--c-disabled)!important;background:transparent!important}.w-json-bar .glyphicon-menu-right{margin-left:15px}.w-json-bar .glyphicon{display:inline-block;width:30px;height:30px;line-height:30px;text-align:center}.w-json-bar .glyphicon:hover{border-radius:30px;background:var(--b-title)}.w-import-remote-dialog .glyphicon-cloud{vertical-align:middle;padding-bottom:1px;margin-right:5px}.w-qrcode-wrap{display:flex;justify-content:center;align-items:center;color:var(--c-thin);cursor:default}.w-qrcode-wrap img{width:100%;height:100%}.w-hover-body tr:hover td,.w-hover-body tr:hover th{background:var(--b-hover)}.w-req-table .w-hover-body tr:hover td,.w-req-table .w-hover-body tr:hover th{background:var(--b-list-hover)!important}.w-hover-body tr.warning:hover td,.w-hover-body tr.warning:hover th{background:var(--b-list-warn)!important}.w-hover-body tr.info:hover td,.w-hover-body tr.info:hover th{background:var(--b-list-info)!important}.w-hover-body tr.success:hover td,.w-hover-body tr.success:hover th{background:var(--b-success-hover)!important}.w-hover-body tr.danger:hover td,.w-hover-body tr.danger:hover th{background:var(--b-list-danger-hover)!important}.w-hover-body tr.w-mark:hover td,.w-hover-body tr.w-mark:hover th{background:var(--b-list-mark)!important}.w-confim-reload-note{color:var(--c-error);margin:0}\",\"\"])},function(e,t,n){var r=n(23);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\"[data-theme=dark]:root{color-scheme:dark;--c-default:#f0f0f0;--c-btn:#eee;--c-active:#1a1a1a;--c-heavy:#ddd;--c-editor:#ccc;--c-forbidden:#c0c060;--c-disabled:#666;--c-link:#4a9fe3;--c-link-hover:#7bb8f0;--c-border:#444;--c-error:#ff6b6b;--c-risk:#ff8a8a;--c-danger:#ff6da9;--c-gray:#aaa;--c-thin:#ccc;--c-ok:#70d485;--c-has:#7a7aff;--c-warn:#fc4;--c-warn-active:#ffe066;--c-post:#6da9df;--c-del:#e57c6d;--c-head:#6dc8b2;--c-options:#ffc266;--c-trace:#6cd3ec;--c-patch:#b080d0;--c-put:#f0b55a;--c-tag:#6cd3ec;--c-tagx:#f0c066;--c-rule-filter:#a580e8;--c-rule-props:#ffb366;--c-req:#4c4;--c-res:#ff8c42;--c-tree-success:#6dc291;--c-tree-info:#6db0d0;--c-tree-warning:#d4b06d;--c-tree-danger:#e57c6d;--c-tree-forbidden:#e5a844;--c-tree-mark:#5a9cf5;--c-j0:#0a0f14;--c-j1:#101a20;--c-j2:#2d3b42;--c-j3:#3a4a52;--c-j4:#5a6a70;--c-j5:#7a8a8a;--c-j6:#e8dbb5;--c-j7:#f6e9c3;--c-j8:#ff6b6b;--c-j9:#ff8c42;--c-ja:#fc4;--c-jb:#9c6;--c-jc:#4cc;--c-jd:#5a9cf5;--c-je:#9d8ef0;--c-jf:#f06da3;--c-success:#5cb85c;--c-primary:#6da8ff;--c-primary-hover:#8bbdff;--c-primary-active:#a9d1ff;--c-danger-hover:#ff8a8a;--c-danger-active:#ff4d4d;--c-warning:#fc4;--c-warning-hover:#ffd966;--c-warning-active:#ffe599;--c-info:#6cd3ec;--c-info-hover:#8bddf2;--c-info-active:#a9e7f7;--c-cobalt:#e6e6e6;--b-success:#1e3c21;--b-success-hover:#3a4a38;--b-warning:#5c3b00;--b-warning-hover:#7a4d00;--b-warning-active:#996000;--b-info:#0d2b3d;--b-info-hover:#124a8a;--b-info-active:#1659a8;--b-primary:#0d3a6b;--b-primary-hover:#124a8a;--b-primary-active:#1659a8;--b-danger:#5c1a1a;--b-danger-hover:#7a2222;--b-danger-active:#4d0a0a;--b-alert-info:#0d2b3d;--b-default:#1a1a1a;--b-error:#3a2222;--b-error-hover:#4a2a2a;--b-active:#3f3f3f;--b-gray:#333;--b-btn-hover:#333;--b-btn-active:#1e2c4a;--b-hover:#333;--b-heavy:#333;--b-heavy-active:#2d2d2d;--b-disabled:#333;--b-bar:#212121;--b-mark:#1a3a7a;--b-title:#222427;--b-img:#1a1a1a;--b-frames:#c60;--b-prop:#2a2d30;--b-ok:#2a4a2a;--b-editor:#001a33;--b-filtered:#332;--b-warn:#332e22;--b-req:#232;--b-req-hover:#2d402d;--b-blink:#665c00;--b-tree-active:#4a6a4a;--b-tl-dns:#2a4a45;--b-tl-req:#332;--b-tl-res:#332a22;--b-tl-load:#2a3a4a;--b-list-hover:#444;--b-list-warn:#40392a;--b-list-info:#22333a;--b-list-mark:#1a3a7a;--b-list-danger-hover:#6a2323;--b-modal:rgba(0,0,0,.7);--b-cobalt:#012;--s-rgba5:hsla(0,0%,100%,.08);--s-rgba8:hsla(0,0%,100%,.12);--s-rgba12:hsla(0,0%,100%,.18);--s-rgba15:hsla(0,0%,100%,.24);--s-rgba20:hsla(0,0%,100%,.32);--s-fc:rgba(77,140,190,.65);--b-rgba50:hsla(0,0%,100%,.12);--b-rgba70:hsla(0,0%,100%,.15);--b-rgba15:hsla(0,0%,100%,.06);--b-rgba30:hsla(0,0%,100%,.08)}html[data-theme=dark] .w-filter-input{background:field;color:var(--c-thin)}\",\"\"])},function(e,t,n){\"use strict\";e.exports=n(25)},function(e,t,n){\"use strict\";var r=n(26),o=n(27),i=n(36),a=n(44),s=n(38),l=n(45),c=n(53),u=n(54),d=n(56),p=s.createElement,h=s.createFactory,g=s.cloneElement,f=r,m=function(e){return e},A={Children:{map:i.map,forEach:i.forEach,count:i.count,toArray:i.toArray,only:d},Component:o.Component,PureComponent:o.PureComponent,createElement:p,cloneElement:g,isValidElement:s.isValidElement,PropTypes:l,createClass:u,createFactory:h,createMixin:m,DOM:a,version:c,__spread:f};e.exports=A},function(e,t){/*\n\tobject-assign\n\t(c) Sindre Sorhus\n\t@license MIT\n\t*/\n\"use strict\";function n(e){if(null===e||void 0===e)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(e)}function r(){try{if(!Object.assign)return!1;var e=new String(\"abc\");if(e[5]=\"de\",\"5\"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;10>n;n++)t[\"_\"+String.fromCharCode(n)]=n;var r=Object.getOwnPropertyNames(t).map(function(e){return t[e]});if(\"0123456789\"!==r.join(\"\"))return!1;var o={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(e){o[e]=e}),\"abcdefghijklmnopqrst\"!==Object.keys(Object.assign({},o)).join(\"\")?!1:!0}catch(i){return!1}}var o=Object.getOwnPropertySymbols,i=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;e.exports=r()?Object.assign:function(e,t){for(var r,s,l=n(e),c=1;c<arguments.length;c++){r=Object(arguments[c]);for(var u in r)i.call(r,u)&&(l[u]=r[u]);if(o){s=o(r);for(var d=0;d<s.length;d++)a.call(r,s[d])&&(l[s[d]]=r[s[d]])}}return l}},function(e,t,n){\"use strict\";function r(e,t,n){this.props=e,this.context=t,this.refs=c,this.updater=n||l}function o(e,t,n){this.props=e,this.context=t,this.refs=c,this.updater=n||l}function i(){}var a=n(28),s=n(26),l=n(29),c=(n(32),n(33));n(34),n(35);r.prototype.isReactComponent={},r.prototype.setState=function(e,t){\"object\"!=typeof e&&\"function\"!=typeof e&&null!=e?a(\"85\"):void 0,this.updater.enqueueSetState(this,e),t&&this.updater.enqueueCallback(this,t,\"setState\")},r.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this),e&&this.updater.enqueueCallback(this,e,\"forceUpdate\")};i.prototype=r.prototype,o.prototype=new i,o.prototype.constructor=o,s(o.prototype,r.prototype),o.prototype.isPureReactComponent=!0,e.exports={Component:r,PureComponent:o}},function(e,t){\"use strict\";function n(e){for(var t=arguments.length-1,n=\"Minified React error #\"+e+\"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=\"+e,r=0;t>r;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r+1]);n+=\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\";var o=new Error(n);throw o.name=\"Invariant Violation\",o.framesToPop=1,o}e.exports=n},function(e,t,n){\"use strict\";function r(e,t){}var o=(n(30),{isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){r(e,\"forceUpdate\")},enqueueReplaceState:function(e,t){r(e,\"replaceState\")},enqueueSetState:function(e,t){r(e,\"setState\")}});e.exports=o},function(e,t,n){\"use strict\";var r=n(31),o=r;e.exports=o},function(e,t){\"use strict\";function n(e){return function(){return e}}var r=function(){};r.thatReturns=n,r.thatReturnsFalse=n(!1),r.thatReturnsTrue=n(!0),r.thatReturnsNull=n(null),r.thatReturnsThis=function(){return this},r.thatReturnsArgument=function(e){return e},e.exports=r},function(e,t,n){\"use strict\";var r=!1;e.exports=r},function(e,t,n){\"use strict\";var r={};e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r,i,a,s,l){if(o(t),!e){var c;if(void 0===t)c=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var u=[n,r,i,a,s,l],d=0;c=new Error(t.replace(/%s/g,function(){return u[d++]})),c.name=\"Invariant Violation\"}throw c.framesToPop=1,c}}var o=function(e){};e.exports=r},function(e,t,n){\"use strict\";var r=function(){};e.exports=r},function(e,t,n){\"use strict\";function r(e){return(\"\"+e).replace(v,\"$&/\")}function o(e,t){this.func=e,this.context=t,this.count=0}function i(e,t,n){var r=e.func,o=e.context;r.call(o,t,e.count++)}function a(e,t,n){if(null==e)return e;var r=o.getPooled(t,n);A(e,i,r),o.release(r)}function s(e,t,n,r){this.result=e,this.keyPrefix=t,this.func=n,this.context=r,this.count=0}function l(e,t,n){var o=e.result,i=e.keyPrefix,a=e.func,s=e.context,l=a.call(s,t,e.count++);Array.isArray(l)?c(l,o,n,m.thatReturnsArgument):null!=l&&(f.isValidElement(l)&&(l=f.cloneAndReplaceKey(l,i+(!l.key||t&&t.key===l.key?\"\":r(l.key)+\"/\")+n)),o.push(l))}function c(e,t,n,o,i){var a=\"\";null!=n&&(a=r(n)+\"/\");var c=s.getPooled(t,a,o,i);A(e,l,c),s.release(c)}function u(e,t,n){if(null==e)return e;var r=[];return c(e,r,null,t,n),r}function d(e,t,n){return null}function p(e,t){return A(e,d,null)}function h(e){var t=[];return c(e,t,null,m.thatReturnsArgument),t}var g=n(37),f=n(38),m=n(31),A=n(41),M=g.twoArgumentPooler,w=g.fourArgumentPooler,v=/\\/+/g;o.prototype.destructor=function(){this.func=null,this.context=null,this.count=0},g.addPoolingTo(o,M),s.prototype.destructor=function(){this.result=null,this.keyPrefix=null,this.func=null,this.context=null,this.count=0},g.addPoolingTo(s,w);var b={forEach:a,map:u,mapIntoWithKeyPrefixInternal:c,count:p,toArray:h};e.exports=b},function(e,t,n){\"use strict\";var r=n(28),o=(n(34),function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)}),i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},s=function(e,t,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,e,t,n,r),i}return new o(e,t,n,r)},l=function(e){var t=this;e instanceof t?void 0:r(\"25\"),e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},c=10,u=o,d=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||u,n.poolSize||(n.poolSize=c),n.release=l,n},p={addPoolingTo:d,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fourArgumentPooler:s};e.exports=p},function(e,t,n){\"use strict\";function r(e){return void 0!==e.ref}function o(e){return void 0!==e.key}var i=n(26),a=n(39),s=(n(30),n(32),Object.prototype.hasOwnProperty),l=n(40),c={key:!0,ref:!0,__self:!0,__source:!0},u=function(e,t,n,r,o,i,a){var s={$$typeof:l,type:e,key:t,ref:n,props:a,_owner:i};return s};u.createElement=function(e,t,n){var i,l={},d=null,p=null,h=null,g=null;if(null!=t){r(t)&&(p=t.ref),o(t)&&(d=\"\"+t.key),h=void 0===t.__self?null:t.__self,g=void 0===t.__source?null:t.__source;for(i in t)s.call(t,i)&&!c.hasOwnProperty(i)&&(l[i]=t[i])}var f=arguments.length-2;if(1===f)l.children=n;else if(f>1){for(var m=Array(f),A=0;f>A;A++)m[A]=arguments[A+2];l.children=m}if(e&&e.defaultProps){var M=e.defaultProps;for(i in M)void 0===l[i]&&(l[i]=M[i])}return u(e,d,p,h,g,a.current,l)},u.createFactory=function(e){var t=u.createElement.bind(null,e);return t.type=e,t},u.cloneAndReplaceKey=function(e,t){var n=u(e.type,t,e.ref,e._self,e._source,e._owner,e.props);return n},u.cloneElement=function(e,t,n){var l,d=i({},e.props),p=e.key,h=e.ref,g=e._self,f=e._source,m=e._owner;if(null!=t){r(t)&&(h=t.ref,m=a.current),o(t)&&(p=\"\"+t.key);var A;e.type&&e.type.defaultProps&&(A=e.type.defaultProps);for(l in t)s.call(t,l)&&!c.hasOwnProperty(l)&&(void 0===t[l]&&void 0!==A?d[l]=A[l]:d[l]=t[l])}var M=arguments.length-2;if(1===M)d.children=n;else if(M>1){for(var w=Array(M),v=0;M>v;v++)w[v]=arguments[v+2];d.children=w}return u(e.type,p,h,g,f,m,d)},u.isValidElement=function(e){return\"object\"==typeof e&&null!==e&&e.$$typeof===l},e.exports=u},function(e,t){\"use strict\";var n={current:null};e.exports=n},function(e,t){\"use strict\";var n=\"function\"==typeof Symbol&&Symbol[\"for\"]&&Symbol[\"for\"](\"react.element\")||60103;e.exports=n},function(e,t,n){\"use strict\";function r(e,t){return e&&\"object\"==typeof e&&null!=e.key?c.escape(e.key):t.toString(36)}function o(e,t,n,i){var p=typeof e;if((\"undefined\"===p||\"boolean\"===p)&&(e=null),null===e||\"string\"===p||\"number\"===p||\"object\"===p&&e.$$typeof===s)return n(i,e,\"\"===t?u+r(e,0):t),1;var h,g,f=0,m=\"\"===t?u:t+d;if(Array.isArray(e))for(var A=0;A<e.length;A++)h=e[A],g=m+r(h,A),f+=o(h,g,n,i);else{var M=l(e);if(M){var w,v=M.call(e);if(M!==e.entries)for(var b=0;!(w=v.next()).done;)h=w.value,g=m+r(h,b++),f+=o(h,g,n,i);else for(;!(w=v.next()).done;){var y=w.value;y&&(h=y[1],g=m+c.escape(y[0])+d+r(h,0),f+=o(h,g,n,i))}}else if(\"object\"===p){var x=\"\",T=String(e);a(\"31\",\"[object Object]\"===T?\"object with keys {\"+Object.keys(e).join(\", \")+\"}\":T,x)}}return f}function i(e,t,n){return null==e?0:o(e,\"\",t,n)}var a=n(28),s=(n(39),n(40)),l=n(42),c=(n(34),n(43)),u=(n(30),\".\"),d=\":\";e.exports=i},function(e,t){\"use strict\";function n(e){var t=e&&(r&&e[r]||e[o]);return\"function\"==typeof t?t:void 0}var r=\"function\"==typeof Symbol&&Symbol.iterator,o=\"@@iterator\";e.exports=n},function(e,t){\"use strict\";function n(e){var t=/[=:]/g,n={\"=\":\"=0\",\":\":\"=2\"},r=(\"\"+e).replace(t,function(e){return n[e]});return\"$\"+r}function r(e){var t=/(=0|=2)/g,n={\"=0\":\"=\",\"=2\":\":\"},r=\".\"===e[0]&&\"$\"===e[1]?e.substring(2):e.substring(1);return(\"\"+r).replace(t,function(e){return n[e]})}var o={escape:n,unescape:r};e.exports=o},function(e,t,n){\"use strict\";var r=n(38),o=r.createFactory,i={a:o(\"a\"),abbr:o(\"abbr\"),address:o(\"address\"),area:o(\"area\"),article:o(\"article\"),aside:o(\"aside\"),audio:o(\"audio\"),b:o(\"b\"),base:o(\"base\"),bdi:o(\"bdi\"),bdo:o(\"bdo\"),big:o(\"big\"),blockquote:o(\"blockquote\"),body:o(\"body\"),br:o(\"br\"),button:o(\"button\"),canvas:o(\"canvas\"),caption:o(\"caption\"),cite:o(\"cite\"),code:o(\"code\"),col:o(\"col\"),colgroup:o(\"colgroup\"),data:o(\"data\"),datalist:o(\"datalist\"),dd:o(\"dd\"),del:o(\"del\"),details:o(\"details\"),dfn:o(\"dfn\"),dialog:o(\"dialog\"),div:o(\"div\"),dl:o(\"dl\"),dt:o(\"dt\"),em:o(\"em\"),embed:o(\"embed\"),fieldset:o(\"fieldset\"),figcaption:o(\"figcaption\"),figure:o(\"figure\"),footer:o(\"footer\"),form:o(\"form\"),h1:o(\"h1\"),h2:o(\"h2\"),h3:o(\"h3\"),h4:o(\"h4\"),h5:o(\"h5\"),h6:o(\"h6\"),head:o(\"head\"),header:o(\"header\"),hgroup:o(\"hgroup\"),hr:o(\"hr\"),html:o(\"html\"),i:o(\"i\"),iframe:o(\"iframe\"),img:o(\"img\"),input:o(\"input\"),ins:o(\"ins\"),kbd:o(\"kbd\"),keygen:o(\"keygen\"),label:o(\"label\"),legend:o(\"legend\"),li:o(\"li\"),link:o(\"link\"),main:o(\"main\"),map:o(\"map\"),mark:o(\"mark\"),menu:o(\"menu\"),menuitem:o(\"menuitem\"),meta:o(\"meta\"),meter:o(\"meter\"),nav:o(\"nav\"),noscript:o(\"noscript\"),object:o(\"object\"),ol:o(\"ol\"),optgroup:o(\"optgroup\"),option:o(\"option\"),output:o(\"output\"),p:o(\"p\"),param:o(\"param\"),picture:o(\"picture\"),pre:o(\"pre\"),progress:o(\"progress\"),q:o(\"q\"),rp:o(\"rp\"),rt:o(\"rt\"),ruby:o(\"ruby\"),s:o(\"s\"),samp:o(\"samp\"),script:o(\"script\"),section:o(\"section\"),select:o(\"select\"),small:o(\"small\"),source:o(\"source\"),span:o(\"span\"),strong:o(\"strong\"),style:o(\"style\"),sub:o(\"sub\"),summary:o(\"summary\"),sup:o(\"sup\"),table:o(\"table\"),tbody:o(\"tbody\"),td:o(\"td\"),textarea:o(\"textarea\"),tfoot:o(\"tfoot\"),th:o(\"th\"),thead:o(\"thead\"),time:o(\"time\"),title:o(\"title\"),tr:o(\"tr\"),track:o(\"track\"),u:o(\"u\"),ul:o(\"ul\"),\"var\":o(\"var\"),video:o(\"video\"),wbr:o(\"wbr\"),circle:o(\"circle\"),clipPath:o(\"clipPath\"),defs:o(\"defs\"),ellipse:o(\"ellipse\"),g:o(\"g\"),image:o(\"image\"),line:o(\"line\"),linearGradient:o(\"linearGradient\"),mask:o(\"mask\"),path:o(\"path\"),pattern:o(\"pattern\"),polygon:o(\"polygon\"),polyline:o(\"polyline\"),radialGradient:o(\"radialGradient\"),rect:o(\"rect\"),stop:o(\"stop\"),svg:o(\"svg\"),text:o(\"text\"),tspan:o(\"tspan\")};e.exports=i},function(e,t,n){\"use strict\";var r=n(38),o=r.isValidElement,i=n(46);e.exports=i(o)},function(e,t,n){\"use strict\";var r=n(47);e.exports=function(e){var t=!1;return r(e,t)}},function(e,t,n){\"use strict\";function r(){return null}var o=n(48),i=n(26),a=n(50),s=n(51),l=n(52),c=function(){};e.exports=function(e,t){function n(e){var t=e&&(k&&e[k]||e[j]);return\"function\"==typeof t?t:void 0}function u(e,t){return e===t?0!==e||1/e===1/t:e!==e&&t!==t}function d(e,t){this.message=e,this.data=t&&\"object\"==typeof t?t:{},this.stack=\"\"}function p(e){function n(n,r,o,i,s,l,c){if(i=i||U,l=l||o,c!==a){if(t){var u=new Error(\"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types\");throw u.name=\"Invariant Violation\",u}}return null==r[o]?n?new d(null===r[o]?\"The \"+s+\" `\"+l+\"` is marked as required \"+(\"in `\"+i+\"`, but its value is `null`.\"):\"The \"+s+\" `\"+l+\"` is marked as required in \"+(\"`\"+i+\"`, but its value is `undefined`.\")):null:e(r,o,i,s,l)}var r=n.bind(null,!1);return r.isRequired=n.bind(null,!0),r}function h(e){function t(t,n,r,o,i,a){var s=t[n],l=E(s);if(l!==e){var c=D(s);return new d(\"Invalid \"+o+\" `\"+i+\"` of type \"+(\"`\"+c+\"` supplied to `\"+r+\"`, expected \")+(\"`\"+e+\"`.\"),{expectedType:e})}return null}return p(t)}function g(){return p(r)}function f(e){function t(t,n,r,o,i){if(\"function\"!=typeof e)return new d(\"Property `\"+i+\"` of component `\"+r+\"` has invalid PropType notation inside arrayOf.\");var s=t[n];if(!Array.isArray(s)){var l=E(s);return new d(\"Invalid \"+o+\" `\"+i+\"` of type \"+(\"`\"+l+\"` supplied to `\"+r+\"`, expected an array.\"))}for(var c=0;c<s.length;c++){var u=e(s,c,r,o,i+\"[\"+c+\"]\",a);if(u instanceof Error)return u}return null}return p(t)}function m(){function t(t,n,r,o,i){var a=t[n];if(!e(a)){var s=E(a);return new d(\"Invalid \"+o+\" `\"+i+\"` of type \"+(\"`\"+s+\"` supplied to `\"+r+\"`, expected a single ReactElement.\"))}return null}return p(t)}function A(){function e(e,t,n,r,i){var a=e[t];if(!o.isValidElementType(a)){var s=E(a);return new d(\"Invalid \"+r+\" `\"+i+\"` of type \"+(\"`\"+s+\"` supplied to `\"+n+\"`, expected a single ReactElement type.\"))}return null}return p(e)}function M(e){function t(t,n,r,o,i){if(!(t[n]instanceof e)){var a=e.name||U,s=L(t[n]);return new d(\"Invalid \"+o+\" `\"+i+\"` of type \"+(\"`\"+s+\"` supplied to `\"+r+\"`, expected \")+(\"instance of `\"+a+\"`.\"))}return null}return p(t)}function w(e){function t(t,n,r,o,i){for(var a=t[n],s=0;s<e.length;s++)if(u(a,e[s]))return null;var l=JSON.stringify(e,function(e,t){var n=D(t);return\"symbol\"===n?String(t):t});return new d(\"Invalid \"+o+\" `\"+i+\"` of value `\"+String(a)+\"` \"+(\"supplied to `\"+r+\"`, expected one of \"+l+\".\"))}return Array.isArray(e)?p(t):r}function v(e){function t(t,n,r,o,i){if(\"function\"!=typeof e)return new d(\"Property `\"+i+\"` of component `\"+r+\"` has invalid PropType notation inside objectOf.\");var l=t[n],c=E(l);if(\"object\"!==c)return new d(\"Invalid \"+o+\" `\"+i+\"` of type \"+(\"`\"+c+\"` supplied to `\"+r+\"`, expected an object.\"));for(var u in l)if(s(l,u)){var p=e(l,u,r,o,i+\".\"+u,a);if(p instanceof Error)return p}return null}return p(t)}function b(e){function t(t,n,r,o,i){for(var l=[],c=0;c<e.length;c++){var u=e[c],p=u(t,n,r,o,i,a);if(null==p)return null;p.data&&s(p.data,\"expectedType\")&&l.push(p.data.expectedType)}var h=l.length>0?\", expected one of type [\"+l.join(\", \")+\"]\":\"\";return new d(\"Invalid \"+o+\" `\"+i+\"` supplied to \"+(\"`\"+r+\"`\"+h+\".\"))}if(!Array.isArray(e))return r;for(var n=0;n<e.length;n++){var o=e[n];if(\"function\"!=typeof o)return c(\"Invalid argument supplied to oneOfType. Expected an array of check functions, but received \"+S(o)+\" at index \"+n+\".\"),r}return p(t)}function y(){function e(e,t,n,r,o){return N(e[t])?null:new d(\"Invalid \"+r+\" `\"+o+\"` supplied to \"+(\"`\"+n+\"`, expected a ReactNode.\"))}return p(e)}function x(e,t,n,r,o){return new d((e||\"React class\")+\": \"+t+\" type `\"+n+\".\"+r+\"` is invalid; it must be a function, usually from the `prop-types` package, but received `\"+o+\"`.\")}function T(e){function t(t,n,r,o,i){var s=t[n],l=E(s);if(\"object\"!==l)return new d(\"Invalid \"+o+\" `\"+i+\"` of type `\"+l+\"` \"+(\"supplied to `\"+r+\"`, expected `object`.\"));for(var c in e){var u=e[c];if(\"function\"!=typeof u)return x(r,o,i,c,D(u));var p=u(s,c,r,o,i+\".\"+c,a);if(p)return p}return null}return p(t)}function C(e){function t(t,n,r,o,l){var c=t[n],u=E(c);if(\"object\"!==u)return new d(\"Invalid \"+o+\" `\"+l+\"` of type `\"+u+\"` \"+(\"supplied to `\"+r+\"`, expected `object`.\"));var p=i({},t[n],e);for(var h in p){var g=e[h];if(s(e,h)&&\"function\"!=typeof g)return x(r,o,l,h,D(g));if(!g)return new d(\"Invalid \"+o+\" `\"+l+\"` key `\"+h+\"` supplied to `\"+r+\"`.\\nBad object: \"+JSON.stringify(t[n],null,\"  \")+\"\\nValid keys: \"+JSON.stringify(Object.keys(e),null,\"  \"));var f=g(c,h,r,o,l+\".\"+h,a);if(f)return f}return null}return p(t)}function N(t){switch(typeof t){case\"number\":case\"string\":case\"undefined\":return!0;case\"boolean\":return!t;case\"object\":if(Array.isArray(t))return t.every(N);if(null===t||e(t))return!0;var r=n(t);if(!r)return!1;var o,i=r.call(t);if(r!==t.entries){for(;!(o=i.next()).done;)if(!N(o.value))return!1}else for(;!(o=i.next()).done;){var a=o.value;if(a&&!N(a[1]))return!1}return!0;default:return!1}}function I(e,t){return\"symbol\"===e?!0:t?\"Symbol\"===t[\"@@toStringTag\"]?!0:\"function\"==typeof Symbol&&t instanceof Symbol?!0:!1:!1}function E(e){var t=typeof e;return Array.isArray(e)?\"array\":e instanceof RegExp?\"object\":I(t,e)?\"symbol\":t}function D(e){if(\"undefined\"==typeof e||null===e)return\"\"+e;var t=E(e);if(\"object\"===t){if(e instanceof Date)return\"date\";if(e instanceof RegExp)return\"regexp\"}return t}function S(e){var t=D(e);switch(t){case\"array\":case\"object\":return\"an \"+t;case\"boolean\":case\"date\":case\"regexp\":return\"a \"+t;default:return t}}function L(e){return e.constructor&&e.constructor.name?e.constructor.name:U}var k=\"function\"==typeof Symbol&&Symbol.iterator,j=\"@@iterator\",U=\"<<anonymous>>\",B={array:h(\"array\"),bigint:h(\"bigint\"),bool:h(\"boolean\"),func:h(\"function\"),number:h(\"number\"),object:h(\"object\"),string:h(\"string\"),symbol:h(\"symbol\"),any:g(),arrayOf:f,element:m(),elementType:A(),instanceOf:M,node:y(),objectOf:v,oneOf:w,oneOfType:b,shape:T,exact:C};return d.prototype=Error.prototype,B.checkPropTypes=l,B.resetWarningCache=l.resetWarningCache,B.PropTypes=B,B}},function(e,t,n){\"use strict\";e.exports=n(49)},function(e,t){/** @license React v16.13.1\n\t * react-is.production.min.js\n\t *\n\t * Copyright (c) Facebook, Inc. and its affiliates.\n\t *\n\t * This source code is licensed under the MIT license found in the\n\t * LICENSE file in the root directory of this source tree.\n\t */\n\"use strict\";function n(e){if(\"object\"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case i:switch(e=e.type){case p:case h:case s:case c:case l:case f:return e;default:switch(e=e&&e.$$typeof){case d:case g:case M:case A:case u:return e;default:return t}}case a:return t}}}function r(e){return n(e)===h}var o=\"function\"==typeof Symbol&&Symbol[\"for\"],i=o?Symbol[\"for\"](\"react.element\"):60103,a=o?Symbol[\"for\"](\"react.portal\"):60106,s=o?Symbol[\"for\"](\"react.fragment\"):60107,l=o?Symbol[\"for\"](\"react.strict_mode\"):60108,c=o?Symbol[\"for\"](\"react.profiler\"):60114,u=o?Symbol[\"for\"](\"react.provider\"):60109,d=o?Symbol[\"for\"](\"react.context\"):60110,p=o?Symbol[\"for\"](\"react.async_mode\"):60111,h=o?Symbol[\"for\"](\"react.concurrent_mode\"):60111,g=o?Symbol[\"for\"](\"react.forward_ref\"):60112,f=o?Symbol[\"for\"](\"react.suspense\"):60113,m=o?Symbol[\"for\"](\"react.suspense_list\"):60120,A=o?Symbol[\"for\"](\"react.memo\"):60115,M=o?Symbol[\"for\"](\"react.lazy\"):60116,w=o?Symbol[\"for\"](\"react.block\"):60121,v=o?Symbol[\"for\"](\"react.fundamental\"):60117,b=o?Symbol[\"for\"](\"react.responder\"):60118,y=o?Symbol[\"for\"](\"react.scope\"):60119;t.AsyncMode=p,t.ConcurrentMode=h,t.ContextConsumer=d,t.ContextProvider=u,t.Element=i,t.ForwardRef=g,t.Fragment=s,t.Lazy=M,t.Memo=A,t.Portal=a,t.Profiler=c,t.StrictMode=l,t.Suspense=f,t.isAsyncMode=function(e){return r(e)||n(e)===p},t.isConcurrentMode=r,t.isContextConsumer=function(e){return n(e)===d},t.isContextProvider=function(e){return n(e)===u},t.isElement=function(e){return\"object\"==typeof e&&null!==e&&e.$$typeof===i},t.isForwardRef=function(e){return n(e)===g},t.isFragment=function(e){return n(e)===s},t.isLazy=function(e){return n(e)===M},t.isMemo=function(e){return n(e)===A},t.isPortal=function(e){return n(e)===a},t.isProfiler=function(e){return n(e)===c},t.isStrictMode=function(e){return n(e)===l},t.isSuspense=function(e){return n(e)===f},t.isValidElementType=function(e){return\"string\"==typeof e||\"function\"==typeof e||e===s||e===h||e===c||e===l||e===f||e===m||\"object\"==typeof e&&null!==e&&(e.$$typeof===M||e.$$typeof===A||e.$$typeof===u||e.$$typeof===d||e.$$typeof===g||e.$$typeof===v||e.$$typeof===b||e.$$typeof===y||e.$$typeof===w)},t.typeOf=n},function(e,t){\"use strict\";var n=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\";e.exports=n},function(e,t){e.exports=Function.call.bind(Object.prototype.hasOwnProperty)},function(e,t,n){\"use strict\";function r(e,t,n,r,o){}r.resetWarningCache=function(){},e.exports=r},function(e,t){\"use strict\";e.exports=\"15.7.0\"},function(e,t,n){\"use strict\";var r=n(27),o=r.Component,i=n(38),a=i.isValidElement,s=n(29),l=n(55);e.exports=l(o,a,s)},function(e,t,n){\"use strict\";function r(e,t,n,r,o,i,a,s){if(c(t),!e){var l;if(void 0===t)l=new Error(\"Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.\");else{var u=[n,r,o,i,a,s],d=0;l=new Error(t.replace(/%s/g,function(){return u[d++]})),l.name=\"Invariant Violation\"}throw l.framesToPop=1,l}}function o(e){return e}function i(e,t,n){function i(e,t){var n=M.hasOwnProperty(t)?M[t]:null;x.hasOwnProperty(t)&&r(\"OVERRIDE_BASE\"===n,\"ReactClassInterface: You are attempting to override `%s` from your class specification. Ensure that your method names do not overlap with React methods.\",t),e&&r(\"DEFINE_MANY\"===n||\"DEFINE_MANY_MERGED\"===n,\"ReactClassInterface: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.\",t)}function a(e,n){if(n){r(\"function\"!=typeof n,\"ReactClass: You're attempting to use a component class or function as a mixin. Instead, just use a regular object.\"),r(!t(n),\"ReactClass: You're attempting to use a component as a mixin. Instead, just use a regular object.\");var o=e.prototype,a=o.__reactAutoBindPairs;n.hasOwnProperty(u)&&v.mixins(e,n.mixins);for(var s in n)if(n.hasOwnProperty(s)&&s!==u){var l=n[s],c=o.hasOwnProperty(s);if(i(c,s),v.hasOwnProperty(s))v[s](e,l);else{var d=M.hasOwnProperty(s),g=\"function\"==typeof l,f=g&&!d&&!c&&n.autobind!==!1;if(f)a.push(s,l),o[s]=l;else if(c){var m=M[s];r(d&&(\"DEFINE_MANY_MERGED\"===m||\"DEFINE_MANY\"===m),\"ReactClass: Unexpected spec policy %s for key %s when mixing in component specs.\",m,s),\"DEFINE_MANY_MERGED\"===m?o[s]=p(o[s],l):\"DEFINE_MANY\"===m&&(o[s]=h(o[s],l))}else o[s]=l}}}else;}function c(e,t){if(t)for(var n in t){var o=t[n];if(t.hasOwnProperty(n)){var i=n in v;r(!i,'ReactClass: You are attempting to define a reserved property, `%s`, that shouldn\\'t be on the \"statics\" key. Define it as an instance property instead; it will still be accessible on the constructor.',n);var a=n in e;if(a){var s=w.hasOwnProperty(n)?w[n]:null;return r(\"DEFINE_MANY_MERGED\"===s,\"ReactClass: You are attempting to define `%s` on your component more than once. This conflict may be due to a mixin.\",n),void(e[n]=p(e[n],o))}e[n]=o}}}function d(e,t){r(e&&t&&\"object\"==typeof e&&\"object\"==typeof t,\"mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.\");for(var n in t)t.hasOwnProperty(n)&&(r(void 0===e[n],\"mergeIntoWithNoDuplicateKeys(): Tried to merge two objects with the same key: `%s`. This conflict may be due to a mixin; in particular, this may be caused by two getInitialState() or getDefaultProps() methods returning objects with clashing keys.\",n),e[n]=t[n]);return e}function p(e,t){return function(){var n=e.apply(this,arguments),r=t.apply(this,arguments);if(null==n)return r;if(null==r)return n;var o={};return d(o,n),d(o,r),o}}function h(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}function g(e,t){var n=t.bind(e);return n}function f(e){for(var t=e.__reactAutoBindPairs,n=0;n<t.length;n+=2){var r=t[n],o=t[n+1];e[r]=g(e,o)}}function m(e){var t=o(function(e,o,i){this.__reactAutoBindPairs.length&&f(this),this.props=e,this.context=o,this.refs=l,this.updater=i||n,this.state=null;var a=this.getInitialState?this.getInitialState():null;r(\"object\"==typeof a&&!Array.isArray(a),\"%s.getInitialState(): must return an object or null\",t.displayName||\"ReactCompositeComponent\"),this.state=a});t.prototype=new T,t.prototype.constructor=t,t.prototype.__reactAutoBindPairs=[],A.forEach(a.bind(null,t)),a(t,b),a(t,e),a(t,y),t.getDefaultProps&&(t.defaultProps=t.getDefaultProps()),r(t.prototype.render,\"createClass(...): Class specification must implement a `render` method.\");for(var i in M)t.prototype[i]||(t.prototype[i]=null);return t}var A=[],M={mixins:\"DEFINE_MANY\",statics:\"DEFINE_MANY\",propTypes:\"DEFINE_MANY\",contextTypes:\"DEFINE_MANY\",childContextTypes:\"DEFINE_MANY\",getDefaultProps:\"DEFINE_MANY_MERGED\",getInitialState:\"DEFINE_MANY_MERGED\",getChildContext:\"DEFINE_MANY_MERGED\",render:\"DEFINE_ONCE\",componentWillMount:\"DEFINE_MANY\",componentDidMount:\"DEFINE_MANY\",componentWillReceiveProps:\"DEFINE_MANY\",shouldComponentUpdate:\"DEFINE_ONCE\",componentWillUpdate:\"DEFINE_MANY\",componentDidUpdate:\"DEFINE_MANY\",componentWillUnmount:\"DEFINE_MANY\",UNSAFE_componentWillMount:\"DEFINE_MANY\",UNSAFE_componentWillReceiveProps:\"DEFINE_MANY\",UNSAFE_componentWillUpdate:\"DEFINE_MANY\",updateComponent:\"OVERRIDE_BASE\"},w={getDerivedStateFromProps:\"DEFINE_MANY_MERGED\"},v={displayName:function(e,t){e.displayName=t},mixins:function(e,t){if(t)for(var n=0;n<t.length;n++)a(e,t[n])},childContextTypes:function(e,t){e.childContextTypes=s({},e.childContextTypes,t)},contextTypes:function(e,t){e.contextTypes=s({},e.contextTypes,t)},getDefaultProps:function(e,t){e.getDefaultProps?e.getDefaultProps=p(e.getDefaultProps,t):e.getDefaultProps=t},propTypes:function(e,t){e.propTypes=s({},e.propTypes,t)},statics:function(e,t){c(e,t)},autobind:function(){}},b={componentDidMount:function(){this.__isMounted=!0}},y={componentWillUnmount:function(){this.__isMounted=!1}},x={replaceState:function(e,t){this.updater.enqueueReplaceState(this,e,t)},isMounted:function(){return!!this.__isMounted}},T=function(){};return s(T.prototype,e.prototype,x),m}var a,s=n(26),l={},c=function(e){},u=\"mixins\";a={},e.exports=i},function(e,t,n){\"use strict\";function r(e){return i.isValidElement(e)?void 0:o(\"143\"),e}var o=n(28),i=n(38);n(34);e.exports=r},function(e,t,n){\"use strict\";e.exports=n(58)},function(e,t,n){\"use strict\";var r=n(59),o=n(63),i=n(187),a=n(84),s=n(81),l=n(192),c=n(193),u=n(194),d=n(195);n(30);o.inject();var p={findDOMNode:c,render:i.render,unmountComponentAtNode:i.unmountComponentAtNode,version:l,unstable_batchedUpdates:s.batchedUpdates,unstable_renderSubtreeIntoContainer:d};\"undefined\"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&\"function\"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject&&__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ComponentTree:{getClosestInstanceFromNode:r.getClosestInstanceFromNode,getNodeFromInstance:function(e){return e._renderedComponent&&(e=u(e)),e?r.getNodeFromInstance(e):null}},Mount:i,Reconciler:a});e.exports=p},function(e,t,n){\"use strict\";function r(e,t){return 1===e.nodeType&&e.getAttribute(g)===String(t)||8===e.nodeType&&e.nodeValue===\" react-text: \"+t+\" \"||8===e.nodeType&&e.nodeValue===\" react-empty: \"+t+\" \"}function o(e){for(var t;t=e._renderedComponent;)e=t;return e}function i(e,t){var n=o(e);n._hostNode=t,t[m]=n}function a(e){var t=e._hostNode;t&&(delete t[m],e._hostNode=null)}function s(e,t){if(!(e._flags&f.hasCachedChildNodes)){var n=e._renderedChildren,a=t.firstChild;e:for(var s in n)if(n.hasOwnProperty(s)){var l=n[s],c=o(l)._domID;if(0!==c){for(;null!==a;a=a.nextSibling)if(r(a,c)){i(l,a);continue e}d(\"32\",c)}}e._flags|=f.hasCachedChildNodes}}function l(e){if(e[m])return e[m];for(var t=[];!e[m];){if(t.push(e),!e.parentNode)return null;e=e.parentNode}for(var n,r;e&&(r=e[m]);e=t.pop())n=r,t.length&&s(r,e);return n}function c(e){var t=l(e);return null!=t&&t._hostNode===e?t:null}function u(e){if(void 0===e._hostNode?d(\"33\"):void 0,e._hostNode)return e._hostNode;for(var t=[];!e._hostNode;)t.push(e),e._hostParent?void 0:d(\"34\"),e=e._hostParent;for(;t.length;e=t.pop())s(e,e._hostNode);return e._hostNode}var d=n(60),p=n(61),h=n(62),g=(n(34),p.ID_ATTRIBUTE_NAME),f=h,m=\"__reactInternalInstance$\"+Math.random().toString(36).slice(2),A={getClosestInstanceFromNode:l,getInstanceFromNode:c,getNodeFromInstance:u,precacheChildNodes:s,precacheNode:i,uncacheNode:a};e.exports=A},function(e,t){\"use strict\";function n(e){for(var t=arguments.length-1,n=\"Minified React error #\"+e+\"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=\"+e,r=0;t>r;r++)n+=\"&args[]=\"+encodeURIComponent(arguments[r+1]);n+=\" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.\";var o=new Error(n);throw o.name=\"Invariant Violation\",o.framesToPop=1,o}e.exports=n},function(e,t,n){\"use strict\";function r(e,t){return(e&t)===t}var o=n(60),i=(n(34),{MUST_USE_PROPERTY:1,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,injectDOMPropertyConfig:function(e){var t=i,n=e.Properties||{},a=e.DOMAttributeNamespaces||{},l=e.DOMAttributeNames||{},c=e.DOMPropertyNames||{},u=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var d in n){s.properties.hasOwnProperty(d)?o(\"48\",d):void 0;var p=d.toLowerCase(),h=n[d],g={attributeName:p,attributeNamespace:null,propertyName:d,mutationMethod:null,mustUseProperty:r(h,t.MUST_USE_PROPERTY),hasBooleanValue:r(h,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(h,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(h,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(h,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(g.hasBooleanValue+g.hasNumericValue+g.hasOverloadedBooleanValue<=1?void 0:o(\"50\",d),l.hasOwnProperty(d)){var f=l[d];g.attributeName=f}a.hasOwnProperty(d)&&(g.attributeNamespace=a[d]),c.hasOwnProperty(d)&&(g.propertyName=c[d]),u.hasOwnProperty(d)&&(g.mutationMethod=u[d]),s.properties[d]=g}}}),a=\":A-Z_a-z\\\\u00C0-\\\\u00D6\\\\u00D8-\\\\u00F6\\\\u00F8-\\\\u02FF\\\\u0370-\\\\u037D\\\\u037F-\\\\u1FFF\\\\u200C-\\\\u200D\\\\u2070-\\\\u218F\\\\u2C00-\\\\u2FEF\\\\u3001-\\\\uD7FF\\\\uF900-\\\\uFDCF\\\\uFDF0-\\\\uFFFD\",s={ID_ATTRIBUTE_NAME:\"data-reactid\",ROOT_ATTRIBUTE_NAME:\"data-reactroot\",ATTRIBUTE_NAME_START_CHAR:a,ATTRIBUTE_NAME_CHAR:a+\"\\\\-.0-9\\\\u00B7\\\\u0300-\\\\u036F\\\\u203F-\\\\u2040\",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t<s._isCustomAttributeFunctions.length;t++){var n=s._isCustomAttributeFunctions[t];if(n(e))return!0}return!1},injection:i};e.exports=s},function(e,t){\"use strict\";var n={hasCachedChildNodes:1};e.exports=n},function(e,t,n){\"use strict\";function r(){x||(x=!0,M.EventEmitter.injectReactEventListener(A),M.EventPluginHub.injectEventPluginOrder(s),M.EventPluginUtils.injectComponentTree(p),M.EventPluginUtils.injectTreeTraversal(g),M.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:y,EnterLeaveEventPlugin:l,ChangeEventPlugin:a,SelectEventPlugin:b,BeforeInputEventPlugin:i}),M.HostComponent.injectGenericComponentClass(d),M.HostComponent.injectTextComponentClass(f),M.DOMProperty.injectDOMPropertyConfig(o),M.DOMProperty.injectDOMPropertyConfig(c),M.DOMProperty.injectDOMPropertyConfig(v),M.EmptyComponent.injectEmptyComponentFactory(function(e){return new h(e)}),M.Updates.injectReconcileTransaction(w),M.Updates.injectBatchingStrategy(m),M.Component.injectEnvironment(u))}var o=n(64),i=n(65),a=n(80),s=n(93),l=n(94),c=n(99),u=n(100),d=n(113),p=n(59),h=n(158),g=n(159),f=n(160),m=n(161),A=n(162),M=n(165),w=n(166),v=n(174),b=n(175),y=n(176),x=!1;e.exports={inject:r}},function(e,t){\"use strict\";var n={Properties:{\"aria-current\":0,\"aria-details\":0,\"aria-disabled\":0,\"aria-hidden\":0,\"aria-invalid\":0,\"aria-keyshortcuts\":0,\"aria-label\":0,\"aria-roledescription\":0,\"aria-autocomplete\":0,\"aria-checked\":0,\"aria-expanded\":0,\"aria-haspopup\":0,\"aria-level\":0,\"aria-modal\":0,\"aria-multiline\":0,\"aria-multiselectable\":0,\"aria-orientation\":0,\"aria-placeholder\":0,\"aria-pressed\":0,\"aria-readonly\":0,\"aria-required\":0,\"aria-selected\":0,\"aria-sort\":0,\"aria-valuemax\":0,\"aria-valuemin\":0,\"aria-valuenow\":0,\"aria-valuetext\":0,\"aria-atomic\":0,\"aria-busy\":0,\"aria-live\":0,\"aria-relevant\":0,\"aria-dropeffect\":0,\"aria-grabbed\":0,\"aria-activedescendant\":0,\"aria-colcount\":0,\"aria-colindex\":0,\"aria-colspan\":0,\"aria-controls\":0,\"aria-describedby\":0,\"aria-errormessage\":0,\"aria-flowto\":0,\"aria-labelledby\":0,\"aria-owns\":0,\"aria-posinset\":0,\"aria-rowcount\":0,\"aria-rowindex\":0,\"aria-rowspan\":0,\"aria-setsize\":0},DOMAttributeNames:{},DOMPropertyNames:{}};e.exports=n},function(e,t,n){\"use strict\";function r(){var e=window.opera;return\"object\"==typeof e&&\"function\"==typeof e.version&&parseInt(e.version(),10)<=12}function o(e){return(e.ctrlKey||e.altKey||e.metaKey)&&!(e.ctrlKey&&e.altKey)}function i(e){switch(e){case\"topCompositionStart\":return N.compositionStart;case\"topCompositionEnd\":return N.compositionEnd;case\"topCompositionUpdate\":return N.compositionUpdate}}function a(e,t){return\"topKeyDown\"===e&&t.keyCode===w}function s(e,t){switch(e){case\"topKeyUp\":return-1!==M.indexOf(t.keyCode);case\"topKeyDown\":return t.keyCode!==w;case\"topKeyPress\":case\"topMouseDown\":case\"topBlur\":return!0;default:return!1}}function l(e){var t=e.detail;return\"object\"==typeof t&&\"data\"in t?t.data:null}function c(e,t,n,r){var o,c;if(v?o=i(e):E?s(e,n)&&(o=N.compositionEnd):a(e,n)&&(o=N.compositionStart),!o)return null;x&&(E||o!==N.compositionStart?o===N.compositionEnd&&E&&(c=E.getData()):E=f.getPooled(r));var u=m.getPooled(o,t,n,r);if(c)u.data=c;else{var d=l(n);null!==d&&(u.data=d)}return h.accumulateTwoPhaseDispatches(u),u}function u(e,t){switch(e){case\"topCompositionEnd\":return l(t);case\"topKeyPress\":var n=t.which;return n!==T?null:(I=!0,C);case\"topTextInput\":var r=t.data;return r===C&&I?null:r;default:return null}}function d(e,t){if(E){if(\"topCompositionEnd\"===e||!v&&s(e,t)){var n=E.getData();return f.release(E),E=null,n}return null}switch(e){case\"topPaste\":return null;case\"topKeyPress\":return t.which&&!o(t)?String.fromCharCode(t.which):null;case\"topCompositionEnd\":return x?null:t.data;default:return null}}function p(e,t,n,r){var o;if(o=y?u(e,n):d(e,n),!o)return null;var i=A.getPooled(N.beforeInput,t,n,r);return i.data=o,h.accumulateTwoPhaseDispatches(i),i}var h=n(66),g=n(73),f=n(74),m=n(77),A=n(79),M=[9,13,27,32],w=229,v=g.canUseDOM&&\"CompositionEvent\"in window,b=null;g.canUseDOM&&\"documentMode\"in document&&(b=document.documentMode);var y=g.canUseDOM&&\"TextEvent\"in window&&!b&&!r(),x=g.canUseDOM&&(!v||b&&b>8&&11>=b),T=32,C=String.fromCharCode(T),N={beforeInput:{phasedRegistrationNames:{bubbled:\"onBeforeInput\",captured:\"onBeforeInputCapture\"},dependencies:[\"topCompositionEnd\",\"topKeyPress\",\"topTextInput\",\"topPaste\"]},compositionEnd:{phasedRegistrationNames:{bubbled:\"onCompositionEnd\",captured:\"onCompositionEndCapture\"},dependencies:[\"topBlur\",\"topCompositionEnd\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionStart:{phasedRegistrationNames:{bubbled:\"onCompositionStart\",captured:\"onCompositionStartCapture\"},dependencies:[\"topBlur\",\"topCompositionStart\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]},compositionUpdate:{phasedRegistrationNames:{bubbled:\"onCompositionUpdate\",captured:\"onCompositionUpdateCapture\"},dependencies:[\"topBlur\",\"topCompositionUpdate\",\"topKeyDown\",\"topKeyPress\",\"topKeyUp\",\"topMouseDown\"]}},I=!1,E=null,D={eventTypes:N,extractEvents:function(e,t,n,r){return[c(e,t,n,r),p(e,t,n,r)]}};e.exports=D},function(e,t,n){\"use strict\";function r(e,t,n){var r=t.dispatchConfig.phasedRegistrationNames[n];return A(e,r)}function o(e,t,n){var o=r(e,n,t);o&&(n._dispatchListeners=f(n._dispatchListeners,o),n._dispatchInstances=f(n._dispatchInstances,e))}function i(e){e&&e.dispatchConfig.phasedRegistrationNames&&g.traverseTwoPhase(e._targetInst,o,e)}function a(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var t=e._targetInst,n=t?g.getParentInstance(t):null;g.traverseTwoPhase(n,o,e)}}function s(e,t,n){if(n&&n.dispatchConfig.registrationName){var r=n.dispatchConfig.registrationName,o=A(e,r);o&&(n._dispatchListeners=f(n._dispatchListeners,o),n._dispatchInstances=f(n._dispatchInstances,e))}}function l(e){e&&e.dispatchConfig.registrationName&&s(e._targetInst,null,e)}function c(e){m(e,i)}function u(e){m(e,a)}function d(e,t,n,r){g.traverseEnterLeave(n,r,s,e,t)}function p(e){m(e,l)}var h=n(67),g=n(69),f=n(71),m=n(72),A=(n(30),h.getListener),M={accumulateTwoPhaseDispatches:c,accumulateTwoPhaseDispatchesSkipTarget:u,accumulateDirectDispatches:p,accumulateEnterLeaveDispatches:d};e.exports=M},function(e,t,n){\"use strict\";function r(e){return\"button\"===e||\"input\"===e||\"select\"===e||\"textarea\"===e}function o(e,t,n){switch(e){case\"onClick\":case\"onClickCapture\":case\"onDoubleClick\":case\"onDoubleClickCapture\":case\"onMouseDown\":case\"onMouseDownCapture\":case\"onMouseMove\":case\"onMouseMoveCapture\":case\"onMouseUp\":case\"onMouseUpCapture\":return!(!n.disabled||!r(t));default:return!1}}var i=n(60),a=n(68),s=n(69),l=n(70),c=n(71),u=n(72),d=(n(34),{}),p=null,h=function(e,t){e&&(s.executeDispatchesInOrder(e,t),e.isPersistent()||e.constructor.release(e))},g=function(e){return h(e,!0)},f=function(e){return h(e,!1)},m=function(e){return\".\"+e._rootNodeID},A={injection:{injectEventPluginOrder:a.injectEventPluginOrder,injectEventPluginsByName:a.injectEventPluginsByName},putListener:function(e,t,n){\"function\"!=typeof n?i(\"94\",t,typeof n):void 0;var r=m(e),o=d[t]||(d[t]={});o[r]=n;var s=a.registrationNameModules[t];s&&s.didPutListener&&s.didPutListener(e,t,n)},getListener:function(e,t){var n=d[t];if(o(t,e._currentElement.type,e._currentElement.props))return null;var r=m(e);return n&&n[r]},deleteListener:function(e,t){var n=a.registrationNameModules[t];n&&n.willDeleteListener&&n.willDeleteListener(e,t);var r=d[t];if(r){var o=m(e);delete r[o]}},deleteAllListeners:function(e){var t=m(e);for(var n in d)if(d.hasOwnProperty(n)&&d[n][t]){var r=a.registrationNameModules[n];r&&r.willDeleteListener&&r.willDeleteListener(e,n),delete d[n][t]}},extractEvents:function(e,t,n,r){for(var o,i=a.plugins,s=0;s<i.length;s++){var l=i[s];if(l){var u=l.extractEvents(e,t,n,r);u&&(o=c(o,u))}}return o},enqueueEvents:function(e){e&&(p=c(p,e))},processEventQueue:function(e){var t=p;p=null,e?u(t,g):u(t,f),p?i(\"95\"):void 0,l.rethrowCaughtError()},__purge:function(){d={}},__getListenerBank:function(){return d}};e.exports=A},function(e,t,n){\"use strict\";function r(){if(s)for(var e in l){var t=l[e],n=s.indexOf(e);if(n>-1?void 0:a(\"96\",e),!c.plugins[n]){t.extractEvents?void 0:a(\"97\",e),c.plugins[n]=t;var r=t.eventTypes;for(var i in r)o(r[i],t,i)?void 0:a(\"98\",i,e)}}}function o(e,t,n){c.eventNameDispatchConfigs.hasOwnProperty(n)?a(\"99\",n):void 0,c.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var o in r)if(r.hasOwnProperty(o)){var s=r[o];i(s,t,n)}return!0}return e.registrationName?(i(e.registrationName,t,n),!0):!1}function i(e,t,n){c.registrationNameModules[e]?a(\"100\",e):void 0,c.registrationNameModules[e]=t,c.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=n(60),s=(n(34),null),l={},c={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){s?a(\"101\"):void 0,s=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];l.hasOwnProperty(n)&&l[n]===o||(l[n]?a(\"102\",n):void 0,l[n]=o,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return c.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var o=c.registrationNameModules[n[r]];if(o)return o}}return null},_resetEventPlugins:function(){s=null;for(var e in l)l.hasOwnProperty(e)&&delete l[e];c.plugins.length=0;var t=c.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=c.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};e.exports=c},function(e,t,n){\"use strict\";function r(e){return\"topMouseUp\"===e||\"topTouchEnd\"===e||\"topTouchCancel\"===e}function o(e){return\"topMouseMove\"===e||\"topTouchMove\"===e}function i(e){return\"topMouseDown\"===e||\"topTouchStart\"===e}function a(e,t,n,r){var o=e.type||\"unknown-event\";e.currentTarget=A.getNodeFromInstance(r),t?f.invokeGuardedCallbackWithCatch(o,n,e):f.invokeGuardedCallback(o,n,e),e.currentTarget=null}function s(e,t){var n=e._dispatchListeners,r=e._dispatchInstances;if(Array.isArray(n))for(var o=0;o<n.length&&!e.isPropagationStopped();o++)a(e,t,n[o],r[o]);else n&&a(e,t,n,r);e._dispatchListeners=null,e._dispatchInstances=null}function l(e){var t=e._dispatchListeners,n=e._dispatchInstances;if(Array.isArray(t)){for(var r=0;r<t.length&&!e.isPropagationStopped();r++)if(t[r](e,n[r]))return n[r]}else if(t&&t(e,n))return n;return null}function c(e){var t=l(e);return e._dispatchInstances=null,e._dispatchListeners=null,t}function u(e){var t=e._dispatchListeners,n=e._dispatchInstances;Array.isArray(t)?g(\"103\"):void 0,e.currentTarget=t?A.getNodeFromInstance(n):null;var r=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,r}function d(e){return!!e._dispatchListeners}var p,h,g=n(60),f=n(70),m=(n(34),n(30),{injectComponentTree:function(e){p=e},injectTreeTraversal:function(e){h=e}}),A={isEndish:r,isMoveish:o,isStartish:i,executeDirectDispatch:u,executeDispatchesInOrder:s,executeDispatchesInOrderStopAtTrue:c,hasDispatches:d,getInstanceFromNode:function(e){return p.getInstanceFromNode(e)},getNodeFromInstance:function(e){return p.getNodeFromInstance(e)},isAncestor:function(e,t){return h.isAncestor(e,t)},getLowestCommonAncestor:function(e,t){return h.getLowestCommonAncestor(e,t)},getParentInstance:function(e){return h.getParentInstance(e)},traverseTwoPhase:function(e,t,n){return h.traverseTwoPhase(e,t,n)},traverseEnterLeave:function(e,t,n,r,o){return h.traverseEnterLeave(e,t,n,r,o)},injection:m};e.exports=A},function(e,t,n){\"use strict\";function r(e,t,n){try{t(n)}catch(r){null===o&&(o=r)}}var o=null,i={invokeGuardedCallback:r,invokeGuardedCallbackWithCatch:r,rethrowCaughtError:function(){if(o){var e=o;throw o=null,e}}};e.exports=i},function(e,t,n){\"use strict\";function r(e,t){return null==t?o(\"30\"):void 0,null==e?t:Array.isArray(e)?Array.isArray(t)?(e.push.apply(e,t),e):(e.push(t),e):Array.isArray(t)?[e].concat(t):[e,t]}var o=n(60);n(34);e.exports=r},function(e,t){\"use strict\";function n(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)}e.exports=n},function(e,t){\"use strict\";var n=!(\"undefined\"==typeof window||!window.document||!window.document.createElement),r={canUseDOM:n,canUseWorkers:\"undefined\"!=typeof Worker,canUseEventListeners:n&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:n&&!!window.screen,isInWorker:!n};e.exports=r},function(e,t,n){\"use strict\";function r(e){this._root=e,this._startText=this.getText(),this._fallbackText=null}var o=n(26),i=n(75),a=n(76);o(r.prototype,{destructor:function(){this._root=null,this._startText=null,this._fallbackText=null},getText:function(){return\"value\"in this._root?this._root.value:this._root[a()]},getData:function(){if(this._fallbackText)return this._fallbackText;var e,t,n=this._startText,r=n.length,o=this.getText(),i=o.length;for(e=0;r>e&&n[e]===o[e];e++);var a=r-e;for(t=1;a>=t&&n[r-t]===o[i-t];t++);var s=t>1?1-t:void 0;return this._fallbackText=o.slice(e,s),this._fallbackText}}),i.addPoolingTo(r),e.exports=r},function(e,t,n){\"use strict\";var r=n(60),o=(n(34),function(e){var t=this;if(t.instancePool.length){var n=t.instancePool.pop();return t.call(n,e),n}return new t(e)}),i=function(e,t){var n=this;if(n.instancePool.length){var r=n.instancePool.pop();return n.call(r,e,t),r}return new n(e,t)},a=function(e,t,n){var r=this;if(r.instancePool.length){var o=r.instancePool.pop();return r.call(o,e,t,n),o}return new r(e,t,n)},s=function(e,t,n,r){var o=this;if(o.instancePool.length){var i=o.instancePool.pop();return o.call(i,e,t,n,r),i}return new o(e,t,n,r)},l=function(e){var t=this;e instanceof t?void 0:r(\"25\"),e.destructor(),t.instancePool.length<t.poolSize&&t.instancePool.push(e)},c=10,u=o,d=function(e,t){var n=e;return n.instancePool=[],n.getPooled=t||u,n.poolSize||(n.poolSize=c),n.release=l,n},p={addPoolingTo:d,oneArgumentPooler:o,twoArgumentPooler:i,threeArgumentPooler:a,fourArgumentPooler:s};e.exports=p},function(e,t,n){\"use strict\";function r(){return!i&&o.canUseDOM&&(i=\"textContent\"in document.documentElement?\"textContent\":\"innerText\"),i}var o=n(73),i=null;e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i={data:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){this.dispatchConfig=e,this._targetInst=t,this.nativeEvent=n;var o=this.constructor.Interface;for(var i in o)if(o.hasOwnProperty(i)){var s=o[i];s?this[i]=s(n):\"target\"===i?this.target=r:this[i]=n[i]}var l=null!=n.defaultPrevented?n.defaultPrevented:n.returnValue===!1;return l?this.isDefaultPrevented=a.thatReturnsTrue:this.isDefaultPrevented=a.thatReturnsFalse,this.isPropagationStopped=a.thatReturnsFalse,this}var o=n(26),i=n(75),a=n(31),s=(n(30),\"function\"==typeof Proxy,[\"dispatchConfig\",\"_targetInst\",\"nativeEvent\",\"isDefaultPrevented\",\"isPropagationStopped\",\"_dispatchListeners\",\"_dispatchInstances\"]),l={type:null,target:null,currentTarget:a.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};o(r.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():\"unknown\"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=a.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():\"unknown\"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=a.thatReturnsTrue)},persist:function(){this.isPersistent=a.thatReturnsTrue},isPersistent:a.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var t in e)this[t]=null;for(var n=0;n<s.length;n++)this[s[n]]=null}}),r.Interface=l,r.augmentClass=function(e,t){var n=this,r=function(){};r.prototype=n.prototype;var a=new r;o(a,e.prototype),e.prototype=a,e.prototype.constructor=e,e.Interface=o({},n.Interface,t),e.augmentClass=n.augmentClass,i.addPoolingTo(e,i.fourArgumentPooler)},i.addPoolingTo(r,i.fourArgumentPooler),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i={data:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n){var r=N.getPooled(L.change,e,t,n);return r.type=\"change\",y.accumulateTwoPhaseDispatches(r),r}function o(e){var t=e.nodeName&&e.nodeName.toLowerCase();return\"select\"===t||\"input\"===t&&\"file\"===e.type}function i(e){var t=r(j,e,E(e));C.batchedUpdates(a,t)}function a(e){b.enqueueEvents(e),b.processEventQueue(!1)}function s(e,t){k=e,j=t,k.attachEvent(\"onchange\",i)}function l(){k&&(k.detachEvent(\"onchange\",i),k=null,j=null)}function c(e,t){var n=I.updateValueIfChanged(e),r=t.simulated===!0&&R._allowSimulatedPassThrough;return n||r?e:void 0}function u(e,t){return\"topChange\"===e?t:void 0}function d(e,t,n){\"topFocus\"===e?(l(),s(t,n)):\"topBlur\"===e&&l()}function p(e,t){k=e,j=t,k.attachEvent(\"onpropertychange\",g)}function h(){k&&(k.detachEvent(\"onpropertychange\",g),k=null,j=null)}function g(e){\"value\"===e.propertyName&&c(j,e)&&i(e)}function f(e,t,n){\"topFocus\"===e?(h(),p(t,n)):\"topBlur\"===e&&h()}function m(e,t,n){return\"topSelectionChange\"===e||\"topKeyUp\"===e||\"topKeyDown\"===e?c(j,n):void 0}function A(e){var t=e.nodeName;return t&&\"input\"===t.toLowerCase()&&(\"checkbox\"===e.type||\"radio\"===e.type)}function M(e,t,n){return\"topClick\"===e?c(t,n):void 0}function w(e,t,n){return\"topInput\"===e||\"topChange\"===e?c(t,n):void 0}function v(e,t){if(null!=e){var n=e._wrapperState||t._wrapperState;if(n&&n.controlled&&\"number\"===t.type){var r=\"\"+t.value;t.getAttribute(\"value\")!==r&&t.setAttribute(\"value\",r)}}}var b=n(67),y=n(66),x=n(73),T=n(59),C=n(81),N=n(78),I=n(89),E=n(90),D=n(91),S=n(92),L={change:{phasedRegistrationNames:{bubbled:\"onChange\",captured:\"onChangeCapture\"},dependencies:[\"topBlur\",\"topChange\",\"topClick\",\"topFocus\",\"topInput\",\"topKeyDown\",\"topKeyUp\",\"topSelectionChange\"]}},k=null,j=null,U=!1;x.canUseDOM&&(U=D(\"change\")&&(!document.documentMode||document.documentMode>8));var B=!1;x.canUseDOM&&(B=D(\"input\")&&(!document.documentMode||document.documentMode>9));var R={eventTypes:L,_allowSimulatedPassThrough:!0,_isInputEventSupported:B,extractEvents:function(e,t,n,i){var a,s,l=t?T.getNodeFromInstance(t):window;if(o(l)?U?a=u:s=d:S(l)?B?a=w:(a=m,s=f):A(l)&&(a=M),a){var c=a(e,t,n);if(c){var p=r(c,n,i);return p}}s&&s(e,l,t),\"topBlur\"===e&&v(t,l)}};e.exports=R},function(e,t,n){\"use strict\";function r(){E.ReactReconcileTransaction&&y?void 0:u(\"123\")}function o(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=p.getPooled(),this.reconcileTransaction=E.ReactReconcileTransaction.getPooled(!0)}function i(e,t,n,o,i,a){return r(),y.batchedUpdates(e,t,n,o,i,a)}function a(e,t){return e._mountOrder-t._mountOrder}function s(e){var t=e.dirtyComponentsLength;t!==M.length?u(\"124\",t,M.length):void 0,M.sort(a),w++;for(var n=0;t>n;n++){var r=M[n],o=r._pendingCallbacks;r._pendingCallbacks=null;var i;if(g.logTopLevelRenders){var s=r;r._currentElement.type.isReactTopLevelWrapper&&(s=r._renderedComponent),i=\"React update: \"+s.getName(),console.time(i)}if(f.performUpdateIfNecessary(r,e.reconcileTransaction,w),i&&console.timeEnd(i),o)for(var l=0;l<o.length;l++)e.callbackQueue.enqueue(o[l],r.getPublicInstance())}}function l(e){return r(),y.isBatchingUpdates?(M.push(e),void(null==e._updateBatchNumber&&(e._updateBatchNumber=w+1))):void y.batchedUpdates(l,e)}function c(e,t){A(y.isBatchingUpdates,\"ReactUpdates.asap: Can't enqueue an asap callback in a context whereupdates are not being batched.\"),v.enqueue(e,t),b=!0}var u=n(60),d=n(26),p=n(82),h=n(75),g=n(83),f=n(84),m=n(88),A=n(34),M=[],w=0,v=p.getPooled(),b=!1,y=null,x={initialize:function(){\nthis.dirtyComponentsLength=M.length},close:function(){this.dirtyComponentsLength!==M.length?(M.splice(0,this.dirtyComponentsLength),N()):M.length=0}},T={initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}},C=[x,T];d(o.prototype,m,{getTransactionWrappers:function(){return C},destructor:function(){this.dirtyComponentsLength=null,p.release(this.callbackQueue),this.callbackQueue=null,E.ReactReconcileTransaction.release(this.reconcileTransaction),this.reconcileTransaction=null},perform:function(e,t,n){return m.perform.call(this,this.reconcileTransaction.perform,this.reconcileTransaction,e,t,n)}}),h.addPoolingTo(o);var N=function(){for(;M.length||b;){if(M.length){var e=o.getPooled();e.perform(s,null,e),o.release(e)}if(b){b=!1;var t=v;v=p.getPooled(),t.notifyAll(),p.release(t)}}},I={injectReconcileTransaction:function(e){e?void 0:u(\"126\"),E.ReactReconcileTransaction=e},injectBatchingStrategy:function(e){e?void 0:u(\"127\"),\"function\"!=typeof e.batchedUpdates?u(\"128\"):void 0,\"boolean\"!=typeof e.isBatchingUpdates?u(\"129\"):void 0,y=e}},E={ReactReconcileTransaction:null,batchedUpdates:i,enqueueUpdate:l,flushBatchedUpdates:N,injection:I,asap:c};e.exports=E},function(e,t,n){\"use strict\";function r(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}var o=n(60),i=n(75),a=(n(34),function(){function e(t){r(this,e),this._callbacks=null,this._contexts=null,this._arg=t}return e.prototype.enqueue=function(e,t){this._callbacks=this._callbacks||[],this._callbacks.push(e),this._contexts=this._contexts||[],this._contexts.push(t)},e.prototype.notifyAll=function(){var e=this._callbacks,t=this._contexts,n=this._arg;if(e&&t){e.length!==t.length?o(\"24\"):void 0,this._callbacks=null,this._contexts=null;for(var r=0;r<e.length;r++)e[r].call(t[r],n);e.length=0,t.length=0}},e.prototype.checkpoint=function(){return this._callbacks?this._callbacks.length:0},e.prototype.rollback=function(e){this._callbacks&&this._contexts&&(this._callbacks.length=e,this._contexts.length=e)},e.prototype.reset=function(){this._callbacks=null,this._contexts=null},e.prototype.destructor=function(){this.reset()},e}());e.exports=i.addPoolingTo(a)},function(e,t){\"use strict\";var n={logTopLevelRenders:!1};e.exports=n},function(e,t,n){\"use strict\";function r(){o.attachRefs(this,this._currentElement)}var o=n(85),i=(n(87),n(30),{mountComponent:function(e,t,n,o,i,a){var s=e.mountComponent(t,n,o,i,a);return e._currentElement&&null!=e._currentElement.ref&&t.getReactMountReady().enqueue(r,e),s},getHostNode:function(e){return e.getHostNode()},unmountComponent:function(e,t){o.detachRefs(e,e._currentElement),e.unmountComponent(t)},receiveComponent:function(e,t,n,i){var a=e._currentElement;if(t!==a||i!==e._context){var s=o.shouldUpdateRefs(a,t);s&&o.detachRefs(e,a),e.receiveComponent(t,n,i),s&&e._currentElement&&null!=e._currentElement.ref&&n.getReactMountReady().enqueue(r,e)}},performUpdateIfNecessary:function(e,t,n){e._updateBatchNumber===n&&e.performUpdateIfNecessary(t)}});e.exports=i},function(e,t,n){\"use strict\";function r(e,t,n){\"function\"==typeof e?e(t.getPublicInstance()):i.addComponentAsRefTo(t,e,n)}function o(e,t,n){\"function\"==typeof e?e(null):i.removeComponentAsRefFrom(t,e,n)}var i=n(86),a={};a.attachRefs=function(e,t){if(null!==t&&\"object\"==typeof t){var n=t.ref;null!=n&&r(n,e,t._owner)}},a.shouldUpdateRefs=function(e,t){var n=null,r=null;null!==e&&\"object\"==typeof e&&(n=e.ref,r=e._owner);var o=null,i=null;return null!==t&&\"object\"==typeof t&&(o=t.ref,i=t._owner),n!==o||\"string\"==typeof o&&i!==r},a.detachRefs=function(e,t){if(null!==t&&\"object\"==typeof t){var n=t.ref;null!=n&&o(n,e,t._owner)}},e.exports=a},function(e,t,n){\"use strict\";function r(e){return!(!e||\"function\"!=typeof e.attachRef||\"function\"!=typeof e.detachRef)}var o=n(60),i=(n(34),{addComponentAsRefTo:function(e,t,n){r(n)?void 0:o(\"119\"),n.attachRef(t,e)},removeComponentAsRefFrom:function(e,t,n){r(n)?void 0:o(\"120\");var i=n.getPublicInstance();i&&i.refs[t]===e.getPublicInstance()&&n.detachRef(t)}});e.exports=i},function(e,t,n){\"use strict\";var r=null;e.exports={debugTool:r}},function(e,t,n){\"use strict\";var r=n(60),o=(n(34),{}),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,s,l){this.isInTransaction()?r(\"27\"):void 0;var c,u;try{this._isInTransaction=!0,c=!0,this.initializeAll(0),u=e.call(t,n,o,i,a,s,l),c=!1}finally{try{if(c)try{this.closeAll(0)}catch(d){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return u},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n<t.length;n++){var r=t[n];try{this.wrapperInitData[n]=o,this.wrapperInitData[n]=r.initialize?r.initialize.call(this):null}finally{if(this.wrapperInitData[n]===o)try{this.initializeAll(n+1)}catch(i){}}}},closeAll:function(e){this.isInTransaction()?void 0:r(\"28\");for(var t=this.transactionWrappers,n=e;n<t.length;n++){var i,a=t[n],s=this.wrapperInitData[n];try{i=!0,s!==o&&a.close&&a.close.call(this,s),i=!1}finally{if(i)try{this.closeAll(n+1)}catch(l){}}}this.wrapperInitData.length=0}};e.exports=i},function(e,t,n){\"use strict\";function r(e){var t=e.type,n=e.nodeName;return n&&\"input\"===n.toLowerCase()&&(\"checkbox\"===t||\"radio\"===t)}function o(e){return e._wrapperState.valueTracker}function i(e,t){e._wrapperState.valueTracker=t}function a(e){e._wrapperState.valueTracker=null}function s(e){var t;return e&&(t=r(e)?\"\"+e.checked:e.value),t}var l=n(59),c={_getTrackerFromNode:function(e){return o(l.getInstanceFromNode(e))},track:function(e){if(!o(e)){var t=l.getNodeFromInstance(e),n=r(t)?\"checked\":\"value\",s=Object.getOwnPropertyDescriptor(t.constructor.prototype,n),c=\"\"+t[n];t.hasOwnProperty(n)||\"function\"!=typeof s.get||\"function\"!=typeof s.set||(Object.defineProperty(t,n,{enumerable:s.enumerable,configurable:!0,get:function(){return s.get.call(this)},set:function(e){c=\"\"+e,s.set.call(this,e)}}),i(e,{getValue:function(){return c},setValue:function(e){c=\"\"+e},stopTracking:function(){a(e),delete t[n]}}))}},updateValueIfChanged:function(e){if(!e)return!1;var t=o(e);if(!t)return c.track(e),!0;var n=t.getValue(),r=s(l.getNodeFromInstance(e));return r!==n?(t.setValue(r),!0):!1},stopTracking:function(e){var t=o(e);t&&t.stopTracking()}};e.exports=c},function(e,t){\"use strict\";function n(e){var t=e.target||e.srcElement||window;return t.correspondingUseElement&&(t=t.correspondingUseElement),3===t.nodeType?t.parentNode:t}e.exports=n},function(e,t,n){\"use strict\";/**\n\t * Checks if an event is supported in the current execution environment.\n\t *\n\t * NOTE: This will not work correctly for non-generic events such as `change`,\n\t * `reset`, `load`, `error`, and `select`.\n\t *\n\t * Borrows from Modernizr.\n\t *\n\t * @param {string} eventNameSuffix Event name, e.g. \"click\".\n\t * @param {?boolean} capture Check if the capture phase is supported.\n\t * @return {boolean} True if the event is supported.\n\t * @internal\n\t * @license Modernizr 3.0.0pre (Custom Build) | MIT\n\t */\nfunction r(e,t){if(!i.canUseDOM||t&&!(\"addEventListener\"in document))return!1;var n=\"on\"+e,r=n in document;if(!r){var a=document.createElement(\"div\");a.setAttribute(n,\"return;\"),r=\"function\"==typeof a[n]}return!r&&o&&\"wheel\"===e&&(r=document.implementation.hasFeature(\"Events.wheel\",\"3.0\")),r}var o,i=n(73);i.canUseDOM&&(o=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature(\"\",\"\")!==!0),e.exports=r},function(e,t){\"use strict\";function n(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return\"input\"===t?!!r[e.type]:\"textarea\"===t?!0:!1}var r={color:!0,date:!0,datetime:!0,\"datetime-local\":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};e.exports=n},function(e,t){\"use strict\";var n=[\"ResponderEventPlugin\",\"SimpleEventPlugin\",\"TapEventPlugin\",\"EnterLeaveEventPlugin\",\"ChangeEventPlugin\",\"SelectEventPlugin\",\"BeforeInputEventPlugin\"];e.exports=n},function(e,t,n){\"use strict\";var r=n(66),o=n(59),i=n(95),a={mouseEnter:{registrationName:\"onMouseEnter\",dependencies:[\"topMouseOut\",\"topMouseOver\"]},mouseLeave:{registrationName:\"onMouseLeave\",dependencies:[\"topMouseOut\",\"topMouseOver\"]}},s={eventTypes:a,extractEvents:function(e,t,n,s){if(\"topMouseOver\"===e&&(n.relatedTarget||n.fromElement))return null;if(\"topMouseOut\"!==e&&\"topMouseOver\"!==e)return null;var l;if(s.window===s)l=s;else{var c=s.ownerDocument;l=c?c.defaultView||c.parentWindow:window}var u,d;if(\"topMouseOut\"===e){u=t;var p=n.relatedTarget||n.toElement;d=p?o.getClosestInstanceFromNode(p):null}else u=null,d=t;if(u===d)return null;var h=null==u?l:o.getNodeFromInstance(u),g=null==d?l:o.getNodeFromInstance(d),f=i.getPooled(a.mouseLeave,u,n,s);f.type=\"mouseleave\",f.target=h,f.relatedTarget=g;var m=i.getPooled(a.mouseEnter,d,n,s);return m.type=\"mouseenter\",m.target=g,m.relatedTarget=h,r.accumulateEnterLeaveDispatches(f,m,u,d),[f,m]}};e.exports=s},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(96),i=n(97),a=n(98),s={screenX:null,screenY:null,clientX:null,clientY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:a,button:function(e){var t=e.button;return\"which\"in e?t:2===t?2:4===t?1:0},buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},pageX:function(e){return\"pageX\"in e?e.pageX:e.clientX+i.currentScrollLeft},pageY:function(e){return\"pageY\"in e?e.pageY:e.clientY+i.currentScrollTop}};o.augmentClass(r,s),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i=n(90),a={view:function(e){if(e.view)return e.view;var t=i(e);if(t.window===t)return t;var n=t.ownerDocument;return n?n.defaultView||n.parentWindow:window},detail:function(e){return e.detail||0}};o.augmentClass(r,a),e.exports=r},function(e,t){\"use strict\";var n={currentScrollLeft:0,currentScrollTop:0,refreshScrollValues:function(e){n.currentScrollLeft=e.x,n.currentScrollTop=e.y}};e.exports=n},function(e,t){\"use strict\";function n(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=o[e];return r?!!n[r]:!1}function r(e){return n}var o={Alt:\"altKey\",Control:\"ctrlKey\",Meta:\"metaKey\",Shift:\"shiftKey\"};e.exports=r},function(e,t,n){\"use strict\";var r=n(61),o=r.injection.MUST_USE_PROPERTY,i=r.injection.HAS_BOOLEAN_VALUE,a=r.injection.HAS_NUMERIC_VALUE,s=r.injection.HAS_POSITIVE_NUMERIC_VALUE,l=r.injection.HAS_OVERLOADED_BOOLEAN_VALUE,c={isCustomAttribute:RegExp.prototype.test.bind(new RegExp(\"^(data|aria)-[\"+r.ATTRIBUTE_NAME_CHAR+\"]*$\")),Properties:{accept:0,acceptCharset:0,accessKey:0,action:0,allowFullScreen:i,allowTransparency:0,alt:0,as:0,async:i,autoComplete:0,autoPlay:i,capture:i,cellPadding:0,cellSpacing:0,charSet:0,challenge:0,checked:o|i,cite:0,classID:0,className:0,cols:s,colSpan:0,content:0,contentEditable:0,contextMenu:0,controls:i,controlsList:0,coords:0,crossOrigin:0,data:0,dateTime:0,\"default\":i,defer:i,dir:0,disabled:i,download:l,draggable:0,encType:0,form:0,formAction:0,formEncType:0,formMethod:0,formNoValidate:i,formTarget:0,frameBorder:0,headers:0,height:0,hidden:i,high:0,href:0,hrefLang:0,htmlFor:0,httpEquiv:0,icon:0,id:0,inputMode:0,integrity:0,is:0,keyParams:0,keyType:0,kind:0,label:0,lang:0,list:0,loop:i,low:0,manifest:0,marginHeight:0,marginWidth:0,max:0,maxLength:0,media:0,mediaGroup:0,method:0,min:0,minLength:0,multiple:o|i,muted:o|i,name:0,nonce:0,noValidate:i,open:i,optimum:0,pattern:0,placeholder:0,playsInline:i,poster:0,preload:0,profile:0,radioGroup:0,readOnly:i,referrerPolicy:0,rel:0,required:i,reversed:i,role:0,rows:s,rowSpan:a,sandbox:0,scope:0,scoped:i,scrolling:0,seamless:i,selected:o|i,shape:0,size:s,sizes:0,span:s,spellCheck:0,src:0,srcDoc:0,srcLang:0,srcSet:0,start:a,step:0,style:0,summary:0,tabIndex:0,target:0,title:0,type:0,useMap:0,value:0,width:0,wmode:0,wrap:0,about:0,datatype:0,inlist:0,prefix:0,property:0,resource:0,\"typeof\":0,vocab:0,autoCapitalize:0,autoCorrect:0,autoSave:0,color:0,itemProp:0,itemScope:i,itemType:0,itemID:0,itemRef:0,results:0,security:0,unselectable:0},DOMAttributeNames:{acceptCharset:\"accept-charset\",className:\"class\",htmlFor:\"for\",httpEquiv:\"http-equiv\"},DOMPropertyNames:{},DOMMutationMethods:{value:function(e,t){return null==t?e.removeAttribute(\"value\"):void(\"number\"!==e.type||e.hasAttribute(\"value\")===!1?e.setAttribute(\"value\",\"\"+t):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute(\"value\",\"\"+t))}}};e.exports=c},function(e,t,n){\"use strict\";var r=n(101),o=n(112),i={processChildrenUpdates:o.dangerouslyProcessChildrenUpdates,replaceNodeWithMarkup:r.dangerouslyReplaceNodeWithMarkup};e.exports=i},function(e,t,n){\"use strict\";function r(e,t){return Array.isArray(t)&&(t=t[1]),t?t.nextSibling:e.firstChild}function o(e,t,n){u.insertTreeBefore(e,t,n)}function i(e,t,n){Array.isArray(t)?s(e,t[0],t[1],n):f(e,t,n)}function a(e,t){if(Array.isArray(t)){var n=t[1];t=t[0],l(e,t,n),e.removeChild(n)}e.removeChild(t)}function s(e,t,n,r){for(var o=t;;){var i=o.nextSibling;if(f(e,o,r),o===n)break;o=i}}function l(e,t,n){for(;;){var r=t.nextSibling;if(r===n)break;e.removeChild(r)}}function c(e,t,n){var r=e.parentNode,o=e.nextSibling;o===t?n&&f(r,document.createTextNode(n),o):n?(g(o,n),l(r,o,t)):l(r,e,t)}var u=n(102),d=n(108),p=(n(59),n(87),n(105)),h=n(104),g=n(106),f=p(function(e,t,n){e.insertBefore(t,n)}),m=d.dangerouslyReplaceNodeWithMarkup,A={dangerouslyReplaceNodeWithMarkup:m,replaceDelimitedText:c,processUpdates:function(e,t){for(var n=0;n<t.length;n++){var s=t[n];switch(s.type){case\"INSERT_MARKUP\":o(e,s.content,r(e,s.afterNode));break;case\"MOVE_EXISTING\":i(e,s.fromNode,r(e,s.afterNode));break;case\"SET_MARKUP\":h(e,s.content);break;case\"TEXT_CONTENT\":g(e,s.content);break;case\"REMOVE_NODE\":a(e,s.fromNode)}}}};e.exports=A},function(e,t,n){\"use strict\";function r(e){if(m){var t=e.node,n=e.children;if(n.length)for(var r=0;r<n.length;r++)A(t,n[r],null);else null!=e.html?d(t,e.html):null!=e.text&&h(t,e.text)}}function o(e,t){e.parentNode.replaceChild(t.node,e),r(t)}function i(e,t){m?e.children.push(t):e.node.appendChild(t.node)}function a(e,t){m?e.html=t:d(e.node,t)}function s(e,t){m?e.text=t:h(e.node,t)}function l(){return this.node.nodeName}function c(e){return{node:e,children:[],html:null,text:null,toString:l}}var u=n(103),d=n(104),p=n(105),h=n(106),g=1,f=11,m=\"undefined\"!=typeof document&&\"number\"==typeof document.documentMode||\"undefined\"!=typeof navigator&&\"string\"==typeof navigator.userAgent&&/\\bEdge\\/\\d/.test(navigator.userAgent),A=p(function(e,t,n){t.node.nodeType===f||t.node.nodeType===g&&\"object\"===t.node.nodeName.toLowerCase()&&(null==t.node.namespaceURI||t.node.namespaceURI===u.html)?(r(t),e.insertBefore(t.node,n)):(e.insertBefore(t.node,n),r(t))});c.insertTreeBefore=A,c.replaceChildWithTree=o,c.queueChild=i,c.queueHTML=a,c.queueText=s,e.exports=c},function(e,t){\"use strict\";var n={html:\"http://www.w3.org/1999/xhtml\",mathml:\"http://www.w3.org/1998/Math/MathML\",svg:\"http://www.w3.org/2000/svg\"};e.exports=n},function(e,t,n){\"use strict\";var r,o=n(73),i=n(103),a=/^[ \\r\\n\\t\\f]/,s=/<(!--|link|noscript|meta|script|style)[ \\r\\n\\t\\f\\/>]/,l=n(105),c=l(function(e,t){if(e.namespaceURI!==i.svg||\"innerHTML\"in e)e.innerHTML=t;else{r=r||document.createElement(\"div\"),r.innerHTML=\"<svg>\"+t+\"</svg>\";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(o.canUseDOM){var u=document.createElement(\"div\");u.innerHTML=\" \",\"\"===u.innerHTML&&(c=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||\"<\"===t[0]&&s.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),u=null}e.exports=c},function(e,t){\"use strict\";var n=function(e){return\"undefined\"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(t,n,r,o){MSApp.execUnsafeLocalFunction(function(){return e(t,n,r,o)})}:e};e.exports=n},function(e,t,n){\"use strict\";var r=n(73),o=n(107),i=n(104),a=function(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t};r.canUseDOM&&(\"textContent\"in document.documentElement||(a=function(e,t){return 3===e.nodeType?void(e.nodeValue=t):void i(e,o(t))})),e.exports=a},function(e,t){\"use strict\";function n(e){var t=\"\"+e,n=o.exec(t);if(!n)return t;var r,i=\"\",a=0,s=0;for(a=n.index;a<t.length;a++){switch(t.charCodeAt(a)){case 34:r=\"&quot;\";break;case 38:r=\"&amp;\";break;case 39:r=\"&#x27;\";break;case 60:r=\"&lt;\";break;case 62:r=\"&gt;\";break;default:continue}s!==a&&(i+=t.substring(s,a)),s=a+1,i+=r}return s!==a?i+t.substring(s,a):i}function r(e){return\"boolean\"==typeof e||\"number\"==typeof e?\"\"+e:n(e)}var o=/[\"'&<>]/;e.exports=r},function(e,t,n){\"use strict\";var r=n(60),o=n(102),i=n(73),a=n(109),s=n(31),l=(n(34),{dangerouslyReplaceNodeWithMarkup:function(e,t){if(i.canUseDOM?void 0:r(\"56\"),t?void 0:r(\"57\"),\"HTML\"===e.nodeName?r(\"58\"):void 0,\"string\"==typeof t){var n=a(t,s)[0];e.parentNode.replaceChild(n,e)}else o.replaceChildWithTree(e,t)}});e.exports=l},function(e,t,n){\"use strict\";function r(e){var t=e.match(u);return t&&t[1].toLowerCase()}function o(e,t){var n=c;c?void 0:l(!1);var o=r(e),i=o&&s(o);if(i){n.innerHTML=i[1]+e+i[2];for(var u=i[0];u--;)n=n.lastChild}else n.innerHTML=e;var d=n.getElementsByTagName(\"script\");d.length&&(t?void 0:l(!1),a(d).forEach(t));for(var p=Array.from(n.childNodes);n.lastChild;)n.removeChild(n.lastChild);return p}var i=n(73),a=n(110),s=n(111),l=n(34),c=i.canUseDOM?document.createElement(\"div\"):null,u=/^\\s*<(\\w+)/;e.exports=o},function(e,t,n){\"use strict\";function r(e){var t=e.length;if(Array.isArray(e)||\"object\"!=typeof e&&\"function\"!=typeof e?a(!1):void 0,\"number\"!=typeof t?a(!1):void 0,0===t||t-1 in e?void 0:a(!1),\"function\"==typeof e.callee?a(!1):void 0,e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(n){}for(var r=Array(t),o=0;t>o;o++)r[o]=e[o];return r}function o(e){return!!e&&(\"object\"==typeof e||\"function\"==typeof e)&&\"length\"in e&&!(\"setInterval\"in e)&&\"number\"!=typeof e.nodeType&&(Array.isArray(e)||\"callee\"in e||\"item\"in e)}function i(e){return o(e)?Array.isArray(e)?e.slice():r(e):[e]}var a=n(34);e.exports=i},function(e,t,n){\"use strict\";function r(e){return a?void 0:i(!1),p.hasOwnProperty(e)||(e=\"*\"),s.hasOwnProperty(e)||(\"*\"===e?a.innerHTML=\"<link />\":a.innerHTML=\"<\"+e+\"></\"+e+\">\",s[e]=!a.firstChild),s[e]?p[e]:null}var o=n(73),i=n(34),a=o.canUseDOM?document.createElement(\"div\"):null,s={},l=[1,'<select multiple=\"true\">',\"</select>\"],c=[1,\"<table>\",\"</table>\"],u=[3,\"<table><tbody><tr>\",\"</tr></tbody></table>\"],d=[1,'<svg xmlns=\"http://www.w3.org/2000/svg\">',\"</svg>\"],p={\"*\":[1,\"?<div>\",\"</div>\"],area:[1,\"<map>\",\"</map>\"],col:[2,\"<table><tbody></tbody><colgroup>\",\"</colgroup></table>\"],legend:[1,\"<fieldset>\",\"</fieldset>\"],param:[1,\"<object>\",\"</object>\"],tr:[2,\"<table><tbody>\",\"</tbody></table>\"],optgroup:l,option:l,caption:c,colgroup:c,tbody:c,tfoot:c,thead:c,td:u,th:u},h=[\"circle\",\"clipPath\",\"defs\",\"ellipse\",\"g\",\"image\",\"line\",\"linearGradient\",\"mask\",\"path\",\"pattern\",\"polygon\",\"polyline\",\"radialGradient\",\"rect\",\"stop\",\"text\",\"tspan\"];h.forEach(function(e){p[e]=d,s[e]=!0}),e.exports=r},function(e,t,n){\"use strict\";var r=n(101),o=n(59),i={dangerouslyProcessChildrenUpdates:function(e,t){var n=o.getNodeFromInstance(e);r.processUpdates(n,t)}};e.exports=i},function(e,t,n){\"use strict\";function r(e){if(e){var t=e._currentElement._owner||null;if(t){var n=t.getName();if(n)return\" This DOM node was rendered by `\"+n+\"`.\"}}return\"\"}function o(e,t){t&&(J[e._tag]&&(null!=t.children||null!=t.dangerouslySetInnerHTML?m(\"137\",e._tag,e._currentElement._owner?\" Check the render method of \"+e._currentElement._owner.getName()+\".\":\"\"):void 0),null!=t.dangerouslySetInnerHTML&&(null!=t.children?m(\"60\"):void 0,\"object\"==typeof t.dangerouslySetInnerHTML&&Y in t.dangerouslySetInnerHTML?void 0:m(\"61\")),null!=t.style&&\"object\"!=typeof t.style?m(\"62\",r(e)):void 0)}function i(e,t,n,r){if(!(r instanceof U)){var o=e._hostContainerInfo,i=o._node&&o._node.nodeType===W,s=i?o._node:o._ownerDocument;H(t,s),r.getReactMountReady().enqueue(a,{inst:e,registrationName:t,listener:n})}}function a(){var e=this;T.putListener(e.inst,e.registrationName,e.listener)}function s(){var e=this;D.postMountWrapper(e)}function l(){var e=this;k.postMountWrapper(e)}function c(){var e=this;S.postMountWrapper(e)}function u(){R.track(this)}function d(){var e=this;e._rootNodeID?void 0:m(\"63\");var t=O(e);switch(t?void 0:m(\"64\"),e._tag){case\"iframe\":case\"object\":e._wrapperState.listeners=[N.trapBubbledEvent(\"topLoad\",\"load\",t)];break;case\"video\":case\"audio\":e._wrapperState.listeners=[];for(var n in X)X.hasOwnProperty(n)&&e._wrapperState.listeners.push(N.trapBubbledEvent(n,X[n],t));break;case\"source\":e._wrapperState.listeners=[N.trapBubbledEvent(\"topError\",\"error\",t)];break;case\"img\":e._wrapperState.listeners=[N.trapBubbledEvent(\"topError\",\"error\",t),N.trapBubbledEvent(\"topLoad\",\"load\",t)];break;case\"form\":e._wrapperState.listeners=[N.trapBubbledEvent(\"topReset\",\"reset\",t),N.trapBubbledEvent(\"topSubmit\",\"submit\",t)];break;case\"input\":case\"select\":case\"textarea\":e._wrapperState.listeners=[N.trapBubbledEvent(\"topInvalid\",\"invalid\",t)]}}function p(){L.postUpdateWrapper(this)}function h(e){$.call(Z,e)||(K.test(e)?void 0:m(\"65\",e),Z[e]=!0)}function g(e,t){return e.indexOf(\"-\")>=0||null!=t.is}function f(e){var t=e.type;h(t),this._currentElement=e,this._tag=t.toLowerCase(),this._namespaceURI=null,this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._hostNode=null,this._hostParent=null,this._rootNodeID=0,this._domID=0,this._hostContainerInfo=null,this._wrapperState=null,this._topLevelWrapper=null,this._flags=0}var m=n(60),A=n(26),M=n(114),w=n(116),v=n(102),b=n(103),y=n(61),x=n(124),T=n(67),C=n(68),N=n(126),I=n(62),E=n(59),D=n(129),S=n(132),L=n(133),k=n(134),j=(n(87),n(135)),U=n(154),B=(n(31),n(107)),R=(n(34),n(91),n(143),n(89)),z=(n(157),n(30),I),Q=T.deleteListener,O=E.getNodeFromInstance,H=N.listenTo,F=C.registrationNameModules,V={string:!0,number:!0},P=\"style\",Y=\"__html\",G={children:null,dangerouslySetInnerHTML:null,suppressContentEditableWarning:null},W=11,X={topAbort:\"abort\",topCanPlay:\"canplay\",topCanPlayThrough:\"canplaythrough\",topDurationChange:\"durationchange\",topEmptied:\"emptied\",topEncrypted:\"encrypted\",topEnded:\"ended\",topError:\"error\",topLoadedData:\"loadeddata\",topLoadedMetadata:\"loadedmetadata\",topLoadStart:\"loadstart\",topPause:\"pause\",topPlay:\"play\",topPlaying:\"playing\",topProgress:\"progress\",topRateChange:\"ratechange\",topSeeked:\"seeked\",topSeeking:\"seeking\",topStalled:\"stalled\",topSuspend:\"suspend\",topTimeUpdate:\"timeupdate\",topVolumeChange:\"volumechange\",topWaiting:\"waiting\"},_={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},q={listing:!0,pre:!0,textarea:!0},J=A({menuitem:!0},_),K=/^[a-zA-Z][a-zA-Z:_\\.\\-\\d]*$/,Z={},$={}.hasOwnProperty,ee=1;f.displayName=\"ReactDOMComponent\",f.Mixin={mountComponent:function(e,t,n,r){this._rootNodeID=ee++,this._domID=n._idCounter++,this._hostParent=t,this._hostContainerInfo=n;var i=this._currentElement.props;switch(this._tag){case\"audio\":case\"form\":case\"iframe\":case\"img\":case\"link\":case\"object\":case\"source\":case\"video\":this._wrapperState={listeners:null},e.getReactMountReady().enqueue(d,this);break;case\"input\":D.mountWrapper(this,i,t),i=D.getHostProps(this,i),e.getReactMountReady().enqueue(u,this),e.getReactMountReady().enqueue(d,this);break;case\"option\":S.mountWrapper(this,i,t),i=S.getHostProps(this,i);break;case\"select\":L.mountWrapper(this,i,t),i=L.getHostProps(this,i),e.getReactMountReady().enqueue(d,this);break;case\"textarea\":k.mountWrapper(this,i,t),i=k.getHostProps(this,i),e.getReactMountReady().enqueue(u,this),e.getReactMountReady().enqueue(d,this)}o(this,i);var a,p;null!=t?(a=t._namespaceURI,p=t._tag):n._tag&&(a=n._namespaceURI,p=n._tag),(null==a||a===b.svg&&\"foreignobject\"===p)&&(a=b.html),a===b.html&&(\"svg\"===this._tag?a=b.svg:\"math\"===this._tag&&(a=b.mathml)),this._namespaceURI=a;var h;if(e.useCreateElement){var g,f=n._ownerDocument;if(a===b.html)if(\"script\"===this._tag){var m=f.createElement(\"div\"),A=this._currentElement.type;m.innerHTML=\"<\"+A+\"></\"+A+\">\",g=m.removeChild(m.firstChild)}else g=i.is?f.createElement(this._currentElement.type,i.is):f.createElement(this._currentElement.type);else g=f.createElementNS(a,this._currentElement.type);E.precacheNode(this,g),this._flags|=z.hasCachedChildNodes,this._hostParent||x.setAttributeForRoot(g),this._updateDOMProperties(null,i,e);var w=v(g);this._createInitialChildren(e,i,r,w),h=w}else{var y=this._createOpenTagMarkupAndPutListeners(e,i),T=this._createContentMarkup(e,i,r);h=!T&&_[this._tag]?y+\"/>\":y+\">\"+T+\"</\"+this._currentElement.type+\">\"}switch(this._tag){case\"input\":e.getReactMountReady().enqueue(s,this),i.autoFocus&&e.getReactMountReady().enqueue(M.focusDOMComponent,this);break;case\"textarea\":e.getReactMountReady().enqueue(l,this),i.autoFocus&&e.getReactMountReady().enqueue(M.focusDOMComponent,this);break;case\"select\":i.autoFocus&&e.getReactMountReady().enqueue(M.focusDOMComponent,this);break;case\"button\":i.autoFocus&&e.getReactMountReady().enqueue(M.focusDOMComponent,this);break;case\"option\":e.getReactMountReady().enqueue(c,this)}return h},_createOpenTagMarkupAndPutListeners:function(e,t){var n=\"<\"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var o=t[r];if(null!=o)if(F.hasOwnProperty(r))o&&i(this,r,o,e);else{r===P&&(o&&(o=this._previousStyleCopy=A({},t.style)),o=w.createMarkupForStyles(o,this));var a=null;null!=this._tag&&g(this._tag,t)?G.hasOwnProperty(r)||(a=x.createMarkupForCustomAttribute(r,o)):a=x.createMarkupForProperty(r,o),a&&(n+=\" \"+a)}}return e.renderToStaticMarkup?n:(this._hostParent||(n+=\" \"+x.createMarkupForRoot()),n+=\" \"+x.createMarkupForID(this._domID))},_createContentMarkup:function(e,t,n){var r=\"\",o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&(r=o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)r=B(i);else if(null!=a){var s=this.mountChildren(a,e,n);r=s.join(\"\")}}return q[this._tag]&&\"\\n\"===r.charAt(0)?\"\\n\"+r:r},_createInitialChildren:function(e,t,n,r){var o=t.dangerouslySetInnerHTML;if(null!=o)null!=o.__html&&v.queueHTML(r,o.__html);else{var i=V[typeof t.children]?t.children:null,a=null!=i?null:t.children;if(null!=i)\"\"!==i&&v.queueText(r,i);else if(null!=a)for(var s=this.mountChildren(a,e,n),l=0;l<s.length;l++)v.queueChild(r,s[l])}},receiveComponent:function(e,t,n){var r=this._currentElement;this._currentElement=e,this.updateComponent(t,r,e,n)},updateComponent:function(e,t,n,r){var i=t.props,a=this._currentElement.props;switch(this._tag){case\"input\":i=D.getHostProps(this,i),a=D.getHostProps(this,a);break;case\"option\":i=S.getHostProps(this,i),a=S.getHostProps(this,a);break;case\"select\":i=L.getHostProps(this,i),a=L.getHostProps(this,a);break;case\"textarea\":i=k.getHostProps(this,i),a=k.getHostProps(this,a)}switch(o(this,a),this._updateDOMProperties(i,a,e),this._updateDOMChildren(i,a,e,r),this._tag){case\"input\":D.updateWrapper(this),R.updateValueIfChanged(this);break;case\"textarea\":k.updateWrapper(this);break;case\"select\":e.getReactMountReady().enqueue(p,this)}},_updateDOMProperties:function(e,t,n){var r,o,a;for(r in e)if(!t.hasOwnProperty(r)&&e.hasOwnProperty(r)&&null!=e[r])if(r===P){var s=this._previousStyleCopy;for(o in s)s.hasOwnProperty(o)&&(a=a||{},a[o]=\"\");this._previousStyleCopy=null}else F.hasOwnProperty(r)?e[r]&&Q(this,r):g(this._tag,e)?G.hasOwnProperty(r)||x.deleteValueForAttribute(O(this),r):(y.properties[r]||y.isCustomAttribute(r))&&x.deleteValueForProperty(O(this),r);for(r in t){var l=t[r],c=r===P?this._previousStyleCopy:null!=e?e[r]:void 0;if(t.hasOwnProperty(r)&&l!==c&&(null!=l||null!=c))if(r===P)if(l?l=this._previousStyleCopy=A({},l):this._previousStyleCopy=null,c){for(o in c)!c.hasOwnProperty(o)||l&&l.hasOwnProperty(o)||(a=a||{},a[o]=\"\");for(o in l)l.hasOwnProperty(o)&&c[o]!==l[o]&&(a=a||{},a[o]=l[o])}else a=l;else if(F.hasOwnProperty(r))l?i(this,r,l,n):c&&Q(this,r);else if(g(this._tag,t))G.hasOwnProperty(r)||x.setValueForAttribute(O(this),r,l);else if(y.properties[r]||y.isCustomAttribute(r)){var u=O(this);null!=l?x.setValueForProperty(u,r,l):x.deleteValueForProperty(u,r)}}a&&w.setValueForStyles(O(this),a,this)},_updateDOMChildren:function(e,t,n,r){var o=V[typeof e.children]?e.children:null,i=V[typeof t.children]?t.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,s=t.dangerouslySetInnerHTML&&t.dangerouslySetInnerHTML.__html,l=null!=o?null:e.children,c=null!=i?null:t.children,u=null!=o||null!=a,d=null!=i||null!=s;null!=l&&null==c?this.updateChildren(null,n,r):u&&!d&&this.updateTextContent(\"\"),null!=i?o!==i&&this.updateTextContent(\"\"+i):null!=s?a!==s&&this.updateMarkup(\"\"+s):null!=c&&this.updateChildren(c,n,r)},getHostNode:function(){return O(this)},unmountComponent:function(e){switch(this._tag){case\"audio\":case\"form\":case\"iframe\":case\"img\":case\"link\":case\"object\":case\"source\":case\"video\":var t=this._wrapperState.listeners;if(t)for(var n=0;n<t.length;n++)t[n].remove();break;case\"input\":case\"textarea\":R.stopTracking(this);break;case\"html\":case\"head\":case\"body\":m(\"66\",this._tag)}this.unmountChildren(e),E.uncacheNode(this),T.deleteAllListeners(this),this._rootNodeID=0,this._domID=0,this._wrapperState=null},getPublicInstance:function(){return O(this)}},A(f.prototype,f.Mixin,j.Mixin),e.exports=f},function(e,t,n){\"use strict\";var r=n(59),o=n(115),i={focusDOMComponent:function(){o(r.getNodeFromInstance(this))}};e.exports=i},function(e,t){\"use strict\";function n(e){try{e.focus()}catch(t){}}e.exports=n},function(e,t,n){\"use strict\";var r=n(117),o=n(73),i=(n(87),n(118),n(120)),a=n(121),s=n(123),l=(n(30),s(function(e){return a(e)})),c=!1,u=\"cssFloat\";if(o.canUseDOM){var d=document.createElement(\"div\").style;try{d.font=\"\"}catch(p){c=!0}void 0===document.documentElement.style.cssFloat&&(u=\"styleFloat\")}var h={createMarkupForStyles:function(e,t){var n=\"\";for(var r in e)if(e.hasOwnProperty(r)){var o=0===r.indexOf(\"--\"),a=e[r];null!=a&&(n+=l(r)+\":\",n+=i(r,a,t,o)+\";\")}return n||null},setValueForStyles:function(e,t,n){var o=e.style;for(var a in t)if(t.hasOwnProperty(a)){var s=0===a.indexOf(\"--\"),l=i(a,t[a],n,s);if((\"float\"===a||\"cssFloat\"===a)&&(a=u),s)o.setProperty(a,l);else if(l)o[a]=l;else{var d=c&&r.shorthandPropertyExpansions[a];if(d)for(var p in d)o[p]=\"\";else o[a]=\"\"}}}};e.exports=h},function(e,t){\"use strict\";function n(e,t){return e+t.charAt(0).toUpperCase()+t.substring(1)}var r={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},o=[\"Webkit\",\"ms\",\"Moz\",\"O\"];Object.keys(r).forEach(function(e){o.forEach(function(t){r[n(t,e)]=r[e]})});var i={background:{backgroundAttachment:!0,backgroundColor:!0,backgroundImage:!0,backgroundPositionX:!0,backgroundPositionY:!0,backgroundRepeat:!0},backgroundPosition:{backgroundPositionX:!0,backgroundPositionY:!0},border:{borderWidth:!0,borderStyle:!0,borderColor:!0},borderBottom:{borderBottomWidth:!0,borderBottomStyle:!0,borderBottomColor:!0},borderLeft:{borderLeftWidth:!0,borderLeftStyle:!0,borderLeftColor:!0},borderRight:{borderRightWidth:!0,borderRightStyle:!0,borderRightColor:!0},borderTop:{borderTopWidth:!0,borderTopStyle:!0,borderTopColor:!0},font:{fontStyle:!0,fontVariant:!0,fontWeight:!0,fontSize:!0,lineHeight:!0,fontFamily:!0},outline:{outlineWidth:!0,outlineStyle:!0,outlineColor:!0}},a={isUnitlessNumber:r,shorthandPropertyExpansions:i};e.exports=a},function(e,t,n){\"use strict\";function r(e){return o(e.replace(i,\"ms-\"))}var o=n(119),i=/^-ms-/;e.exports=r},function(e,t){\"use strict\";function n(e){return e.replace(r,function(e,t){return t.toUpperCase()})}var r=/-(.)/g;e.exports=n},function(e,t,n){\"use strict\";function r(e,t,n,r){var o=null==t||\"boolean\"==typeof t||\"\"===t;if(o)return\"\";var a=isNaN(t);if(r||a||0===t||i.hasOwnProperty(e)&&i[e])return\"\"+t;if(\"string\"==typeof t){t=t.trim()}return t+\"px\"}var o=n(117),i=(n(30),o.isUnitlessNumber);e.exports=r},function(e,t,n){\"use strict\";function r(e){return o(e).replace(i,\"-ms-\")}var o=n(122),i=/^ms-/;e.exports=r},function(e,t){\"use strict\";function n(e){return e.replace(r,\"-$1\").toLowerCase()}var r=/([A-Z])/g;e.exports=n},function(e,t){\"use strict\";function n(e){var t={};return function(n){return t.hasOwnProperty(n)||(t[n]=e.call(this,n)),t[n]}}e.exports=n},function(e,t,n){\"use strict\";function r(e){return c.hasOwnProperty(e)?!0:l.hasOwnProperty(e)?!1:s.test(e)?(c[e]=!0,!0):(l[e]=!0,!1)}function o(e,t){return null==t||e.hasBooleanValue&&!t||e.hasNumericValue&&isNaN(t)||e.hasPositiveNumericValue&&1>t||e.hasOverloadedBooleanValue&&t===!1}var i=n(61),a=(n(59),n(87),n(125)),s=(n(30),new RegExp(\"^[\"+i.ATTRIBUTE_NAME_START_CHAR+\"][\"+i.ATTRIBUTE_NAME_CHAR+\"]*$\")),l={},c={},u={createMarkupForID:function(e){return i.ID_ATTRIBUTE_NAME+\"=\"+a(e)},setAttributeForID:function(e,t){e.setAttribute(i.ID_ATTRIBUTE_NAME,t)},createMarkupForRoot:function(){return i.ROOT_ATTRIBUTE_NAME+'=\"\"'},setAttributeForRoot:function(e){e.setAttribute(i.ROOT_ATTRIBUTE_NAME,\"\")},createMarkupForProperty:function(e,t){var n=i.properties.hasOwnProperty(e)?i.properties[e]:null;if(n){if(o(n,t))return\"\";var r=n.attributeName;return n.hasBooleanValue||n.hasOverloadedBooleanValue&&t===!0?r+'=\"\"':r+\"=\"+a(t)}return i.isCustomAttribute(e)?null==t?\"\":e+\"=\"+a(t):null},createMarkupForCustomAttribute:function(e,t){return r(e)&&null!=t?e+\"=\"+a(t):\"\"},setValueForProperty:function(e,t,n){var r=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(r){var a=r.mutationMethod;if(a)a(e,n);else{if(o(r,n))return void this.deleteValueForProperty(e,t);if(r.mustUseProperty)e[r.propertyName]=n;else{var s=r.attributeName,l=r.attributeNamespace;l?e.setAttributeNS(l,s,\"\"+n):r.hasBooleanValue||r.hasOverloadedBooleanValue&&n===!0?e.setAttribute(s,\"\"):e.setAttribute(s,\"\"+n)}}}else if(i.isCustomAttribute(t))return void u.setValueForAttribute(e,t,n)},setValueForAttribute:function(e,t,n){if(r(t)){null==n?e.removeAttribute(t):e.setAttribute(t,\"\"+n)}},deleteValueForAttribute:function(e,t){e.removeAttribute(t)},deleteValueForProperty:function(e,t){var n=i.properties.hasOwnProperty(t)?i.properties[t]:null;if(n){var r=n.mutationMethod;if(r)r(e,void 0);else if(n.mustUseProperty){var o=n.propertyName;n.hasBooleanValue?e[o]=!1:e[o]=\"\"}else e.removeAttribute(n.attributeName)}else i.isCustomAttribute(t)&&e.removeAttribute(t)}};e.exports=u},function(e,t,n){\"use strict\";function r(e){return'\"'+o(e)+'\"'}var o=n(107);e.exports=r},function(e,t,n){\"use strict\";function r(e){return Object.prototype.hasOwnProperty.call(e,f)||(e[f]=h++,d[e[f]]={}),d[e[f]]}var o,i=n(26),a=n(68),s=n(127),l=n(97),c=n(128),u=n(91),d={},p=!1,h=0,g={topAbort:\"abort\",topAnimationEnd:c(\"animationend\")||\"animationend\",topAnimationIteration:c(\"animationiteration\")||\"animationiteration\",topAnimationStart:c(\"animationstart\")||\"animationstart\",topBlur:\"blur\",topCanPlay:\"canplay\",topCanPlayThrough:\"canplaythrough\",topChange:\"change\",topClick:\"click\",topCompositionEnd:\"compositionend\",topCompositionStart:\"compositionstart\",topCompositionUpdate:\"compositionupdate\",topContextMenu:\"contextmenu\",topCopy:\"copy\",topCut:\"cut\",topDoubleClick:\"dblclick\",topDrag:\"drag\",topDragEnd:\"dragend\",topDragEnter:\"dragenter\",topDragExit:\"dragexit\",topDragLeave:\"dragleave\",topDragOver:\"dragover\",topDragStart:\"dragstart\",topDrop:\"drop\",topDurationChange:\"durationchange\",topEmptied:\"emptied\",topEncrypted:\"encrypted\",topEnded:\"ended\",topError:\"error\",topFocus:\"focus\",topInput:\"input\",topKeyDown:\"keydown\",topKeyPress:\"keypress\",topKeyUp:\"keyup\",topLoadedData:\"loadeddata\",topLoadedMetadata:\"loadedmetadata\",topLoadStart:\"loadstart\",topMouseDown:\"mousedown\",topMouseMove:\"mousemove\",topMouseOut:\"mouseout\",topMouseOver:\"mouseover\",topMouseUp:\"mouseup\",topPaste:\"paste\",topPause:\"pause\",topPlay:\"play\",topPlaying:\"playing\",topProgress:\"progress\",topRateChange:\"ratechange\",topScroll:\"scroll\",topSeeked:\"seeked\",topSeeking:\"seeking\",topSelectionChange:\"selectionchange\",topStalled:\"stalled\",topSuspend:\"suspend\",topTextInput:\"textInput\",topTimeUpdate:\"timeupdate\",topTouchCancel:\"touchcancel\",topTouchEnd:\"touchend\",topTouchMove:\"touchmove\",topTouchStart:\"touchstart\",topTransitionEnd:c(\"transitionend\")||\"transitionend\",topVolumeChange:\"volumechange\",topWaiting:\"waiting\",topWheel:\"wheel\"},f=\"_reactListenersID\"+String(Math.random()).slice(2),m=i({},s,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(m.handleTopLevel),m.ReactEventListener=e}},setEnabled:function(e){m.ReactEventListener&&m.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!m.ReactEventListener||!m.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,o=r(n),i=a.registrationNameDependencies[e],s=0;s<i.length;s++){var l=i[s];o.hasOwnProperty(l)&&o[l]||(\"topWheel\"===l?u(\"wheel\")?m.ReactEventListener.trapBubbledEvent(\"topWheel\",\"wheel\",n):u(\"mousewheel\")?m.ReactEventListener.trapBubbledEvent(\"topWheel\",\"mousewheel\",n):m.ReactEventListener.trapBubbledEvent(\"topWheel\",\"DOMMouseScroll\",n):\"topScroll\"===l?u(\"scroll\",!0)?m.ReactEventListener.trapCapturedEvent(\"topScroll\",\"scroll\",n):m.ReactEventListener.trapBubbledEvent(\"topScroll\",\"scroll\",m.ReactEventListener.WINDOW_HANDLE):\"topFocus\"===l||\"topBlur\"===l?(u(\"focus\",!0)?(m.ReactEventListener.trapCapturedEvent(\"topFocus\",\"focus\",n),m.ReactEventListener.trapCapturedEvent(\"topBlur\",\"blur\",n)):u(\"focusin\")&&(m.ReactEventListener.trapBubbledEvent(\"topFocus\",\"focusin\",n),m.ReactEventListener.trapBubbledEvent(\"topBlur\",\"focusout\",n)),o.topBlur=!0,o.topFocus=!0):g.hasOwnProperty(l)&&m.ReactEventListener.trapBubbledEvent(l,g[l],n),o[l]=!0)}},trapBubbledEvent:function(e,t,n){return m.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return m.ReactEventListener.trapCapturedEvent(e,t,n)},supportsEventPageXY:function(){if(!document.createEvent)return!1;var e=document.createEvent(\"MouseEvent\");return null!=e&&\"pageX\"in e},ensureScrollValueMonitoring:function(){if(void 0===o&&(o=m.supportsEventPageXY()),!o&&!p){var e=l.refreshScrollValues;m.ReactEventListener.monitorScrollValue(e),p=!0}}});e.exports=m},function(e,t,n){\"use strict\";function r(e){o.enqueueEvents(e),o.processEventQueue(!1)}var o=n(67),i={handleTopLevel:function(e,t,n,i){var a=o.extractEvents(e,t,n,i);r(a)}};e.exports=i},function(e,t,n){\"use strict\";function r(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n[\"Webkit\"+e]=\"webkit\"+t,n[\"Moz\"+e]=\"moz\"+t,\nn[\"ms\"+e]=\"MS\"+t,n[\"O\"+e]=\"o\"+t.toLowerCase(),n}function o(e){if(s[e])return s[e];if(!a[e])return e;var t=a[e];for(var n in t)if(t.hasOwnProperty(n)&&n in l)return s[e]=t[n];return\"\"}var i=n(73),a={animationend:r(\"Animation\",\"AnimationEnd\"),animationiteration:r(\"Animation\",\"AnimationIteration\"),animationstart:r(\"Animation\",\"AnimationStart\"),transitionend:r(\"Transition\",\"TransitionEnd\")},s={},l={};i.canUseDOM&&(l=document.createElement(\"div\").style,\"AnimationEvent\"in window||(delete a.animationend.animation,delete a.animationiteration.animation,delete a.animationstart.animation),\"TransitionEvent\"in window||delete a.transitionend.transition),e.exports=o},function(e,t,n){\"use strict\";function r(){this._rootNodeID&&p.updateWrapper(this)}function o(e){var t=\"checkbox\"===e.type||\"radio\"===e.type;return t?null!=e.checked:null!=e.value}function i(e){var t=this._currentElement.props,n=c.executeOnChange(t,e);d.asap(r,this);var o=t.name;if(\"radio\"===t.type&&null!=o){for(var i=u.getNodeFromInstance(this),s=i;s.parentNode;)s=s.parentNode;for(var l=s.querySelectorAll(\"input[name=\"+JSON.stringify(\"\"+o)+'][type=\"radio\"]'),p=0;p<l.length;p++){var h=l[p];if(h!==i&&h.form===i.form){var g=u.getInstanceFromNode(h);g?void 0:a(\"90\"),d.asap(r,g)}}}return n}var a=n(60),s=n(26),l=n(124),c=n(130),u=n(59),d=n(81),p=(n(34),n(30),{getHostProps:function(e,t){var n=c.getValue(t),r=c.getChecked(t),o=s({type:void 0,step:void 0,min:void 0,max:void 0},t,{defaultChecked:void 0,defaultValue:void 0,value:null!=n?n:e._wrapperState.initialValue,checked:null!=r?r:e._wrapperState.initialChecked,onChange:e._wrapperState.onChange});return o},mountWrapper:function(e,t){var n=t.defaultValue;e._wrapperState={initialChecked:null!=t.checked?t.checked:t.defaultChecked,initialValue:null!=t.value?t.value:n,listeners:null,onChange:i.bind(e),controlled:o(t)}},updateWrapper:function(e){var t=e._currentElement.props,n=t.checked;null!=n&&l.setValueForProperty(u.getNodeFromInstance(e),\"checked\",n||!1);var r=u.getNodeFromInstance(e),o=c.getValue(t);if(null!=o)if(0===o&&\"\"===r.value)r.value=\"0\";else if(\"number\"===t.type){var i=parseFloat(r.value,10)||0;(o!=i||o==i&&r.value!=o)&&(r.value=\"\"+o)}else r.value!==\"\"+o&&(r.value=\"\"+o);else null==t.value&&null!=t.defaultValue&&r.defaultValue!==\"\"+t.defaultValue&&(r.defaultValue=\"\"+t.defaultValue),null==t.checked&&null!=t.defaultChecked&&(r.defaultChecked=!!t.defaultChecked)},postMountWrapper:function(e){var t=e._currentElement.props,n=u.getNodeFromInstance(e);switch(t.type){case\"submit\":case\"reset\":break;case\"color\":case\"date\":case\"datetime\":case\"datetime-local\":case\"month\":case\"time\":case\"week\":n.value=\"\",n.value=n.defaultValue;break;default:n.value=n.value}var r=n.name;\"\"!==r&&(n.name=\"\"),n.defaultChecked=!n.defaultChecked,n.defaultChecked=!n.defaultChecked,\"\"!==r&&(n.name=r)}});e.exports=p},function(e,t,n){\"use strict\";function r(e){null!=e.checkedLink&&null!=e.valueLink?s(\"87\"):void 0}function o(e){r(e),null!=e.value||null!=e.onChange?s(\"88\"):void 0}function i(e){r(e),null!=e.checked||null!=e.onChange?s(\"89\"):void 0}function a(e){if(e){var t=e.getName();if(t)return\" Check the render method of `\"+t+\"`.\"}return\"\"}var s=n(60),l=n(131),c=n(46),u=n(25),d=c(u.isValidElement),p=(n(34),n(30),{button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0}),h={value:function(e,t,n){return!e[t]||p[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error(\"You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.\")},checked:function(e,t,n){return!e[t]||e.onChange||e.readOnly||e.disabled?null:new Error(\"You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.\")},onChange:d.func},g={},f={checkPropTypes:function(e,t,n){for(var r in h){if(h.hasOwnProperty(r))var o=h[r](t,r,e,\"prop\",null,l);if(o instanceof Error&&!(o.message in g)){g[o.message]=!0;a(n)}}},getValue:function(e){return e.valueLink?(o(e),e.valueLink.value):e.value},getChecked:function(e){return e.checkedLink?(i(e),e.checkedLink.value):e.checked},executeOnChange:function(e,t){return e.valueLink?(o(e),e.valueLink.requestChange(t.target.value)):e.checkedLink?(i(e),e.checkedLink.requestChange(t.target.checked)):e.onChange?e.onChange.call(void 0,t):void 0}};e.exports=f},function(e,t){\"use strict\";var n=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\";e.exports=n},function(e,t,n){\"use strict\";function r(e){var t=\"\";return i.Children.forEach(e,function(e){null!=e&&(\"string\"==typeof e||\"number\"==typeof e?t+=e:l||(l=!0))}),t}var o=n(26),i=n(25),a=n(59),s=n(133),l=(n(30),!1),c={mountWrapper:function(e,t,n){var o=null;if(null!=n){var i=n;\"optgroup\"===i._tag&&(i=i._hostParent),null!=i&&\"select\"===i._tag&&(o=s.getSelectValueContext(i))}var a=null;if(null!=o){var l;if(l=null!=t.value?t.value+\"\":r(t.children),a=!1,Array.isArray(o)){for(var c=0;c<o.length;c++)if(\"\"+o[c]===l){a=!0;break}}else a=\"\"+o===l}e._wrapperState={selected:a}},postMountWrapper:function(e){var t=e._currentElement.props;if(null!=t.value){var n=a.getNodeFromInstance(e);n.setAttribute(\"value\",t.value)}},getHostProps:function(e,t){var n=o({selected:void 0,children:void 0},t);null!=e._wrapperState.selected&&(n.selected=e._wrapperState.selected);var i=r(t.children);return i&&(n.children=i),n}};e.exports=c},function(e,t,n){\"use strict\";function r(){if(this._rootNodeID&&this._wrapperState.pendingUpdate){this._wrapperState.pendingUpdate=!1;var e=this._currentElement.props,t=s.getValue(e);null!=t&&o(this,Boolean(e.multiple),t)}}function o(e,t,n){var r,o,i=l.getNodeFromInstance(e).options;if(t){for(r={},o=0;o<n.length;o++)r[\"\"+n[o]]=!0;for(o=0;o<i.length;o++){var a=r.hasOwnProperty(i[o].value);i[o].selected!==a&&(i[o].selected=a)}}else{for(r=\"\"+n,o=0;o<i.length;o++)if(i[o].value===r)return void(i[o].selected=!0);i.length&&(i[0].selected=!0)}}function i(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return this._rootNodeID&&(this._wrapperState.pendingUpdate=!0),c.asap(r,this),n}var a=n(26),s=n(130),l=n(59),c=n(81),u=(n(30),!1),d={getHostProps:function(e,t){return a({},t,{onChange:e._wrapperState.onChange,value:void 0})},mountWrapper:function(e,t){var n=s.getValue(t);e._wrapperState={pendingUpdate:!1,initialValue:null!=n?n:t.defaultValue,listeners:null,onChange:i.bind(e),wasMultiple:Boolean(t.multiple)},void 0===t.value||void 0===t.defaultValue||u||(u=!0)},getSelectValueContext:function(e){return e._wrapperState.initialValue},postUpdateWrapper:function(e){var t=e._currentElement.props;e._wrapperState.initialValue=void 0;var n=e._wrapperState.wasMultiple;e._wrapperState.wasMultiple=Boolean(t.multiple);var r=s.getValue(t);null!=r?(e._wrapperState.pendingUpdate=!1,o(e,Boolean(t.multiple),r)):n!==Boolean(t.multiple)&&(null!=t.defaultValue?o(e,Boolean(t.multiple),t.defaultValue):o(e,Boolean(t.multiple),t.multiple?[]:\"\"))}};e.exports=d},function(e,t,n){\"use strict\";function r(){this._rootNodeID&&u.updateWrapper(this)}function o(e){var t=this._currentElement.props,n=s.executeOnChange(t,e);return c.asap(r,this),n}var i=n(60),a=n(26),s=n(130),l=n(59),c=n(81),u=(n(34),n(30),{getHostProps:function(e,t){null!=t.dangerouslySetInnerHTML?i(\"91\"):void 0;var n=a({},t,{value:void 0,defaultValue:void 0,children:\"\"+e._wrapperState.initialValue,onChange:e._wrapperState.onChange});return n},mountWrapper:function(e,t){var n=s.getValue(t),r=n;if(null==n){var a=t.defaultValue,l=t.children;null!=l&&(null!=a?i(\"92\"):void 0,Array.isArray(l)&&(l.length<=1?void 0:i(\"93\"),l=l[0]),a=\"\"+l),null==a&&(a=\"\"),r=a}e._wrapperState={initialValue:\"\"+r,listeners:null,onChange:o.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=l.getNodeFromInstance(e),r=s.getValue(t);if(null!=r){var o=\"\"+r;o!==n.value&&(n.value=o),null==t.defaultValue&&(n.defaultValue=o)}null!=t.defaultValue&&(n.defaultValue=t.defaultValue)},postMountWrapper:function(e){var t=l.getNodeFromInstance(e),n=t.textContent;n===e._wrapperState.initialValue&&(t.value=n)}});e.exports=u},function(e,t,n){\"use strict\";function r(e,t,n){return{type:\"INSERT_MARKUP\",content:e,fromIndex:null,fromNode:null,toIndex:n,afterNode:t}}function o(e,t,n){return{type:\"MOVE_EXISTING\",content:null,fromIndex:e._mountIndex,fromNode:p.getHostNode(e),toIndex:n,afterNode:t}}function i(e,t){return{type:\"REMOVE_NODE\",content:null,fromIndex:e._mountIndex,fromNode:t,toIndex:null,afterNode:null}}function a(e){return{type:\"SET_MARKUP\",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function s(e){return{type:\"TEXT_CONTENT\",content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function l(e,t){return t&&(e=e||[],e.push(t)),e}function c(e,t){d.processChildrenUpdates(e,t)}var u=n(60),d=n(136),p=(n(137),n(87),n(39),n(84)),h=n(138),g=(n(31),n(153)),f=(n(34),{Mixin:{_reconcilerInstantiateChildren:function(e,t,n){return h.instantiateChildren(e,t,n)},_reconcilerUpdateChildren:function(e,t,n,r,o,i){var a,s=0;return a=g(t,s),h.updateChildren(e,a,n,r,o,this,this._hostContainerInfo,i,s),a},mountChildren:function(e,t,n){var r=this._reconcilerInstantiateChildren(e,t,n);this._renderedChildren=r;var o=[],i=0;for(var a in r)if(r.hasOwnProperty(a)){var s=r[a],l=0,c=p.mountComponent(s,t,this,this._hostContainerInfo,n,l);s._mountIndex=i++,o.push(c)}return o},updateTextContent:function(e){var t=this._renderedChildren;h.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&u(\"118\");var r=[s(e)];c(this,r)},updateMarkup:function(e){var t=this._renderedChildren;h.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&u(\"118\");var r=[a(e)];c(this,r)},updateChildren:function(e,t,n){this._updateChildren(e,t,n)},_updateChildren:function(e,t,n){var r=this._renderedChildren,o={},i=[],a=this._reconcilerUpdateChildren(r,e,i,o,t,n);if(a||r){var s,u=null,d=0,h=0,g=0,f=null;for(s in a)if(a.hasOwnProperty(s)){var m=r&&r[s],A=a[s];m===A?(u=l(u,this.moveChild(m,f,d,h)),h=Math.max(m._mountIndex,h),m._mountIndex=d):(m&&(h=Math.max(m._mountIndex,h)),u=l(u,this._mountChildAtIndex(A,i[g],f,d,t,n)),g++),d++,f=p.getHostNode(A)}for(s in o)o.hasOwnProperty(s)&&(u=l(u,this._unmountChild(r[s],o[s])));u&&c(this,u),this._renderedChildren=a}},unmountChildren:function(e){var t=this._renderedChildren;h.unmountChildren(t,e),this._renderedChildren=null},moveChild:function(e,t,n,r){return e._mountIndex<r?o(e,t,n):void 0},createChild:function(e,t,n){return r(n,t,e._mountIndex)},removeChild:function(e,t){return i(e,t)},_mountChildAtIndex:function(e,t,n,r,o,i){return e._mountIndex=r,this.createChild(e,n,t)},_unmountChild:function(e,t){var n=this.removeChild(e,t);return e._mountIndex=null,n}}});e.exports=f},function(e,t,n){\"use strict\";var r=n(60),o=(n(34),!1),i={replaceNodeWithMarkup:null,processChildrenUpdates:null,injection:{injectEnvironment:function(e){o?r(\"104\"):void 0,i.replaceNodeWithMarkup=e.replaceNodeWithMarkup,i.processChildrenUpdates=e.processChildrenUpdates,o=!0}}};e.exports=i},function(e,t){\"use strict\";var n={remove:function(e){e._reactInternalInstance=void 0},get:function(e){return e._reactInternalInstance},has:function(e){return void 0!==e._reactInternalInstance},set:function(e,t){e._reactInternalInstance=t}};e.exports=n},function(e,t,n){(function(t){\"use strict\";function r(e,t,n,r){var o=void 0===e[n];null!=t&&o&&(e[n]=i(t,!0))}var o=n(84),i=n(140),a=(n(148),n(144)),s=n(149),l=(n(30),{instantiateChildren:function(e,t,n,o){if(null==e)return null;var i={};return s(e,r,i),i},updateChildren:function(e,t,n,r,s,l,c,u,d){if(t||e){var p,h;for(p in t)if(t.hasOwnProperty(p)){h=e&&e[p];var g=h&&h._currentElement,f=t[p];if(null!=h&&a(g,f))o.receiveComponent(h,f,s,u),t[p]=h;else{h&&(r[p]=o.getHostNode(h),o.unmountComponent(h,!1));var m=i(f,!0);t[p]=m;var A=o.mountComponent(m,s,l,c,u,d);n.push(A)}}for(p in e)!e.hasOwnProperty(p)||t&&t.hasOwnProperty(p)||(h=e[p],r[p]=o.getHostNode(h),o.unmountComponent(h,!1))}},unmountChildren:function(e,t){for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];o.unmountComponent(r,t)}}});e.exports=l}).call(t,n(139))},function(e,t){function n(){throw new Error(\"setTimeout has not been defined\")}function r(){throw new Error(\"clearTimeout has not been defined\")}function o(e){if(u===setTimeout)return setTimeout(e,0);if((u===n||!u)&&setTimeout)return u=setTimeout,setTimeout(e,0);try{return u(e,0)}catch(t){try{return u.call(null,e,0)}catch(t){return u.call(this,e,0)}}}function i(e){if(d===clearTimeout)return clearTimeout(e);if((d===r||!d)&&clearTimeout)return d=clearTimeout,clearTimeout(e);try{return d(e)}catch(t){try{return d.call(null,e)}catch(t){return d.call(this,e)}}}function a(){f&&h&&(f=!1,h.length?g=h.concat(g):m=-1,g.length&&s())}function s(){if(!f){var e=o(a);f=!0;for(var t=g.length;t;){for(h=g,g=[];++m<t;)h&&h[m].run();m=-1,t=g.length}h=null,f=!1,i(e)}}function l(e,t){this.fun=e,this.array=t}function c(){}var u,d,p=e.exports={};!function(){try{u=\"function\"==typeof setTimeout?setTimeout:n}catch(e){u=n}try{d=\"function\"==typeof clearTimeout?clearTimeout:r}catch(e){d=r}}();var h,g=[],f=!1,m=-1;p.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];g.push(new l(e,t)),1!==g.length||f||o(s)},l.prototype.run=function(){this.fun.apply(null,this.array)},p.title=\"browser\",p.browser=!0,p.env={},p.argv=[],p.version=\"\",p.versions={},p.on=c,p.addListener=c,p.once=c,p.off=c,p.removeListener=c,p.removeAllListeners=c,p.emit=c,p.prependListener=c,p.prependOnceListener=c,p.listeners=function(e){return[]},p.binding=function(e){throw new Error(\"process.binding is not supported\")},p.cwd=function(){return\"/\"},p.chdir=function(e){throw new Error(\"process.chdir is not supported\")},p.umask=function(){return 0}},function(e,t,n){\"use strict\";function r(e){if(e){var t=e.getName();if(t)return\" Check the render method of `\"+t+\"`.\"}return\"\"}function o(e){return\"function\"==typeof e&&\"undefined\"!=typeof e.prototype&&\"function\"==typeof e.prototype.mountComponent&&\"function\"==typeof e.prototype.receiveComponent}function i(e,t){var n;if(null===e||e===!1)n=c.create(i);else if(\"object\"==typeof e){var s=e,l=s.type;if(\"function\"!=typeof l&&\"string\"!=typeof l){var p=\"\";p+=r(s._owner),a(\"130\",null==l?l:typeof l,p)}\"string\"==typeof s.type?n=u.createInternalComponent(s):o(s.type)?(n=new s.type(s),n.getHostNode||(n.getHostNode=n.getNativeNode)):n=new d(s)}else\"string\"==typeof e||\"number\"==typeof e?n=u.createInstanceForText(e):a(\"131\",typeof e);return n._mountIndex=0,n._mountImage=null,n}var a=n(60),s=n(26),l=n(141),c=n(145),u=n(146),d=(n(147),n(34),n(30),function(e){this.construct(e)});s(d.prototype,l,{_instantiateReactComponent:i}),e.exports=i},function(e,t,n){\"use strict\";function r(e){}function o(e,t){}function i(e){return!(!e.prototype||!e.prototype.isReactComponent)}function a(e){return!(!e.prototype||!e.prototype.isPureReactComponent)}var s=n(60),l=n(26),c=n(25),u=n(136),d=n(39),p=n(70),h=n(137),g=(n(87),n(142)),f=n(84),m=n(33),A=(n(34),n(143)),M=n(144),w=(n(30),{ImpureClass:0,PureClass:1,StatelessFunctional:2});r.prototype.render=function(){var e=h.get(this)._currentElement.type,t=e(this.props,this.context,this.updater);return o(e,t),t};var v=1,b={construct:function(e){this._currentElement=e,this._rootNodeID=0,this._compositeType=null,this._instance=null,this._hostParent=null,this._hostContainerInfo=null,this._updateBatchNumber=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedNodeType=null,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null,this._calledComponentWillUnmount=!1},mountComponent:function(e,t,n,l){this._context=l,this._mountOrder=v++,this._hostParent=t,this._hostContainerInfo=n;var u,d=this._currentElement.props,p=this._processContext(l),g=this._currentElement.type,f=e.getUpdateQueue(),A=i(g),M=this._constructComponent(A,d,p,f);A||null!=M&&null!=M.render?a(g)?this._compositeType=w.PureClass:this._compositeType=w.ImpureClass:(u=M,o(g,u),null===M||M===!1||c.isValidElement(M)?void 0:s(\"105\",g.displayName||g.name||\"Component\"),M=new r(g),this._compositeType=w.StatelessFunctional);M.props=d,M.context=p,M.refs=m,M.updater=f,this._instance=M,h.set(M,this);var b=M.state;void 0===b&&(M.state=b=null),\"object\"!=typeof b||Array.isArray(b)?s(\"106\",this.getName()||\"ReactCompositeComponent\"):void 0,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var y;return y=M.unstable_handleError?this.performInitialMountWithErrorHandling(u,t,n,e,l):this.performInitialMount(u,t,n,e,l),M.componentDidMount&&e.getReactMountReady().enqueue(M.componentDidMount,M),y},_constructComponent:function(e,t,n,r){return this._constructComponentWithoutOwner(e,t,n,r)},_constructComponentWithoutOwner:function(e,t,n,r){var o=this._currentElement.type;return e?new o(t,n,r):o(t,n,r)},performInitialMountWithErrorHandling:function(e,t,n,r,o){var i,a=r.checkpoint();try{i=this.performInitialMount(e,t,n,r,o)}catch(s){r.rollback(a),this._instance.unstable_handleError(s),this._pendingStateQueue&&(this._instance.state=this._processPendingState(this._instance.props,this._instance.context)),a=r.checkpoint(),this._renderedComponent.unmountComponent(!0),r.rollback(a),i=this.performInitialMount(e,t,n,r,o)}return i},performInitialMount:function(e,t,n,r,o){var i=this._instance,a=0;i.componentWillMount&&(i.componentWillMount(),this._pendingStateQueue&&(i.state=this._processPendingState(i.props,i.context))),void 0===e&&(e=this._renderValidatedComponent());var s=g.getType(e);this._renderedNodeType=s;var l=this._instantiateReactComponent(e,s!==g.EMPTY);this._renderedComponent=l;var c=f.mountComponent(l,r,t,n,this._processChildContext(o),a);return c},getHostNode:function(){return f.getHostNode(this._renderedComponent)},unmountComponent:function(e){if(this._renderedComponent){var t=this._instance;if(t.componentWillUnmount&&!t._calledComponentWillUnmount)if(t._calledComponentWillUnmount=!0,e){var n=this.getName()+\".componentWillUnmount()\";p.invokeGuardedCallback(n,t.componentWillUnmount.bind(t))}else t.componentWillUnmount();this._renderedComponent&&(f.unmountComponent(this._renderedComponent,e),this._renderedNodeType=null,this._renderedComponent=null,this._instance=null),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=0,this._topLevelWrapper=null,h.remove(t)}},_maskContext:function(e){var t=this._currentElement.type,n=t.contextTypes;if(!n)return m;var r={};for(var o in n)r[o]=e[o];return r},_processContext:function(e){var t=this._maskContext(e);return t},_processChildContext:function(e){var t,n=this._currentElement.type,r=this._instance;if(r.getChildContext&&(t=r.getChildContext()),t){\"object\"!=typeof n.childContextTypes?s(\"107\",this.getName()||\"ReactCompositeComponent\"):void 0;for(var o in t)o in n.childContextTypes?void 0:s(\"108\",this.getName()||\"ReactCompositeComponent\",o);return l({},e,t)}return e},_checkContextTypes:function(e,t,n){},receiveComponent:function(e,t,n){var r=this._currentElement,o=this._context;this._pendingElement=null,this.updateComponent(t,r,e,o,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement?f.receiveComponent(this,this._pendingElement,e,this._context):null!==this._pendingStateQueue||this._pendingForceUpdate?this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context):this._updateBatchNumber=null},updateComponent:function(e,t,n,r,o){var i=this._instance;null==i?s(\"136\",this.getName()||\"ReactCompositeComponent\"):void 0;var a,l=!1;this._context===o?a=i.context:(a=this._processContext(o),l=!0);var c=t.props,u=n.props;t!==n&&(l=!0),l&&i.componentWillReceiveProps&&i.componentWillReceiveProps(u,a);var d=this._processPendingState(u,a),p=!0;this._pendingForceUpdate||(i.shouldComponentUpdate?p=i.shouldComponentUpdate(u,d,a):this._compositeType===w.PureClass&&(p=!A(c,u)||!A(i.state,d))),this._updateBatchNumber=null,p?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,u,d,a,e,o)):(this._currentElement=n,this._context=o,i.props=u,i.state=d,i.context=a)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,o=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(o&&1===r.length)return r[0];for(var i=l({},o?r[0]:n.state),a=o?1:0;a<r.length;a++){var s=r[a];l(i,\"function\"==typeof s?s.call(n,i,e,t):s)}return i},_performComponentUpdate:function(e,t,n,r,o,i){var a,s,l,c=this._instance,u=Boolean(c.componentDidUpdate);u&&(a=c.props,s=c.state,l=c.context),c.componentWillUpdate&&c.componentWillUpdate(t,n,r),this._currentElement=e,this._context=i,c.props=t,c.state=n,c.context=r,this._updateRenderedComponent(o,i),u&&o.getReactMountReady().enqueue(c.componentDidUpdate.bind(c,a,s,l),c)},_updateRenderedComponent:function(e,t){var n=this._renderedComponent,r=n._currentElement,o=this._renderValidatedComponent(),i=0;if(M(r,o))f.receiveComponent(n,o,e,this._processChildContext(t));else{var a=f.getHostNode(n);f.unmountComponent(n,!1);var s=g.getType(o);this._renderedNodeType=s;var l=this._instantiateReactComponent(o,s!==g.EMPTY);this._renderedComponent=l;var c=f.mountComponent(l,e,this._hostParent,this._hostContainerInfo,this._processChildContext(t),i);this._replaceNodeWithMarkup(a,c,n)}},_replaceNodeWithMarkup:function(e,t,n){u.replaceNodeWithMarkup(e,t,n)},_renderValidatedComponentWithoutOwnerOrContext:function(){var e,t=this._instance;return e=t.render()},_renderValidatedComponent:function(){var e;if(this._compositeType!==w.StatelessFunctional){d.current=this;try{e=this._renderValidatedComponentWithoutOwnerOrContext()}finally{d.current=null}}else e=this._renderValidatedComponentWithoutOwnerOrContext();return null===e||e===!1||c.isValidElement(e)?void 0:s(\"109\",this.getName()||\"ReactCompositeComponent\"),e},attachRef:function(e,t){var n=this.getPublicInstance();null==n?s(\"110\"):void 0;var r=t.getPublicInstance(),o=n.refs===m?n.refs={}:n.refs;o[e]=r},detachRef:function(e){var t=this.getPublicInstance().refs;delete t[e]},getName:function(){var e=this._currentElement.type,t=this._instance&&this._instance.constructor;return e.displayName||t&&t.displayName||e.name||t&&t.name||null},getPublicInstance:function(){var e=this._instance;return this._compositeType===w.StatelessFunctional?null:e},_instantiateReactComponent:null};e.exports=b},function(e,t,n){\"use strict\";var r=n(60),o=n(25),i=(n(34),{HOST:0,COMPOSITE:1,EMPTY:2,getType:function(e){return null===e||e===!1?i.EMPTY:o.isValidElement(e)?\"function\"==typeof e.type?i.COMPOSITE:i.HOST:void r(\"26\",e)}});e.exports=i},function(e,t){\"use strict\";function n(e,t){return e===t?0!==e||0!==t||1/e===1/t:e!==e&&t!==t}function r(e,t){if(n(e,t))return!0;if(\"object\"!=typeof e||null===e||\"object\"!=typeof t||null===t)return!1;var r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;for(var a=0;a<r.length;a++)if(!o.call(t,r[a])||!n(e[r[a]],t[r[a]]))return!1;return!0}var o=Object.prototype.hasOwnProperty;e.exports=r},function(e,t){\"use strict\";function n(e,t){var n=null===e||e===!1,r=null===t||t===!1;if(n||r)return n===r;var o=typeof e,i=typeof t;return\"string\"===o||\"number\"===o?\"string\"===i||\"number\"===i:\"object\"===i&&e.type===t.type&&e.key===t.key}e.exports=n},function(e,t){\"use strict\";var n,r={injectEmptyComponentFactory:function(e){n=e}},o={create:function(e){return n(e)}};o.injection=r,e.exports=o},function(e,t,n){\"use strict\";function r(e){return s?void 0:a(\"111\",e.type),new s(e)}function o(e){return new l(e)}function i(e){return e instanceof l}var a=n(60),s=(n(34),null),l=null,c={injectGenericComponentClass:function(e){s=e},injectTextComponentClass:function(e){l=e}},u={createInternalComponent:r,createInstanceForText:o,isTextComponent:i,injection:c};e.exports=u},function(e,t){\"use strict\";function n(){return r++}var r=1;e.exports=n},function(e,t){\"use strict\";function n(e){var t=/[=:]/g,n={\"=\":\"=0\",\":\":\"=2\"},r=(\"\"+e).replace(t,function(e){return n[e]});return\"$\"+r}function r(e){var t=/(=0|=2)/g,n={\"=0\":\"=\",\"=2\":\":\"},r=\".\"===e[0]&&\"$\"===e[1]?e.substring(2):e.substring(1);return(\"\"+r).replace(t,function(e){return n[e]})}var o={escape:n,unescape:r};e.exports=o},function(e,t,n){\"use strict\";function r(e,t){return e&&\"object\"==typeof e&&null!=e.key?c.escape(e.key):t.toString(36)}function o(e,t,n,i){var p=typeof e;if((\"undefined\"===p||\"boolean\"===p)&&(e=null),null===e||\"string\"===p||\"number\"===p||\"object\"===p&&e.$$typeof===s)return n(i,e,\"\"===t?u+r(e,0):t),1;var h,g,f=0,m=\"\"===t?u:t+d;if(Array.isArray(e))for(var A=0;A<e.length;A++)h=e[A],g=m+r(h,A),f+=o(h,g,n,i);else{var M=l(e);if(M){var w,v=M.call(e);if(M!==e.entries)for(var b=0;!(w=v.next()).done;)h=w.value,g=m+r(h,b++),f+=o(h,g,n,i);else for(;!(w=v.next()).done;){var y=w.value;y&&(h=y[1],g=m+c.escape(y[0])+d+r(h,0),f+=o(h,g,n,i))}}else if(\"object\"===p){var x=\"\",T=String(e);a(\"31\",\"[object Object]\"===T?\"object with keys {\"+Object.keys(e).join(\", \")+\"}\":T,x)}}return f}function i(e,t,n){return null==e?0:o(e,\"\",t,n)}var a=n(60),s=(n(39),n(150)),l=n(151),c=(n(34),n(148)),u=(n(30),\".\"),d=\":\";e.exports=i},function(e,t){\"use strict\";var n=\"function\"==typeof Symbol&&Symbol[\"for\"]&&Symbol[\"for\"](\"react.element\")||60103;e.exports=n},function(e,t){\"use strict\";function n(e){var t=e&&(r&&e[r]||e[o]);return\"function\"==typeof t?t:void 0}var r=\"function\"==typeof Symbol&&Symbol.iterator,o=\"@@iterator\";e.exports=n},function(e,t,n){\"use strict\";function r(e){var t=Function.prototype.toString,n=Object.prototype.hasOwnProperty,r=RegExp(\"^\"+t.call(n).replace(/[\\\\^$.*+?()[\\]{}|]/g,\"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g,\"$1.*?\")+\"$\");try{var o=t.call(e);return r.test(o)}catch(i){return!1}}function o(e){var t=c(e);if(t){var n=t.childIDs;u(e),n.forEach(o)}}function i(e,t,n){return\"\\n    in \"+(e||\"Unknown\")+(t?\" (at \"+t.fileName.replace(/^.*[\\\\\\/]/,\"\")+\":\"+t.lineNumber+\")\":n?\" (created by \"+n+\")\":\"\")}function a(e){return null==e?\"#empty\":\"string\"==typeof e||\"number\"==typeof e?\"#text\":\"string\"==typeof e.type?e.type:e.type.displayName||e.type.name||\"Unknown\"}function s(e){var t,n=C.getDisplayName(e),r=C.getElement(e),o=C.getOwnerID(e);return o&&(t=C.getDisplayName(o)),i(n,r&&r._source,t)}var l,c,u,d,p,h,g,f=n(28),m=n(39),A=(n(34),n(30),\"function\"==typeof Array.from&&\"function\"==typeof Map&&r(Map)&&null!=Map.prototype&&\"function\"==typeof Map.prototype.keys&&r(Map.prototype.keys)&&\"function\"==typeof Set&&r(Set)&&null!=Set.prototype&&\"function\"==typeof Set.prototype.keys&&r(Set.prototype.keys));if(A){var M=new Map,w=new Set;l=function(e,t){M.set(e,t)},c=function(e){return M.get(e)},u=function(e){M[\"delete\"](e)},d=function(){return Array.from(M.keys())},p=function(e){w.add(e)},h=function(e){w[\"delete\"](e)},g=function(){return Array.from(w.keys())}}else{var v={},b={},y=function(e){return\".\"+e},x=function(e){return parseInt(e.substr(1),10)};l=function(e,t){var n=y(e);v[n]=t},c=function(e){var t=y(e);return v[t]},u=function(e){var t=y(e);delete v[t]},d=function(){return Object.keys(v).map(x)},p=function(e){var t=y(e);b[t]=!0},h=function(e){var t=y(e);delete b[t]},g=function(){return Object.keys(b).map(x)}}var T=[],C={onSetChildren:function(e,t){var n=c(e);n?void 0:f(\"144\"),n.childIDs=t;for(var r=0;r<t.length;r++){var o=t[r],i=c(o);i?void 0:f(\"140\"),null==i.childIDs&&\"object\"==typeof i.element&&null!=i.element?f(\"141\"):void 0,i.isMounted?void 0:f(\"71\"),null==i.parentID&&(i.parentID=e),i.parentID!==e?f(\"142\",o,i.parentID,e):void 0}},onBeforeMountComponent:function(e,t,n){var r={element:t,parentID:n,text:null,childIDs:[],isMounted:!1,updateCount:0};l(e,r)},onBeforeUpdateComponent:function(e,t){var n=c(e);n&&n.isMounted&&(n.element=t)},onMountComponent:function(e){var t=c(e);t?void 0:f(\"144\"),t.isMounted=!0;var n=0===t.parentID;n&&p(e)},onUpdateComponent:function(e){var t=c(e);t&&t.isMounted&&t.updateCount++},onUnmountComponent:function(e){var t=c(e);if(t){t.isMounted=!1;var n=0===t.parentID;n&&h(e)}T.push(e)},purgeUnmountedComponents:function(){if(!C._preventPurging){for(var e=0;e<T.length;e++){var t=T[e];o(t)}T.length=0}},isMounted:function(e){var t=c(e);return t?t.isMounted:!1},getCurrentStackAddendum:function(e){var t=\"\";if(e){var n=a(e),r=e._owner;t+=i(n,e._source,r&&r.getName())}var o=m.current,s=o&&o._debugID;return t+=C.getStackAddendumByID(s)},getStackAddendumByID:function(e){for(var t=\"\";e;)t+=s(e),e=C.getParentID(e);return t},getChildIDs:function(e){var t=c(e);return t?t.childIDs:[]},getDisplayName:function(e){var t=C.getElement(e);return t?a(t):null},getElement:function(e){var t=c(e);return t?t.element:null},getOwnerID:function(e){var t=C.getElement(e);return t&&t._owner?t._owner._debugID:null},getParentID:function(e){var t=c(e);return t?t.parentID:null},getSource:function(e){var t=c(e),n=t?t.element:null,r=null!=n?n._source:null;return r},getText:function(e){var t=C.getElement(e);return\"string\"==typeof t?t:\"number\"==typeof t?\"\"+t:null},getUpdateCount:function(e){var t=c(e);return t?t.updateCount:0},getRootIDs:g,getRegisteredIDs:d,pushNonStandardWarningStack:function(e,t){if(\"function\"==typeof console.reactStack){var n=[],r=m.current,o=r&&r._debugID;try{for(e&&n.push({name:o?C.getDisplayName(o):null,fileName:t?t.fileName:null,lineNumber:t?t.lineNumber:null});o;){var i=C.getElement(o),a=C.getParentID(o),s=C.getOwnerID(o),l=s?C.getDisplayName(s):null,c=i&&i._source;n.push({name:l,fileName:c?c.fileName:null,lineNumber:c?c.lineNumber:null}),o=a}}catch(u){}console.reactStack(n)}},popNonStandardWarningStack:function(){\"function\"==typeof console.reactStackEnd&&console.reactStackEnd()}};e.exports=C},function(e,t,n){(function(t){\"use strict\";function r(e,t,n,r){if(e&&\"object\"==typeof e){var o=e,i=void 0===o[n];i&&null!=t&&(o[n]=t)}}function o(e,t){if(null==e)return e;var n={};return i(e,r,n),n}var i=(n(148),n(149));n(30);e.exports=o}).call(t,n(139))},function(e,t,n){\"use strict\";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=e,this.useCreateElement=!1,this.updateQueue=new s(this)}var o=n(26),i=n(75),a=n(88),s=(n(87),n(155)),l=[],c={enqueue:function(){}},u={getTransactionWrappers:function(){return l},getReactMountReady:function(){return c},getUpdateQueue:function(){return this.updateQueue},destructor:function(){},checkpoint:function(){},rollback:function(){}};o(r.prototype,a,u),i.addPoolingTo(r),e.exports=r},function(e,t,n){\"use strict\";function r(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}function o(e,t){}var i=n(156),a=(n(30),function(){function e(t){r(this,e),this.transaction=t}return e.prototype.isMounted=function(e){return!1},e.prototype.enqueueCallback=function(e,t,n){this.transaction.isInTransaction()&&i.enqueueCallback(e,t,n)},e.prototype.enqueueForceUpdate=function(e){this.transaction.isInTransaction()?i.enqueueForceUpdate(e):o(e,\"forceUpdate\")},e.prototype.enqueueReplaceState=function(e,t){this.transaction.isInTransaction()?i.enqueueReplaceState(e,t):o(e,\"replaceState\")},e.prototype.enqueueSetState=function(e,t){this.transaction.isInTransaction()?i.enqueueSetState(e,t):o(e,\"setState\")},e}());e.exports=a},function(e,t,n){\"use strict\";function r(e){l.enqueueUpdate(e)}function o(e){var t=typeof e;if(\"object\"!==t)return t;var n=e.constructor&&e.constructor.name||t,r=Object.keys(e);return r.length>0&&r.length<20?n+\" (keys: \"+r.join(\", \")+\")\":n}function i(e,t){var n=s.get(e);if(!n){return null}return n}var a=n(60),s=(n(39),n(137)),l=(n(87),n(81)),c=(n(34),n(30),{isMounted:function(e){var t=s.get(e);return t?!!t._renderedComponent:!1},enqueueCallback:function(e,t,n){c.validateCallback(t,n);var o=i(e);return o?(o._pendingCallbacks?o._pendingCallbacks.push(t):o._pendingCallbacks=[t],void r(o)):null},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],\nr(e)},enqueueForceUpdate:function(e){var t=i(e,\"forceUpdate\");t&&(t._pendingForceUpdate=!0,r(t))},enqueueReplaceState:function(e,t,n){var o=i(e,\"replaceState\");o&&(o._pendingStateQueue=[t],o._pendingReplaceState=!0,void 0!==n&&null!==n&&(c.validateCallback(n,\"replaceState\"),o._pendingCallbacks?o._pendingCallbacks.push(n):o._pendingCallbacks=[n]),r(o))},enqueueSetState:function(e,t){var n=i(e,\"setState\");if(n){var o=n._pendingStateQueue||(n._pendingStateQueue=[]);o.push(t),r(n)}},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,r(e)},validateCallback:function(e,t){e&&\"function\"!=typeof e?a(\"122\",t,o(e)):void 0}});e.exports=c},function(e,t,n){\"use strict\";var r=(n(26),n(31)),o=(n(30),r);e.exports=o},function(e,t,n){\"use strict\";var r=n(26),o=n(102),i=n(59),a=function(e){this._currentElement=null,this._hostNode=null,this._hostParent=null,this._hostContainerInfo=null,this._domID=0};r(a.prototype,{mountComponent:function(e,t,n,r){var a=n._idCounter++;this._domID=a,this._hostParent=t,this._hostContainerInfo=n;var s=\" react-empty: \"+this._domID+\" \";if(e.useCreateElement){var l=n._ownerDocument,c=l.createComment(s);return i.precacheNode(this,c),o(c)}return e.renderToStaticMarkup?\"\":\"<!--\"+s+\"-->\"},receiveComponent:function(){},getHostNode:function(){return i.getNodeFromInstance(this)},unmountComponent:function(){i.uncacheNode(this)}}),e.exports=a},function(e,t,n){\"use strict\";function r(e,t){\"_hostNode\"in e?void 0:l(\"33\"),\"_hostNode\"in t?void 0:l(\"33\");for(var n=0,r=e;r;r=r._hostParent)n++;for(var o=0,i=t;i;i=i._hostParent)o++;for(;n-o>0;)e=e._hostParent,n--;for(;o-n>0;)t=t._hostParent,o--;for(var a=n;a--;){if(e===t)return e;e=e._hostParent,t=t._hostParent}return null}function o(e,t){\"_hostNode\"in e?void 0:l(\"35\"),\"_hostNode\"in t?void 0:l(\"35\");for(;t;){if(t===e)return!0;t=t._hostParent}return!1}function i(e){return\"_hostNode\"in e?void 0:l(\"36\"),e._hostParent}function a(e,t,n){for(var r=[];e;)r.push(e),e=e._hostParent;var o;for(o=r.length;o-->0;)t(r[o],\"captured\",n);for(o=0;o<r.length;o++)t(r[o],\"bubbled\",n)}function s(e,t,n,o,i){for(var a=e&&t?r(e,t):null,s=[];e&&e!==a;)s.push(e),e=e._hostParent;for(var l=[];t&&t!==a;)l.push(t),t=t._hostParent;var c;for(c=0;c<s.length;c++)n(s[c],\"bubbled\",o);for(c=l.length;c-->0;)n(l[c],\"captured\",i)}var l=n(60);n(34);e.exports={isAncestor:o,getLowestCommonAncestor:r,getParentInstance:i,traverseTwoPhase:a,traverseEnterLeave:s}},function(e,t,n){\"use strict\";var r=n(60),o=n(26),i=n(101),a=n(102),s=n(59),l=n(107),c=(n(34),n(157),function(e){this._currentElement=e,this._stringText=\"\"+e,this._hostNode=null,this._hostParent=null,this._domID=0,this._mountIndex=0,this._closingComment=null,this._commentNodes=null});o(c.prototype,{mountComponent:function(e,t,n,r){var o=n._idCounter++,i=\" react-text: \"+o+\" \",c=\" /react-text \";if(this._domID=o,this._hostParent=t,e.useCreateElement){var u=n._ownerDocument,d=u.createComment(i),p=u.createComment(c),h=a(u.createDocumentFragment());return a.queueChild(h,a(d)),this._stringText&&a.queueChild(h,a(u.createTextNode(this._stringText))),a.queueChild(h,a(p)),s.precacheNode(this,d),this._closingComment=p,h}var g=l(this._stringText);return e.renderToStaticMarkup?g:\"<!--\"+i+\"-->\"+g+\"<!--\"+c+\"-->\"},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=\"\"+e;if(n!==this._stringText){this._stringText=n;var r=this.getHostNode();i.replaceDelimitedText(r[0],r[1],n)}}},getHostNode:function(){var e=this._commentNodes;if(e)return e;if(!this._closingComment)for(var t=s.getNodeFromInstance(this),n=t.nextSibling;;){if(null==n?r(\"67\",this._domID):void 0,8===n.nodeType&&\" /react-text \"===n.nodeValue){this._closingComment=n;break}n=n.nextSibling}return e=[this._hostNode,this._closingComment],this._commentNodes=e,e},unmountComponent:function(){this._closingComment=null,this._commentNodes=null,s.uncacheNode(this)}}),e.exports=c},function(e,t,n){\"use strict\";function r(){this.reinitializeTransaction()}var o=n(26),i=n(81),a=n(88),s=n(31),l={initialize:s,close:function(){p.isBatchingUpdates=!1}},c={initialize:s,close:i.flushBatchedUpdates.bind(i)},u=[c,l];o(r.prototype,a,{getTransactionWrappers:function(){return u}});var d=new r,p={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,o,i){var a=p.isBatchingUpdates;return p.isBatchingUpdates=!0,a?e(t,n,r,o,i):d.perform(e,null,t,n,r,o,i)}};e.exports=p},function(e,t,n){\"use strict\";function r(e){for(;e._hostParent;)e=e._hostParent;var t=d.getNodeFromInstance(e),n=t.parentNode;return d.getClosestInstanceFromNode(n)}function o(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function i(e){var t=h(e.nativeEvent),n=d.getClosestInstanceFromNode(t),o=n;do e.ancestors.push(o),o=o&&r(o);while(o);for(var i=0;i<e.ancestors.length;i++)n=e.ancestors[i],f._handleTopLevel(e.topLevelType,n,e.nativeEvent,h(e.nativeEvent))}function a(e){var t=g(window);e(t)}var s=n(26),l=n(163),c=n(73),u=n(75),d=n(59),p=n(81),h=n(90),g=n(164);s(o.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),u.addPoolingTo(o,u.twoArgumentPooler);var f={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:c.canUseDOM?window:null,setHandleTopLevel:function(e){f._handleTopLevel=e},setEnabled:function(e){f._enabled=!!e},isEnabled:function(){return f._enabled},trapBubbledEvent:function(e,t,n){return n?l.listen(n,t,f.dispatchEvent.bind(null,e)):null},trapCapturedEvent:function(e,t,n){return n?l.capture(n,t,f.dispatchEvent.bind(null,e)):null},monitorScrollValue:function(e){var t=a.bind(null,e);l.listen(window,\"scroll\",t)},dispatchEvent:function(e,t){if(f._enabled){var n=o.getPooled(e,t);try{p.batchedUpdates(i,n)}finally{o.release(n)}}}};e.exports=f},function(e,t,n){\"use strict\";var r=n(31),o={listen:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!1),{remove:function(){e.removeEventListener(t,n,!1)}}):e.attachEvent?(e.attachEvent(\"on\"+t,n),{remove:function(){e.detachEvent(\"on\"+t,n)}}):void 0},capture:function(e,t,n){return e.addEventListener?(e.addEventListener(t,n,!0),{remove:function(){e.removeEventListener(t,n,!0)}}):{remove:r}},registerDefault:function(){}};e.exports=o},function(e,t){\"use strict\";function n(e){return e.Window&&e instanceof e.Window?{x:e.pageXOffset||e.document.documentElement.scrollLeft,y:e.pageYOffset||e.document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}e.exports=n},function(e,t,n){\"use strict\";var r=n(61),o=n(67),i=n(69),a=n(136),s=n(145),l=n(126),c=n(146),u=n(81),d={Component:a.injection,DOMProperty:r.injection,EmptyComponent:s.injection,EventPluginHub:o.injection,EventPluginUtils:i.injection,EventEmitter:l.injection,HostComponent:c.injection,Updates:u.injection};e.exports=d},function(e,t,n){\"use strict\";function r(e){this.reinitializeTransaction(),this.renderToStaticMarkup=!1,this.reactMountReady=i.getPooled(null),this.useCreateElement=e}var o=n(26),i=n(82),a=n(75),s=n(126),l=n(167),c=(n(87),n(88)),u=n(156),d={initialize:l.getSelectionInformation,close:l.restoreSelection},p={initialize:function(){var e=s.isEnabled();return s.setEnabled(!1),e},close:function(e){s.setEnabled(e)}},h={initialize:function(){this.reactMountReady.reset()},close:function(){this.reactMountReady.notifyAll()}},g=[d,p,h],f={getTransactionWrappers:function(){return g},getReactMountReady:function(){return this.reactMountReady},getUpdateQueue:function(){return u},checkpoint:function(){return this.reactMountReady.checkpoint()},rollback:function(e){this.reactMountReady.rollback(e)},destructor:function(){i.release(this.reactMountReady),this.reactMountReady=null}};o(r.prototype,c,f),a.addPoolingTo(r),e.exports=r},function(e,t,n){\"use strict\";function r(e){return i(document.documentElement,e)}var o=n(168),i=n(170),a=n(115),s=n(173),l={hasSelectionCapabilities:function(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(\"input\"===t&&\"text\"===e.type||\"textarea\"===t||\"true\"===e.contentEditable)},getSelectionInformation:function(){var e=s();return{focusedElem:e,selectionRange:l.hasSelectionCapabilities(e)?l.getSelection(e):null}},restoreSelection:function(e){var t=s(),n=e.focusedElem,o=e.selectionRange;t!==n&&r(n)&&(l.hasSelectionCapabilities(n)&&l.setSelection(n,o),a(n))},getSelection:function(e){var t;if(\"selectionStart\"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&e.nodeName&&\"input\"===e.nodeName.toLowerCase()){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart(\"character\",-e.value.length),end:-n.moveEnd(\"character\",-e.value.length)})}else t=o.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,r=t.end;if(void 0===r&&(r=n),\"selectionStart\"in e)e.selectionStart=n,e.selectionEnd=Math.min(r,e.value.length);else if(document.selection&&e.nodeName&&\"input\"===e.nodeName.toLowerCase()){var i=e.createTextRange();i.collapse(!0),i.moveStart(\"character\",n),i.moveEnd(\"character\",r-n),i.select()}else o.setOffsets(e,t)}};e.exports=l},function(e,t,n){\"use strict\";function r(e,t,n,r){return e===n&&t===r}function o(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint(\"EndToStart\",n);var i=o.text.length,a=i+r;return{start:i,end:a}}function i(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var n=t.anchorNode,o=t.anchorOffset,i=t.focusNode,a=t.focusOffset,s=t.getRangeAt(0);try{s.startContainer.nodeType,s.endContainer.nodeType}catch(l){return null}var c=r(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),u=c?0:s.toString().length,d=s.cloneRange();d.selectNodeContents(e),d.setEnd(s.startContainer,s.startOffset);var p=r(d.startContainer,d.startOffset,d.endContainer,d.endOffset),h=p?0:d.toString().length,g=h+u,f=document.createRange();f.setStart(n,o),f.setEnd(i,a);var m=f.collapsed;return{start:m?g:h,end:m?h:g}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();void 0===t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart(\"character\",n),o.setEndPoint(\"EndToStart\",o),o.moveEnd(\"character\",r-n),o.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[u()].length,o=Math.min(t.start,r),i=void 0===t.end?o:Math.min(t.end,r);if(!n.extend&&o>i){var a=i;i=o,o=a}var s=c(e,o),l=c(e,i);if(s&&l){var d=document.createRange();d.setStart(s.node,s.offset),n.removeAllRanges(),o>i?(n.addRange(d),n.extend(l.node,l.offset)):(d.setEnd(l.node,l.offset),n.addRange(d))}}}var l=n(73),c=n(169),u=n(76),d=l.canUseDOM&&\"selection\"in document&&!(\"getSelection\"in window),p={getOffsets:d?o:i,setOffsets:d?a:s};e.exports=p},function(e,t){\"use strict\";function n(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function r(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function o(e,t){for(var o=n(e),i=0,a=0;o;){if(3===o.nodeType){if(a=i+o.textContent.length,t>=i&&a>=t)return{node:o,offset:t-i};i=a}o=n(r(o))}}e.exports=o},function(e,t,n){\"use strict\";function r(e,t){return e&&t?e===t?!0:o(e)?!1:o(t)?r(e,t.parentNode):\"contains\"in e?e.contains(t):e.compareDocumentPosition?!!(16&e.compareDocumentPosition(t)):!1:!1}var o=n(171);e.exports=r},function(e,t,n){\"use strict\";function r(e){return o(e)&&3==e.nodeType}var o=n(172);e.exports=r},function(e,t){\"use strict\";function n(e){var t=e?e.ownerDocument||e:document,n=t.defaultView||window;return!(!e||!(\"function\"==typeof n.Node?e instanceof n.Node:\"object\"==typeof e&&\"number\"==typeof e.nodeType&&\"string\"==typeof e.nodeName))}e.exports=n},function(e,t){\"use strict\";function n(e){if(e=e||(\"undefined\"!=typeof document?document:void 0),\"undefined\"==typeof e)return null;try{return e.activeElement||e.body}catch(t){return e.body}}e.exports=n},function(e,t){\"use strict\";var n={xlink:\"http://www.w3.org/1999/xlink\",xml:\"http://www.w3.org/XML/1998/namespace\"},r={accentHeight:\"accent-height\",accumulate:0,additive:0,alignmentBaseline:\"alignment-baseline\",allowReorder:\"allowReorder\",alphabetic:0,amplitude:0,arabicForm:\"arabic-form\",ascent:0,attributeName:\"attributeName\",attributeType:\"attributeType\",autoReverse:\"autoReverse\",azimuth:0,baseFrequency:\"baseFrequency\",baseProfile:\"baseProfile\",baselineShift:\"baseline-shift\",bbox:0,begin:0,bias:0,by:0,calcMode:\"calcMode\",capHeight:\"cap-height\",clip:0,clipPath:\"clip-path\",clipRule:\"clip-rule\",clipPathUnits:\"clipPathUnits\",colorInterpolation:\"color-interpolation\",colorInterpolationFilters:\"color-interpolation-filters\",colorProfile:\"color-profile\",colorRendering:\"color-rendering\",contentScriptType:\"contentScriptType\",contentStyleType:\"contentStyleType\",cursor:0,cx:0,cy:0,d:0,decelerate:0,descent:0,diffuseConstant:\"diffuseConstant\",direction:0,display:0,divisor:0,dominantBaseline:\"dominant-baseline\",dur:0,dx:0,dy:0,edgeMode:\"edgeMode\",elevation:0,enableBackground:\"enable-background\",end:0,exponent:0,externalResourcesRequired:\"externalResourcesRequired\",fill:0,fillOpacity:\"fill-opacity\",fillRule:\"fill-rule\",filter:0,filterRes:\"filterRes\",filterUnits:\"filterUnits\",floodColor:\"flood-color\",floodOpacity:\"flood-opacity\",focusable:0,fontFamily:\"font-family\",fontSize:\"font-size\",fontSizeAdjust:\"font-size-adjust\",fontStretch:\"font-stretch\",fontStyle:\"font-style\",fontVariant:\"font-variant\",fontWeight:\"font-weight\",format:0,from:0,fx:0,fy:0,g1:0,g2:0,glyphName:\"glyph-name\",glyphOrientationHorizontal:\"glyph-orientation-horizontal\",glyphOrientationVertical:\"glyph-orientation-vertical\",glyphRef:\"glyphRef\",gradientTransform:\"gradientTransform\",gradientUnits:\"gradientUnits\",hanging:0,horizAdvX:\"horiz-adv-x\",horizOriginX:\"horiz-origin-x\",ideographic:0,imageRendering:\"image-rendering\",\"in\":0,in2:0,intercept:0,k:0,k1:0,k2:0,k3:0,k4:0,kernelMatrix:\"kernelMatrix\",kernelUnitLength:\"kernelUnitLength\",kerning:0,keyPoints:\"keyPoints\",keySplines:\"keySplines\",keyTimes:\"keyTimes\",lengthAdjust:\"lengthAdjust\",letterSpacing:\"letter-spacing\",lightingColor:\"lighting-color\",limitingConeAngle:\"limitingConeAngle\",local:0,markerEnd:\"marker-end\",markerMid:\"marker-mid\",markerStart:\"marker-start\",markerHeight:\"markerHeight\",markerUnits:\"markerUnits\",markerWidth:\"markerWidth\",mask:0,maskContentUnits:\"maskContentUnits\",maskUnits:\"maskUnits\",mathematical:0,mode:0,numOctaves:\"numOctaves\",offset:0,opacity:0,operator:0,order:0,orient:0,orientation:0,origin:0,overflow:0,overlinePosition:\"overline-position\",overlineThickness:\"overline-thickness\",paintOrder:\"paint-order\",panose1:\"panose-1\",pathLength:\"pathLength\",patternContentUnits:\"patternContentUnits\",patternTransform:\"patternTransform\",patternUnits:\"patternUnits\",pointerEvents:\"pointer-events\",points:0,pointsAtX:\"pointsAtX\",pointsAtY:\"pointsAtY\",pointsAtZ:\"pointsAtZ\",preserveAlpha:\"preserveAlpha\",preserveAspectRatio:\"preserveAspectRatio\",primitiveUnits:\"primitiveUnits\",r:0,radius:0,refX:\"refX\",refY:\"refY\",renderingIntent:\"rendering-intent\",repeatCount:\"repeatCount\",repeatDur:\"repeatDur\",requiredExtensions:\"requiredExtensions\",requiredFeatures:\"requiredFeatures\",restart:0,result:0,rotate:0,rx:0,ry:0,scale:0,seed:0,shapeRendering:\"shape-rendering\",slope:0,spacing:0,specularConstant:\"specularConstant\",specularExponent:\"specularExponent\",speed:0,spreadMethod:\"spreadMethod\",startOffset:\"startOffset\",stdDeviation:\"stdDeviation\",stemh:0,stemv:0,stitchTiles:\"stitchTiles\",stopColor:\"stop-color\",stopOpacity:\"stop-opacity\",strikethroughPosition:\"strikethrough-position\",strikethroughThickness:\"strikethrough-thickness\",string:0,stroke:0,strokeDasharray:\"stroke-dasharray\",strokeDashoffset:\"stroke-dashoffset\",strokeLinecap:\"stroke-linecap\",strokeLinejoin:\"stroke-linejoin\",strokeMiterlimit:\"stroke-miterlimit\",strokeOpacity:\"stroke-opacity\",strokeWidth:\"stroke-width\",surfaceScale:\"surfaceScale\",systemLanguage:\"systemLanguage\",tableValues:\"tableValues\",targetX:\"targetX\",targetY:\"targetY\",textAnchor:\"text-anchor\",textDecoration:\"text-decoration\",textRendering:\"text-rendering\",textLength:\"textLength\",to:0,transform:0,u1:0,u2:0,underlinePosition:\"underline-position\",underlineThickness:\"underline-thickness\",unicode:0,unicodeBidi:\"unicode-bidi\",unicodeRange:\"unicode-range\",unitsPerEm:\"units-per-em\",vAlphabetic:\"v-alphabetic\",vHanging:\"v-hanging\",vIdeographic:\"v-ideographic\",vMathematical:\"v-mathematical\",values:0,vectorEffect:\"vector-effect\",version:0,vertAdvY:\"vert-adv-y\",vertOriginX:\"vert-origin-x\",vertOriginY:\"vert-origin-y\",viewBox:\"viewBox\",viewTarget:\"viewTarget\",visibility:0,widths:0,wordSpacing:\"word-spacing\",writingMode:\"writing-mode\",x:0,xHeight:\"x-height\",x1:0,x2:0,xChannelSelector:\"xChannelSelector\",xlinkActuate:\"xlink:actuate\",xlinkArcrole:\"xlink:arcrole\",xlinkHref:\"xlink:href\",xlinkRole:\"xlink:role\",xlinkShow:\"xlink:show\",xlinkTitle:\"xlink:title\",xlinkType:\"xlink:type\",xmlBase:\"xml:base\",xmlns:0,xmlnsXlink:\"xmlns:xlink\",xmlLang:\"xml:lang\",xmlSpace:\"xml:space\",y:0,y1:0,y2:0,yChannelSelector:\"yChannelSelector\",z:0,zoomAndPan:\"zoomAndPan\"},o={Properties:{},DOMAttributeNamespaces:{xlinkActuate:n.xlink,xlinkArcrole:n.xlink,xlinkHref:n.xlink,xlinkRole:n.xlink,xlinkShow:n.xlink,xlinkTitle:n.xlink,xlinkType:n.xlink,xmlBase:n.xml,xmlLang:n.xml,xmlSpace:n.xml},DOMAttributeNames:{}};Object.keys(r).forEach(function(e){o.Properties[e]=0,r[e]&&(o.DOMAttributeNames[e]=r[e])}),e.exports=o},function(e,t,n){\"use strict\";function r(e){if(\"selectionStart\"in e&&l.hasSelectionCapabilities(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var t=window.getSelection();return{anchorNode:t.anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}}if(document.selection){var n=document.selection.createRange();return{parentElement:n.parentElement(),text:n.text,top:n.boundingTop,left:n.boundingLeft}}}function o(e,t){if(M||null==f||f!==u())return null;var n=r(f);if(!A||!p(A,n)){A=n;var o=c.getPooled(g.select,m,e,t);return o.type=\"select\",o.target=f,i.accumulateTwoPhaseDispatches(o),o}return null}var i=n(66),a=n(73),s=n(59),l=n(167),c=n(78),u=n(173),d=n(92),p=n(143),h=a.canUseDOM&&\"documentMode\"in document&&document.documentMode<=11,g={select:{phasedRegistrationNames:{bubbled:\"onSelect\",captured:\"onSelectCapture\"},dependencies:[\"topBlur\",\"topContextMenu\",\"topFocus\",\"topKeyDown\",\"topKeyUp\",\"topMouseDown\",\"topMouseUp\",\"topSelectionChange\"]}},f=null,m=null,A=null,M=!1,w=!1,v={eventTypes:g,extractEvents:function(e,t,n,r){if(!w)return null;var i=t?s.getNodeFromInstance(t):window;switch(e){case\"topFocus\":(d(i)||\"true\"===i.contentEditable)&&(f=i,m=t,A=null);break;case\"topBlur\":f=null,m=null,A=null;break;case\"topMouseDown\":M=!0;break;case\"topContextMenu\":case\"topMouseUp\":return M=!1,o(n,r);case\"topSelectionChange\":if(h)break;case\"topKeyDown\":case\"topKeyUp\":return o(n,r)}return null},didPutListener:function(e,t,n){\"onSelect\"===t&&(w=!0)}};e.exports=v},function(e,t,n){\"use strict\";function r(e){return\".\"+e._rootNodeID}function o(e){return\"button\"===e||\"input\"===e||\"select\"===e||\"textarea\"===e}var i=n(60),a=n(163),s=n(66),l=n(59),c=n(177),u=n(178),d=n(78),p=n(179),h=n(180),g=n(95),f=n(183),m=n(184),A=n(185),M=n(96),w=n(186),v=n(31),b=n(181),y=(n(34),{}),x={};[\"abort\",\"animationEnd\",\"animationIteration\",\"animationStart\",\"blur\",\"canPlay\",\"canPlayThrough\",\"click\",\"contextMenu\",\"copy\",\"cut\",\"doubleClick\",\"drag\",\"dragEnd\",\"dragEnter\",\"dragExit\",\"dragLeave\",\"dragOver\",\"dragStart\",\"drop\",\"durationChange\",\"emptied\",\"encrypted\",\"ended\",\"error\",\"focus\",\"input\",\"invalid\",\"keyDown\",\"keyPress\",\"keyUp\",\"load\",\"loadedData\",\"loadedMetadata\",\"loadStart\",\"mouseDown\",\"mouseMove\",\"mouseOut\",\"mouseOver\",\"mouseUp\",\"paste\",\"pause\",\"play\",\"playing\",\"progress\",\"rateChange\",\"reset\",\"scroll\",\"seeked\",\"seeking\",\"stalled\",\"submit\",\"suspend\",\"timeUpdate\",\"touchCancel\",\"touchEnd\",\"touchMove\",\"touchStart\",\"transitionEnd\",\"volumeChange\",\"waiting\",\"wheel\"].forEach(function(e){var t=e[0].toUpperCase()+e.slice(1),n=\"on\"+t,r=\"top\"+t,o={phasedRegistrationNames:{bubbled:n,captured:n+\"Capture\"},dependencies:[r]};y[e]=o,x[r]=o});var T={},C={eventTypes:y,extractEvents:function(e,t,n,r){var o=x[e];if(!o)return null;var a;switch(e){case\"topAbort\":case\"topCanPlay\":case\"topCanPlayThrough\":case\"topDurationChange\":case\"topEmptied\":case\"topEncrypted\":case\"topEnded\":case\"topError\":case\"topInput\":case\"topInvalid\":case\"topLoad\":case\"topLoadedData\":case\"topLoadedMetadata\":case\"topLoadStart\":case\"topPause\":case\"topPlay\":case\"topPlaying\":case\"topProgress\":case\"topRateChange\":case\"topReset\":case\"topSeeked\":case\"topSeeking\":case\"topStalled\":case\"topSubmit\":case\"topSuspend\":case\"topTimeUpdate\":case\"topVolumeChange\":case\"topWaiting\":a=d;break;case\"topKeyPress\":if(0===b(n))return null;case\"topKeyDown\":case\"topKeyUp\":a=h;break;case\"topBlur\":case\"topFocus\":a=p;break;case\"topClick\":if(2===n.button)return null;case\"topDoubleClick\":case\"topMouseDown\":case\"topMouseMove\":case\"topMouseUp\":case\"topMouseOut\":case\"topMouseOver\":case\"topContextMenu\":a=g;break;case\"topDrag\":case\"topDragEnd\":case\"topDragEnter\":case\"topDragExit\":case\"topDragLeave\":case\"topDragOver\":case\"topDragStart\":case\"topDrop\":a=f;break;case\"topTouchCancel\":case\"topTouchEnd\":case\"topTouchMove\":case\"topTouchStart\":a=m;break;case\"topAnimationEnd\":case\"topAnimationIteration\":case\"topAnimationStart\":a=c;break;case\"topTransitionEnd\":a=A;break;case\"topScroll\":a=M;break;case\"topWheel\":a=w;break;case\"topCopy\":case\"topCut\":case\"topPaste\":a=u}a?void 0:i(\"86\",e);var l=a.getPooled(o,t,n,r);return s.accumulateTwoPhaseDispatches(l),l},didPutListener:function(e,t,n){if(\"onClick\"===t&&!o(e._tag)){var i=r(e),s=l.getNodeFromInstance(e);T[i]||(T[i]=a.listen(s,\"click\",v))}},willDeleteListener:function(e,t){if(\"onClick\"===t&&!o(e._tag)){var n=r(e);T[n].remove(),delete T[n]}}};e.exports=C},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i={animationName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i={clipboardData:function(e){return\"clipboardData\"in e?e.clipboardData:window.clipboardData}};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(96),i={relatedTarget:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(96),i=n(181),a=n(182),s=n(98),l={key:a,location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:s,charCode:function(e){return\"keypress\"===e.type?i(e):0},keyCode:function(e){return\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0},which:function(e){return\"keypress\"===e.type?i(e):\"keydown\"===e.type||\"keyup\"===e.type?e.keyCode:0}};o.augmentClass(r,l),e.exports=r},function(e,t){\"use strict\";function n(e){var t,n=e.keyCode;return\"charCode\"in e?(t=e.charCode,0===t&&13===n&&(t=13)):t=n,t>=32||13===t?t:0}e.exports=n},function(e,t,n){\"use strict\";function r(e){if(e.key){var t=i[e.key]||e.key;if(\"Unidentified\"!==t)return t}if(\"keypress\"===e.type){var n=o(e);return 13===n?\"Enter\":String.fromCharCode(n)}return\"keydown\"===e.type||\"keyup\"===e.type?a[e.keyCode]||\"Unidentified\":\"\"}var o=n(181),i={Esc:\"Escape\",Spacebar:\" \",Left:\"ArrowLeft\",Up:\"ArrowUp\",Right:\"ArrowRight\",Down:\"ArrowDown\",Del:\"Delete\",Win:\"OS\",Menu:\"ContextMenu\",Apps:\"ContextMenu\",Scroll:\"ScrollLock\",MozPrintableKey:\"Unidentified\"},a={8:\"Backspace\",9:\"Tab\",12:\"Clear\",13:\"Enter\",16:\"Shift\",17:\"Control\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Escape\",32:\" \",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"ArrowLeft\",38:\"ArrowUp\",39:\"ArrowRight\",40:\"ArrowDown\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"NumLock\",145:\"ScrollLock\",224:\"Meta\"};e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(95),i={dataTransfer:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(96),i=n(98),a={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:i};o.augmentClass(r,a),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(78),i={propertyName:null,elapsedTime:null,pseudoElement:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t,n,r){return o.call(this,e,t,n,r)}var o=n(95),i={deltaX:function(e){return\"deltaX\"in e?e.deltaX:\"wheelDeltaX\"in e?-e.wheelDeltaX:0},deltaY:function(e){return\"deltaY\"in e?e.deltaY:\"wheelDeltaY\"in e?-e.wheelDeltaY:\"wheelDelta\"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null};o.augmentClass(r,i),e.exports=r},function(e,t,n){\"use strict\";function r(e,t){for(var n=Math.min(e.length,t.length),r=0;n>r;r++)if(e.charAt(r)!==t.charAt(r))return r;return e.length===t.length?-1:n}function o(e){return e?e.nodeType===U?e.documentElement:e.firstChild:null}function i(e){return e.getAttribute&&e.getAttribute(L)||\"\"}function a(e,t,n,r,o){var i;if(b.logTopLevelRenders){var a=e._currentElement.props.child,s=a.type;i=\"React mount: \"+(\"string\"==typeof s?s:s.displayName||s.name),console.time(i)}var l=T.mountComponent(e,n,null,w(e,t),o,0);i&&console.timeEnd(i),e._renderedComponent._topLevelWrapper=e,O._mountImageIntoNode(l,t,e,r,n)}function s(e,t,n,r){var o=N.ReactReconcileTransaction.getPooled(!n&&v.useCreateElement);o.perform(a,null,e,t,o,n,r),N.ReactReconcileTransaction.release(o)}function l(e,t,n){for(T.unmountComponent(e,n),t.nodeType===U&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)}function c(e){var t=o(e);if(t){var n=M.getInstanceFromNode(t);return!(!n||!n._hostParent)}}function u(e){return!(!e||e.nodeType!==j&&e.nodeType!==U&&e.nodeType!==B)}function d(e){var t=o(e),n=t&&M.getInstanceFromNode(t);return n&&!n._hostParent?n:null}function p(e){var t=d(e);return t?t._hostContainerInfo._topLevelWrapper:null}var h=n(60),g=n(102),f=n(61),m=n(25),A=n(126),M=(n(39),n(59)),w=n(188),v=n(189),b=n(83),y=n(137),x=(n(87),n(190)),T=n(84),C=n(156),N=n(81),I=n(33),E=n(140),D=(n(34),n(104)),S=n(144),L=(n(30),f.ID_ATTRIBUTE_NAME),k=f.ROOT_ATTRIBUTE_NAME,j=1,U=9,B=11,R={},z=1,Q=function(){this.rootID=z++};Q.prototype.isReactComponent={},Q.prototype.render=function(){return this.props.child},Q.isReactTopLevelWrapper=!0;var O={TopLevelWrapper:Q,_instancesByReactRootID:R,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r,o){return O.scrollMonitor(r,function(){C.enqueueElementInternal(e,t,n),o&&C.enqueueCallbackInternal(e,o)}),e},_renderNewRootComponent:function(e,t,n,r){u(t)?void 0:h(\"37\"),A.ensureScrollValueMonitoring();var o=E(e,!1);N.batchedUpdates(s,o,t,n,r);var i=o._instance.rootID;return R[i]=o,o},renderSubtreeIntoContainer:function(e,t,n,r){return null!=e&&y.has(e)?void 0:h(\"38\"),O._renderSubtreeIntoContainer(e,t,n,r)},_renderSubtreeIntoContainer:function(e,t,n,r){C.validateCallback(r,\"ReactDOM.render\"),m.isValidElement(t)?void 0:h(\"39\",\"string\"==typeof t?\" Instead of passing a string like 'div', pass React.createElement('div') or <div />.\":\"function\"==typeof t?\" Instead of passing a class like Foo, pass React.createElement(Foo) or <Foo />.\":null!=t&&void 0!==t.props?\" This may be caused by unintentionally loading two independent copies of React.\":\"\");var a,s=m.createElement(Q,{child:t});if(e){var l=y.get(e);a=l._processChildContext(l._context)}else a=I;var u=p(n);if(u){var d=u._currentElement,g=d.props.child;if(S(g,t)){var f=u._renderedComponent.getPublicInstance(),A=r&&function(){r.call(f)};return O._updateRootComponent(u,s,a,n,A),f}O.unmountComponentAtNode(n)}var M=o(n),w=M&&!!i(M),v=c(n),b=w&&!u&&!v,x=O._renderNewRootComponent(s,n,b,a)._renderedComponent.getPublicInstance();return r&&r.call(x),x},render:function(e,t,n){return O._renderSubtreeIntoContainer(null,e,t,n)},unmountComponentAtNode:function(e){u(e)?void 0:h(\"40\");var t=p(e);if(!t){c(e),1===e.nodeType&&e.hasAttribute(k);return!1}return delete R[t._instance.rootID],N.batchedUpdates(l,t,e,!1),!0},_mountImageIntoNode:function(e,t,n,i,a){if(u(t)?void 0:h(\"41\"),i){var s=o(t);if(x.canReuseMarkup(e,s))return void M.precacheNode(n,s);var l=s.getAttribute(x.CHECKSUM_ATTR_NAME);s.removeAttribute(x.CHECKSUM_ATTR_NAME);var c=s.outerHTML;s.setAttribute(x.CHECKSUM_ATTR_NAME,l);var d=e,p=r(d,c),f=\" (client) \"+d.substring(p-20,p+20)+\"\\n (server) \"+c.substring(p-20,p+20);t.nodeType===U?h(\"42\",f):void 0}if(t.nodeType===U?h(\"43\"):void 0,a.useCreateElement){for(;t.lastChild;)t.removeChild(t.lastChild);g.insertTreeBefore(t,e,null)}else D(t,e),M.precacheNode(n,t.firstChild)}};e.exports=O},function(e,t,n){\"use strict\";function r(e,t){var n={_topLevelWrapper:e,_idCounter:1,_ownerDocument:t?t.nodeType===o?t:t.ownerDocument:null,_node:t,_tag:t?t.nodeName.toLowerCase():null,_namespaceURI:t?t.namespaceURI:null};return n}var o=(n(157),9);e.exports=r},function(e,t){\"use strict\";var n={useCreateElement:!0,useFiber:!1};e.exports=n},function(e,t,n){\"use strict\";var r=n(191),o=/\\/?>/,i=/^<\\!\\-\\-/,a={CHECKSUM_ATTR_NAME:\"data-react-checksum\",addChecksumToMarkup:function(e){var t=r(e);return i.test(e)?e:e.replace(o,\" \"+a.CHECKSUM_ATTR_NAME+'=\"'+t+'\"$&')},canReuseMarkup:function(e,t){var n=t.getAttribute(a.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var o=r(e);return o===n}};e.exports=a},function(e,t){\"use strict\";function n(e){for(var t=1,n=0,o=0,i=e.length,a=-4&i;a>o;){for(var s=Math.min(o+4096,a);s>o;o+=4)n+=(t+=e.charCodeAt(o))+(t+=e.charCodeAt(o+1))+(t+=e.charCodeAt(o+2))+(t+=e.charCodeAt(o+3));t%=r,n%=r}for(;i>o;o++)n+=t+=e.charCodeAt(o);return t%=r,n%=r,t|n<<16}var r=65521;e.exports=n},function(e,t){\"use strict\";e.exports=\"15.6.2\"},function(e,t,n){\"use strict\";function r(e){if(null==e)return null;if(1===e.nodeType)return e;var t=a.get(e);return t?(t=s(t),t?i.getNodeFromInstance(t):null):void(\"function\"==typeof e.render?o(\"44\"):o(\"45\",Object.keys(e)))}var o=n(60),i=(n(39),n(59)),a=n(137),s=n(194);n(34),n(30);e.exports=r},function(e,t,n){\"use strict\";function r(e){for(var t;(t=e._renderedNodeType)===o.COMPOSITE;)e=e._renderedComponent;return t===o.HOST?e._renderedComponent:t===o.EMPTY?null:void 0}var o=n(142);e.exports=r},function(e,t,n){\"use strict\";var r=n(187);e.exports=r.renderSubtreeIntoContainer},function(e,t,n){/*!\n\t * clipboard.js v2.0.11\n\t * https://clipboardjs.com/\n\t *\n\t * Licensed MIT © Zeno Rocha\n\t */\n!function(t,n){e.exports=n()}(this,function(){return function(){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{}};return t[r](o,o.exports,e),o.exports}var t={686:function(e,t,n){\"use strict\";function r(e){try{return document.execCommand(e)}catch(t){return!1}}function o(e){var t=\"rtl\"===document.documentElement.getAttribute(\"dir\"),n=document.createElement(\"textarea\");n.style.fontSize=\"12pt\",n.style.border=\"0\",n.style.padding=\"0\",n.style.margin=\"0\",n.style.position=\"absolute\",n.style[t?\"right\":\"left\"]=\"-9999px\";var r=window.pageYOffset||document.documentElement.scrollTop;return n.style.top=\"\".concat(r,\"px\"),n.setAttribute(\"readonly\",\"\"),n.value=e,n}function i(e){\"@babel/helpers - typeof\";return(i=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e})(e)}function a(e){\"@babel/helpers - typeof\";return(a=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e})(e)}function s(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}function l(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function c(e,t,n){return t&&l(e.prototype,t),n&&l(e,n),e}function u(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Super expression must either be null or a function\");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&d(e,t)}function d(e,t){return(d=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function p(e){var t=f();return function(){var n,r=m(e);if(t){var o=m(this).constructor;n=Reflect.construct(r,arguments,o)}else n=r.apply(this,arguments);return h(this,n)}}function h(e,t){return!t||\"object\"!==a(t)&&\"function\"!=typeof t?g(e):t}function g(e){if(void 0===e)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return e}function f(){if(\"undefined\"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if(\"function\"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function m(e){return(m=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function A(e,t){var n=\"data-clipboard-\".concat(e);if(t.hasAttribute(n))return t.getAttribute(n)}n.d(t,{\"default\":function(){return k}});var M=n(279),w=n.n(M),v=n(370),b=n.n(v),y=n(817),x=n.n(y),T=function(e){var t=x()(e);return r(\"cut\"),t},C=T,N=function(e,t){var n=o(e);t.container.appendChild(n);var i=x()(n);return r(\"copy\"),n.remove(),i},I=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{container:document.body},n=\"\";return\"string\"==typeof e?n=N(e,t):e instanceof HTMLInputElement&&![\"text\",\"search\",\"url\",\"tel\",\"password\"].includes(null===e||void 0===e?void 0:e.type)?n=N(e.value,t):(n=x()(e),r(\"copy\")),n},E=I,D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.action,n=void 0===t?\"copy\":t,r=e.container,o=e.target,a=e.text;if(\"copy\"!==n&&\"cut\"!==n)throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');if(void 0!==o){if(!o||\"object\"!==i(o)||1!==o.nodeType)throw new Error('Invalid \"target\" value, use a valid Element');if(\"copy\"===n&&o.hasAttribute(\"disabled\"))throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');if(\"cut\"===n&&(o.hasAttribute(\"readonly\")||o.hasAttribute(\"disabled\")))throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes')}return a?E(a,{container:r}):o?\"cut\"===n?C(o):E(o,{container:r}):void 0},S=D,L=function(e){function t(e,r){var o;return s(this,t),o=n.call(this),o.resolveOptions(r),o.listenClick(e),o}u(t,e);var n=p(t);return c(t,[{key:\"resolveOptions\",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.action=\"function\"==typeof e.action?e.action:this.defaultAction,this.target=\"function\"==typeof e.target?e.target:this.defaultTarget,this.text=\"function\"==typeof e.text?e.text:this.defaultText,this.container=\"object\"===a(e.container)?e.container:document.body}},{key:\"listenClick\",value:function(e){var t=this;this.listener=b()(e,\"click\",function(e){return t.onClick(e)})}},{key:\"onClick\",value:function(e){var t=e.delegateTarget||e.currentTarget,n=this.action(t)||\"copy\",r=S({action:n,container:this.container,target:this.target(t),text:this.text(t)});this.emit(r?\"success\":\"error\",{action:n,text:r,trigger:t,clearSelection:function(){t&&t.focus(),window.getSelection().removeAllRanges()}})}},{key:\"defaultAction\",value:function(e){return A(\"action\",e)}},{key:\"defaultTarget\",value:function(e){var t=A(\"target\",e);return t?document.querySelector(t):void 0}},{key:\"defaultText\",value:function(e){return A(\"text\",e)}},{key:\"destroy\",value:function(){this.listener.destroy()}}],[{key:\"copy\",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{container:document.body};return E(e,t)}},{key:\"cut\",value:function(e){return C(e)}},{key:\"isSupported\",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[\"copy\",\"cut\"],t=\"string\"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach(function(e){n=n&&!!document.queryCommandSupported(e)}),n}}]),t}(w()),k=L},828:function(e){function t(e,t){for(;e&&e.nodeType!==n;){if(\"function\"==typeof e.matches&&e.matches(t))return e;e=e.parentNode}}var n=9;if(\"undefined\"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}e.exports=t},438:function(e,t,n){function r(e,t,n,r,o){var a=i.apply(this,arguments);return e.addEventListener(n,a,o),{destroy:function(){e.removeEventListener(n,a,o)}}}function o(e,t,n,o,i){return\"function\"==typeof e.addEventListener?r.apply(null,arguments):\"function\"==typeof n?r.bind(null,document).apply(null,arguments):(\"string\"==typeof e&&(e=document.querySelectorAll(e)),Array.prototype.map.call(e,function(e){return r(e,t,n,o,i)}))}function i(e,t,n,r){return function(n){n.delegateTarget=a(n.target,t),n.delegateTarget&&r.call(e,n)}}var a=n(828);e.exports=o},879:function(e,t){t.node=function(e){return void 0!==e&&e instanceof HTMLElement&&1===e.nodeType},t.nodeList=function(e){var n=Object.prototype.toString.call(e);return void 0!==e&&(\"[object NodeList]\"===n||\"[object HTMLCollection]\"===n)&&\"length\"in e&&(0===e.length||t.node(e[0]))},t.string=function(e){return\"string\"==typeof e||e instanceof String},t.fn=function(e){var t=Object.prototype.toString.call(e);return\"[object Function]\"===t}},370:function(e,t,n){function r(e,t,n){if(!e&&!t&&!n)throw new Error(\"Missing required arguments\");if(!s.string(t))throw new TypeError(\"Second argument must be a String\");if(!s.fn(n))throw new TypeError(\"Third argument must be a Function\");if(s.node(e))return o(e,t,n);if(s.nodeList(e))return i(e,t,n);if(s.string(e))return a(e,t,n);throw new TypeError(\"First argument must be a String, HTMLElement, HTMLCollection, or NodeList\")}function o(e,t,n){return e.addEventListener(t,n),{destroy:function(){e.removeEventListener(t,n)}}}function i(e,t,n){return Array.prototype.forEach.call(e,function(e){e.addEventListener(t,n)}),{destroy:function(){Array.prototype.forEach.call(e,function(e){e.removeEventListener(t,n)})}}}function a(e,t,n){return l(document.body,e,t,n)}var s=n(879),l=n(438);e.exports=r},817:function(e){function t(e){var t;if(\"SELECT\"===e.nodeName)e.focus(),t=e.value;else if(\"INPUT\"===e.nodeName||\"TEXTAREA\"===e.nodeName){var n=e.hasAttribute(\"readonly\");n||e.setAttribute(\"readonly\",\"\"),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute(\"readonly\"),t=e.value}else{e.hasAttribute(\"contenteditable\")&&e.focus();var r=window.getSelection(),o=document.createRange();o.selectNodeContents(e),r.removeAllRanges(),r.addRange(o),t=r.toString()}return t}e.exports=t},279:function(e){function t(){}t.prototype={on:function(e,t,n){var r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){function r(){o.off(e,r),t.apply(n,arguments)}var o=this;return r._=t,this.on(e,r,n)},emit:function(e){var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),r=0,o=n.length;for(r;o>r;r++)n[r].fn.apply(n[r].ctx,t);return this},off:function(e,t){var n=this.e||(this.e={}),r=n[e],o=[];if(r&&t)for(var i=0,a=r.length;a>i;i++)r[i].fn!==t&&r[i].fn._!==t&&o.push(r[i]);return o.length?n[e]=o:delete n[e],this}},e.exports=t,e.exports.TinyEmitter=t}},n={};return!function(){e.n=function(t){var n=t&&t.__esModule?function(){return t[\"default\"]}:function(){return t};return e.d(n,{a:n}),n}}(),!function(){e.d=function(t,n){for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})}}(),!function(){e.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}}(),e(686)}()[\"default\"]})},function(e,t,n){\"use strict\";function r(e,t){return\"blank\"===e?t(\"\"):void l.getTempFile({filename:e},function(e,n){return e?void(e.em&&(u.error(e.em),e.ec)||t(e.value||\"\")):c.showSystemError(n)})}var o=n(24),i=n(57),a=n(198),s=n(199),l=n(200),c=n(202),u=n(206),d=n(209),p=n(213),h=n(214),g=i.findDOMNode,f=11534336,m='javascript:\"<style>html,body{padding:0;margin:0}</style><textarea></textarea>\"',A={padding:0,border:\"none\",width:980,height:550,margin:0,verticalAlign:\"top\"},M=o.createClass({displayName:\"EditorDialog\",getInitialState:function(){return{}},show:function(e){this._hideDialog=!1,this.setState(e);var t=this._textarea;this.props.textEditor&&t&&(t.value=e&&e.value||\"\",setTimeout(function(){t.focus()},600)),this.refs.editorDialog.show()},hide:function(){this.refs.editorDialog.hide(),this._hideDialog=!0},onChange:function(e){this.setState({value:e.target.value})},shouldComponentUpdate:function(){return this._hideDialog===!1},componentDidMount:function(){var e=this;if(e.props.textEditor){s.on(\"uploadTempFile\",function(t,n){e.readFile(n)});var t=g(e.refs.iframe),n=function(){var n=t.contentWindow.document.querySelector(\"textarea\"),r=n&&n.style;e._textarea=n,r&&(r.resize=\"none\",r.width=A.width+\"px\",r.height=A.height+\"px\",r.padding=\"5px\",r.border=\"1px solid var(--c-border, #ccc)\",r.borderRadius=\"3px\",n.maxLength=f,n.placeholder=e.props.placeholder||\"Enter text\",n.onkeydown=function(t){(t.ctrlKey||t.metaKey)&&83===t.keyCode&&(t.preventDefault(),e.props.textEditor&&e.onSave()),c.handleFormat(t,e.formatValue),c.handleTab(t)})};t.onload=n,n(),this.props.standalone&&s.on(\"showEditorDialog\",function(t,n,o){if(n.name){var i=l.valuesModal.get(n.name),a=i&&i.value||\"\";e._keyName=n.name,e.show({value:a,title:i?\"Update value for key '\"+n.name+\"' in Values\":\"Create a new key '\"+n.name+\"' to Values\",isTempFile:!1})}else{var s=o&&l.rulesModal.get(n.ruleName);if(s){var c=n.tempFile;e._tempFile=c,e._fileElem=o,e._rulesItem=s,c=c||\"blank\";var u=\"blank\"===c||/[\\\\/]/.test(c);r(c,function(t){e.show({value:t,title:(u?\"Create\":\"Modify\")+\" temp file\"+(u?\"\":\" (temp/\"+c+\")\"),isTempFile:!0})})}}})}},getValue:function(){var e=this._textarea?this._textarea.value:this.state.value;return e||\"\"},onConfirm:function(){var e=this.props.onConfirm(this.getValue());e!==!1&&this.hide()},onSave:function(e){var t=this,n=\"string\"==typeof e,r=n?e:t.getValue();if(!n&&!t.state.isTempFile)return void l.values.add({name:t._keyName,value:r},function(e,n){e&&0===e.ec?(s.trigger(\"addNewValuesFile\",{filename:t._keyName,data:r,update:!0}),t.hide()):c.showSystemError(n)});var o={clientId:l.getPageId()};o[n?\"base64\":\"value\"]=r,l.createTempFile(JSON.stringify(o),function(e,n){if(!e||0!==e.ec)return c.showSystemError(n);for(var r=t._fileElem,o=r.closest(\".CodeMirror-line\")[0],i=r.closest(\".CodeMirror-code\").find(\".CodeMirror-line\"),a=0,u=0,d=i.length;d>u;u++)if(i[u]===o){a=u;break}var p,h=r.text(),g=t._tempFile;if(g){var f=g.lastIndexOf(\".\");-1===f?p=h.replace(\"temp/\"+g,e.filepath):(p=h.replace(g.substring(0,f),e.filepath),-1===p.indexOf(\"://\")&&(p=\"file://\"+p))}else p=h.replace(/temp(\\.[\\w-]+)?$/,e.filepath+\"$1\");var m=t._rulesItem.value.split(/\\r\\n|\\r|\\n/).map(function(e,t){return t===a&&(e=e.trim().split(/\\s+/).map(function(e){return e===h?p:e}).join(\" \")),e}).join(\"\\n\"),A=t._rulesItem.name;l.rules.add({name:A,value:m,selected:t._rulesItem.selected?\"1\":\"\"},function(e,n){e&&0===e.ec?(s.trigger(\"addNewRulesFile\",{filename:A,data:m,update:!0}),t.hide()):c.showSystemError(n)})})},formatValue:function(){var e=this._textarea;try{var t=e.value.trim();if(\"{\"===t[0]||\"[\"===t[0]){var n=JSON.stringify(JSON.parse(t),null,\"  \");e.value!==n&&(e.value=n)}}catch(r){u.error(r.message)}},clearValue:function(){this._textarea.value=\"\"},onUpload:function(){this.reading||g(this.refs.readLocalFile).click()},readFile:function(e){var t=this;t.reading=!0,c.readFile(e,function(e){t.reading=!1,t.onSave(c.bytesToBase64(e))})},readLocalFile:function(){var e=new FormData(g(this.refs.readLocalFileForm)),t=e.get(\"localFile\");return t.size>f?d.alert(\"Total file size must not exceed 10MB\"):(this.readFile(t),void(g(this.refs.readLocalFile).value=\"\"))},render:function(){var e=this.state,t=this.props,n=e.value,r=t.title||e.title,i=this.props.textEditor,s=i&&!t.onConfirm;return o.createElement(a,{ref:\"editorDialog\",wstyle:\"w-editor-dialog\"+(i?\" w-big-editor-dialog\":\"\")+(s?\" w-show-upload-temp-file\":\"\")},o.createElement(\"div\",{className:\"modal-header\"},o.createElement(\"h4\",null,r||\"Edit copied text\"),o.createElement(h,null)),o.createElement(\"div\",{className:\"modal-body\"},i?o.createElement(\"div\",{className:\"w-mock-action\"},t.hideFormat?null:o.createElement(\"a\",{onClick:this.formatValue},\"Format\"),o.createElement(\"a\",{onClick:this.clearValue},\"Clear\")):null,i?o.createElement(\"div\",{className:\"w-fake-iframe w-fix-drag\"},o.createElement(\"iframe\",{ref:\"iframe\",\"data-type\":\"fake\",src:m,onLoad:l.handleIframeLoad,style:A})):o.createElement(\"textarea\",{onChange:this.onChange,value:n})),i?o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),t.onConfirm?null:o.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:this.onUpload},o.createElement(p,{name:\"folder-open\"}),\"Upload\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:t.onConfirm?this.onConfirm:this.onSave},t.onConfirm?\"Confirm\":\"Save\")):o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),o.createElement(\"button\",{type:\"button\",\"data-dismiss\":\"modal\",className:\"btn btn-primary w-copy-text-with-tips\",\"data-clipboard-text\":e.value,disabled:!n},\"Copy\")),o.createElement(\"form\",{ref:\"readLocalFileForm\",encType:\"multipart/form-data\",style:{display:\"none\"}},o.createElement(\"input\",{ref:\"readLocalFile\",onChange:this.readLocalFile,type:\"file\",name:\"localFile\"})))}});e.exports=M},function(e,t,n){\"use strict\";var r=window.jQuery=n(18),o=n(24),i=n(57),a=o.createClass({displayName:\"Dialog\",getInitialState:function(){return{}},componentDidMount:function(){var e=this;this.container=r(document.createElement(\"div\"));var t=this.props.fullCustom?\" w-custom-dialog\":\"\";this.container.addClass(\"modal fade\"+t+(this.props.wstyle?\" \"+this.props.wstyle:\"\")),document.body.appendChild(this.container[0]),this.componentDidUpdate(),\"function\"==typeof this.props.customRef&&this.props.customRef(this),\"function\"==typeof this.props.onClose&&this.container.on(\"hidden.bs.modal\",this.props.onClose),this.container.on(\"hide.bs.modal\",function(){e._isVisible=!1}),this.container.on(\"show.bs.modal\",function(){e._isVisible=!0}),\"function\"==typeof e.props.onShow&&this.container.on(\"shown.bs.modal\",function(){e.props.onShow(e)})},componentDidUpdate:function(){i.unstable_renderSubtreeIntoContainer(this,this.getDialogElement(),this.container[0])},getDialogElement:function(){var e,t=this.props,n=t.wclassName;return t.width&&(e=e||{},e.width=t.width),t.fullCustom&&t.height&&(e=e||{},e.height=t.height),o.createElement(\"div\",{style:e,className:\"modal-dialog\"+(n?\" \"+n:\"\")},o.createElement(\"div\",{className:\"modal-content\"},this.props.children))},componentWillUnmount:function(){i.unmountComponentAtNode(this.container[0]),document.body.removeChild(this.container[0])},show:function(){this.container.hasClass(\"in\")||(this._isVisible=!0,this.container.modal(this.props.disableBackdrop?{show:!0,backdrop:!1}:\"show\"))},isVisible:function(){return this._isVisible},hide:function(){this.container.modal(\"hide\")},destroy:function(){this.hide(),this.container&&this.componentWillUnmount()},render:function(){return null}});e.exports=a},function(e,t,n){\"use strict\";var r=n(18);e.exports=r({})},function(e,t,n){\"use strict\";function r(e){var n=e.enabledCount||0;t.backRulesFirst=e.backRulesFirst,t.enabledRulesCount!==n&&(t.enabledRulesCount=n,ye.trigger(\"enabledRulesCountChange\",n))}function o(e){return e!==ae?!1:e?e.url!==ae.url||e.ip!==ae.ip?!1:e.name===ae.name&&e.value===ae.value:!0}function i(e){var n,r=location.hash.substring(1),i=r.indexOf(\"?\");if(-1!==i){var a=we.parseQueryString(r.substring(i+1),null,null,decodeURIComponent),s=a.rulesName||a.ruleName,l=a.valuesName||a.valueName;s!==t.activeRulesName&&(t.activeRulesName=s,ye.trigger(\"activeRules\")),l!==t.activeValuesName&&(t.activeValuesName=l,ye.trigger(\"activeValues\")),a.url&&(n={},n.url=a.url);for(var c=0;6>c;c++){var u=\"name\"+(c||\"\"),d=a[u];if(d){n=n||{},n[u]=d;var p=\"value\"+(c||\"\");n[p]=a[p]||\"\"}}a.ip&&(n=n||{},n.ip=a.ip),n&&n.name&&\"exact\"===a.mtype&&(n.mtype=1),le||\"true\"!==a.clearNetwork||(se=!0)}t.hashFilterObj=n,e&&!o(n)&&ye.trigger(\"hashFilterChange\"),ae=n}function a(e){e=e||{},be.set(\"filterText\",JSON.stringify({disabledFilterText:e.disabledFilterText,filterText:e.filterText,disabledExcludeText:e.disabledExcludeText,excludeText:e.excludeText}))}function s(){var e=we.parseJSON(be.get(\"filterText\"));return e?{disabledFilterText:e.disabledFilterText,filterText:we.toString(e.filterText).substring(0,Ie),disabledExcludeText:e.disabledExcludeText,excludeText:we.toString(e.excludeText).substring(0,Ee)}:{filterText:\"\",excludeText:\"\"}}function l(e){e=e||{},be.set(\"networkColumns\",JSON.stringify({columns:e.columns}))}function c(){return we.parseJSON(be.get(\"networkColumns\"))||{}}function u(e){var t=ft.length,n=t?we.findArray(ft,function(t){return t.text===e}):null;return t-=10,t>2&&(ft=ft.slice(t),n&&-1===ft.indexOf(n)&&ft.push(n)),n&&n.filter}function d(e){if(e=e&&e.trim()){var t=u(e);if(t)return t;var n;return e.split(/\\s+/).forEach(function(e){if(ht.test(e)){var r=gt[RegExp.$1],o=\"!\"===e[2];e=e.substring(o?3:2),e&&(t=t||[],n=we.toRegExp(e),t.push({type:r,not:o,pattern:n,keyword:n?null:e.toLowerCase()}))}else e&&(t=t||[],n=we.toRegExp(e),t.push({pattern:n,keyword:n?null:e.toLowerCase()}))}),t&&ft.push({text:e,filter:t}),t}}function p(e,t,n){if(!e)return!1;var r;if(t.pattern)r=t.pattern.test(e);else{if(n)try{var o=decodeURIComponent(e);o!==e&&(e+=\"\\n\"+o)}catch(i){}r=-1!==g(e).indexOf(t.keyword)}return t.not?!r:r}function h(e,t){for(var n=0,r=t.length;r>n;n++){var o=t[n];switch(o.type){case\"method\":if(p(e.req.method,o))return!0;break;case\"ip\":if(p(e.req.ip||\"127.0.0.1\",o))return!0;break;case\"headers\":if(p(we.objectToString(e.req.headers),o,!0))return!0;break;case\"host\":if(p(e.isHttps?e.url:we.getHost(e.url),o))return!0;break;case\"body\":if(p(we.getBody(e.req,!0),o))return!0;break;default:if(p((e.isHttps?\"tunnel://\":\"\")+e.url,o))return!0}}return!1}function g(e){return String(null==e?\"\":e).trim().toLowerCase()}function f(e){return Me({_:{url:\"cgi-bin/composer\",mode:e?\"cancel\":null}},vt)._}function m(e,t,n,r){return\"string\"!=typeof e&&(e=JSON.stringify(e)),r(e,t,n)}function A(e){var t=++Ct;return clearTimeout(xt),xt=null,Tt(function(n){return++Nt,Nt>t?void 0:Ct>t||n&&!n.ec?n&&!n.ec&&e(n.list):void(xt=xt||setTimeout(function(){A(e)},160))})}function M(e){t.hasInvalidCerts!=e.hasInvalidCerts&&(t.hasInvalidCerts=e.hasInvalidCerts,ye.trigger(\"updateUI\"))}function w(e){return ct[e]}function v(e,n){return t.getComposeData({ids:e.join()},n)}function b(e){var t=e&&e.length;if(t?(e=e.filter(w),t=e.length):(e=Object.keys(ct),t=e.length),!t)return setTimeout(b,It);var n=e.slice(0,5);e=e.slice(5),v(n,function(t){t&&n.forEach(function(e){var n=t[e];if(\"\"!==n){var r=ct[e];null==n&&delete ct[e],r&&r.forEach(function(e){e(n||\"\")})}}),setTimeout(function(){b(e)},It)})}function y(e,t,n){if(!e[n]||ee.clientId===e[t])return!1;var r=e[t],o=e[n];return ee[t]===r&&ee[n]===o?!1:(ee[t]=r,ee[n]=o,!0)}function x(e){y(e,\"mrulesClientId\",\"mrulesTime\")&&ye.trigger(\"rulesChanged\")}function T(e){y(e,\"mvaluesClientId\",\"mvaluesTime\")&&ye.trigger(\"valuesChanged\")}function C(e,t,n){for(var r=0;n>r;r++){var o=e[r],i=t[r];if(o.name!==i.name||o.action!==i.action)return!0}}function N(e,t){var n=e.length;if(n!==t.length)return!0;for(var r=0;n>r;r++){var o=e[r],i=t[r];if(o.title!==i.title||o.key!==i.key||o.iconKey!==i.iconKey||o.width!==i.width)return!0}}function I(e,t,n){var r=e.length,o=t.length;return r?1===r?void((!o||o>1||e[0].name!==t[0].name||e[0].action!==t[0].action)&&ye.trigger(n)):o&&1!==o?void((r!==o||C(e,t,r))&&ye.trigger(n)):ye.trigger(n):void(o&&ye.trigger(n))}function E(e){if(!e)return 0;var t=e.length;return\"=\"===e[t-1]&&(t-=2,\"=\"===e[t]&&--t),t}function D(e,t){Object.keys(t).forEach(function(n){var r=t[n];if(\"rules\"===n||\"rulesHeaders\"===n||\"url\"===n)r=r||e[n];else if(\"req\"===n||\"res\"===n){var o=e[n],i=o.base64;r.headers=r.headers||o.headers,r.rawHeaderNames=r.rawHeaderNames||o.rawHeaderNames,r.base64?r.preLen>0&&(r.base64=(i?i.substring(0,r.preLen):\"\")+r.base64):(r.base64=i,r[we.BODY_KEY]=o[we.BODY_KEY],r[we.HEX_KEY]=o[we.HEX_KEY],r[we.JSON_KEY]=o[we.JSON_KEY])}e[n]=r})}function S(e){var t=[\"\"];return e.requestTime||(t[0]=E(e.req.base64)),e.endTime||(t[1]=E(e.res.base64)),t.join(\"-\")}function L(){var e=document.querySelector(\"#whistleComposerFrames\");return e&&e.offsetWidth?ge:null}function k(){function e(){if(document.hidden){if(Date.now()-Et>Ke)return setTimeout(e,100)}else Et=Date.now();Fe.clearNetwork&&(oe=ie||oe,Fe.clearNetwork=!1);var n=G(),o=Qe.length,i=Oe.length,a=-1,l=-1,c=[],u=[],p=[];ze.forEach(function(e){e.endTime||e.lost||(c.push(e.id),u.push(S(e))),e.reqPlugin>0&&e.reqPlugin<10&&(++e.reqPlugin,p.push(e.id))});var g=t.clearedLogs,f=t.clearedSvrLogs;t.clearedLogs=t.clearedSvrLogs=!1,!t.pauseConsoleRefresh&&We>o&&(a=g&&ne||Ve),!t.pauseServerLogRefresh&&We>i&&(l=f&&re||Pe);var m,A,w=L()||Fe.getActive(),v=w&&w.frames;v&&!w.pauseRecordFrames&&(w.stopRecordFrames?(A=w.id,m=-3):v.length<=De&&(A=w.id,m=w.lastFrameId));var b=le?20:Fe.getDisplayCount(),y=t.curComposerReqId,C={startLogTime:t.stopConsoleRefresh?-3:a,startSvrLogTime:t.stopServerLogRefresh?-3:l,ids:c.join(),status:u.join(),startTime:n,dumpCount:_e,lastRowId:le||!b?oe:void 0,curReqId:A,lastFrameId:m,logId:ce||\"\",count:b||20,tunnelIds:p,composerReqId:y};le=!0,Ae.extend(C,ae),Ze&&(C.ip=\"self\"),Mt.getData(C,function(n){var o=n&&n.data&&n.data.hasNew;if(setTimeout(e,o?100:900),W(n),n&&0===n.ec){Array.isArray(n.installErrors)&&n.installErrors.length&&Te.error(n.installErrors.join(\"\\n\"));var i=t.isCapture;if(0===i)t.isCapture=!1;else if(1===i)t.isCapture=!0;else{var a=!!n.interceptHttpsConnects;t.isCapture!==a&&(t.isCapture=a,ye.trigger(\"reqTabsChange\"),ye.trigger(\"resTabsChange\"))}var l=n.server;ue=l&&l.port,pe=l&&l.account,me=l&&l.hasUpdater,K(l),t.whistleName=n.wName,t.account=n.account,t.disableInstaller=n.disableInstaller,t.supportH2=n.supportH2,t.version=l&&l.version,t.isWin=l&&l.isWin,r(n),t.custom1=n.custom1,t.custom2=n.custom2,t.custom1Key=n.custom1Key,t.custom2Key=n.custom2Key,C.dumpCount>0&&(_e=0),n.clientIp&&(t.clientIp=n.clientIp),M(n),x(n),T(n),Re.forEach(function(e){e(n)});var c,u=n.log.length,p=n.svrLog.length,g=tt,f=nt,m=rt,b=it,E=ot,S=[],L=[],k=[];$e=n.plugins||{},et=n.disabledPlugins||{},he||Object.keys($e).forEach(function(e){var t=e.slice(0,-1);if(!et[t]){var n=$e[e];k.push({mtime:n.mtime,priority:n.priority,_key:e,plugin:t,reqTab:n.reqTab,resTab:n.resTab,tab:n.tab,comTab:n.comTab,toolTab:n.toolTab,col:n.networkColumn});var r=n.webWorker;r&&-1===L.indexOf(r)&&(L.push(r),c=c||-1===st.indexOf(r))}}),k.sort(we.comparePlugin),tt=[],nt=[],rt=[],it=[],ot=[],Xe=[],k.forEach(function(e){var t=e.reqTab,n=e.resTab,r=e.tab,o=e.toolTab,i=e.comTab,a=e.plugin,s=e.col;t&&(t.plugin=a,tt.push(t)),n&&(n.plugin=a,nt.push(n)),r&&(r.plugin=a,rt.push(r)),i&&(i.plugin=a,it.push(i)),o&&(o.plugin=a,ot.push(o)),s&&(s.name=s.className=\"whistle.\"+e.plugin,s.isPlugin=!0,S.push(s),Xe.push(s.key))}),I(tt,g,\"reqTabsChange\"),I(nt,f,\"resTabsChange\"),I(rt,m,\"tabsChange\"),I(it,b,\"comTabsChange\"),I(ot,E,\"toolTabsChange\"),N(S,at)&&(at=S,ye.trigger(\"pluginColumnsChange\")),(c||st.length!==L.length)&&(st=L,Ce(st)),he=n.disabledAllPlugins,(u||p)&&(u&&(Qe.push.apply(Qe,n.log),Ve=n.log[u-1].id),p&&(Oe.push.apply(Oe,n.svrLog),Pe=n.svrLog[p-1].id),Be.forEach(function(e){e(Qe,Oe)})),n.lastLogId&&(Ve=n.lastLogId),n.lastSvrLogId&&(Pe=n.lastSvrLogId),n=n.data;var j,U=n.frames&&n.frames.length,B=n.composerTime;if(B&&y===t.curComposerReqId&&t.onTakeTimeChange&&(B.endTime&&(t.curComposerReqId=void 0),t.onTakeTimeChange(B)),U?(w.lastFrameId=n.frames[U-1].frameId,v.push.apply(v,n.frames)):n.lastFrameId&&(w.lastFrameId=n.lastFrameId),A){var R=n.socketStatus;R?(w.closed=void 0,R.sendStatus>-1&&(j=w.sendStatus!==R.sendStatus,w.sendStatus=R.sendStatus),R.receiveStatus>-1&&(j=j||w.receiveStatus!==R.receiveStatus,w.receiveStatus=R.receiveStatus)):w.closed||(j=!0,w.closed=!0)}n.lastId&&(oe=n.lastId),n.endId&&(ie=n.endId);var z=n.tunnelIps||\"\";if(!n.ids.length&&!n.newIds.length||Fe.clearNetwork){if((j||U)&&Ue.forEach(function(e){e()}),Object.keys(z).length){var Q;ze.forEach(function(e){var t=z[e.id];t&&(delete e.reqPlugin,e.realIp=t,Q=!0)}),Q&&ye.trigger(\"updateUI\")}}else{var O=n.newIds,H=[];if(n=n.data,ze.forEach(function(e){var t=n[e.id];t?(D(e,t),V(e),xe.postMessage(e)):(e.lost=!0,e.endTime||xe.postMessage(e));var r=z[e.id];r&&(delete e.reqPlugin,e.realIp=r)}),O.length){var F=s(),P=F.disabledExcludeText?null:d(F.excludeText),Y=F.disabledFilterText?null:d(F.filterText);t.curNewIdList=O.filter(function(e){var t=n[e];return!t||(xe.postMessage(t),t.fc&&(H.push(t),t.highlight=!0),P&&h(t,P)||Y&&!h(t,Y))?void 0:(V(t),ze.push(t),!0)}),H.length&&setTimeout(function(){H.forEach(function(e){delete e.highlight})},800)}ke.forEach(function(e){e(Fe)})}}})}te||(te=!0,e())}function j(e,t){if(e&&t){var n={};return Object.keys(e).forEach(function(r){n[t[r]||r]=e[r]}),n}}function U(){return de+\"/\"+lt++}function B(e){if(!e)return!1;if(e.useFrames)return!0;if(e.reqError||e.resError)return!1;var t=e.res.statusCode;return/^wss?:\\/\\//.test(e.url)?101==t:e.inspect&&200==t}function R(e){var t=e.indexOf(\"&\");return-1!==t&&(e=e.substring(0,t)),t=e.indexOf(\"!\"),-1!==t&&(e=e.substring(0,t)),\"@\"===e[0]&&(e=\"#\"+e.substring(1)),e.length>32?void 0:e}function z(e,t){var n=e.lastIndexOf(\"&custom\"+(t?\"1=\":\"2=\"));if(-1!==n){if(e=e.substring(n+9),n=e.indexOf(\"&\"),e=-1===n?e:e.substring(0,n),-1!==e.indexOf(\"%\"))try{return decodeURIComponent(e)}catch(r){}return e}}function Q(e){e.style=void 0;var n=e.rules&&e.rules.style;if(n=n&&n.list){n=\"&\"+n.map(function(e){return e=e.value||e.matcher,e.substring(e.indexOf(\"://\")+3)}).join(\"&\");var r,o,i,a=n.lastIndexOf(\"&color=\");-1!==a&&(r=R(n.substring(a+7)));var s=n.lastIndexOf(\"&fontStyle=\");-1!==s&&(o=R(n.substring(s+11)));var l=n.lastIndexOf(\"&bgColor=\");-1!==l&&(i=R(n.substring(l+9))),(r||o||i)&&(e.style={color:r,fontStyle:o,backgroundColor:i});var c=t.custom1Key,u=t.custom2Key;we.notEStr(c)||(e.custom1=z(n,!0)),we.notEStr(u)||(e.custom2=z(n))}}function O(e){var t=e&&(St.exec(e)||Lt.exec(e));if(!t){if(e){if(/\\b(?:chrome|crmo|crios)\\//i.test(e)||/^Chrome\\s/.test(e))return\"chrome\";if(/Version\\//.test(e)&&/Safari\\//.test(e))return\"safari\";if(/Windows NT|Microsoft NCSI|Win64|Win32|Windows 10|Windows 11/i.test(e))return\"windows\";if(/android/i.test(e))return\"android\";if(/HarmonyOS|HMSCore|huawei/i.test(e))return\"huawei\";if(/iPad/i.test(e)||/Macintosh/i.test(e)&&/Mobile/i.test(e))return\"ipad\";if(/iPhone|iPod/i.test(e))return\"iphone\";if(/Macintosh/i.test(e))return\"mac\"}return\"browser\"}switch(t=t[0].toLowerCase()){case\"micromessenger\":return\"wechat\";case\"brave/\":return\"brave\";case\"qq/\":return\"qq\";case\"firefox/\":case\"fxios/\":return\"firefox\";case\"cfnetwork/\":return\"cfnetwork\";case\"ciccwm/\":case\"zztapp\":return\"cicc\";case\"wework/\":case\"wxwork/\":return\"wework\";case\"amap_sdk\":return\"amap\";case\"%e6%94%af%e4%bb%98%e5%ae%9d/\":return\"alipay\";case\"electron/\":return\"electron\";case\"whistleclient/\":return\"whistle\";case\"%e6%b7%98%e5%ae%9d/\":return\"taobao\";case\"%e5%a4%a9%e7%8c%ab/\":return\"tmall\";case\"%e9%92%89%e9%92%89/\":return\"dingtalk\";case\"ucbrowser/\":case\"uc%e6%b5%8f%e8%a7%88%e5%99%a8/\":return\"uc\";case\"pinduoduo\":return\"pdd\";case\"jd4\":case\"jdmall\":return\"jd\";case\"edg/\":case\"edge/\":case\"edga/\":case\"edgios/\":return\"edge\";default:return t}}function H(e){if(e.fc)return void(e.appName=\"whistle\");var t=e.appName;t&&-1===!Dt.indexOf(t)||(e.appName=O(e.req.headers[\"user-agent\"]))}function F(e){var t=e&&Object.keys(e);if(t&&t.length)for(var n=0,r=t.length;r>n;n++){var o=e[t[n]],i=o&&o.list&&1===o.list.length&&o.list[0].matcher;if(o&&!kt[t[n]]&&\"enable://capture\"!==i&&\"enable://intercept\"!==i)return!0}return!1}function V(e){var t=e.url,n=e.req,r=e.res;e.method=n.method;var o=e.endTime,i=o?\"\":\"-\",a=r.headers||\"\";H(e),e.hostIp=r.ip||i,e[pt]=e[pt]||F(e.rules),e.clientIp=n.ip||\"127.0.0.1\",e.date=e.date||we.toLocaleString(new Date(e.startTime)),e.clientPort=n.port,e.serverPort=e.res.port,e.contentEncoding=(a[\"content-encoding\"]||\"\")+(e.res.hasGzipError?\" (Incorrect header)\":\"\");var s=null==n.size?i:n.size,l=null==r.size?i:r.size,c=we.getDisplaySize(s,n.unzipSize),u=we.getDisplaySize(l,r.unzipSize);e.body=\"\"===c&&\"\"===u?\"\":c+\", \"+u,e.bodySize=(n.size||0)+(r.size||0);var d=null==r.statusCode?i:r.statusCode;if(e.result=/^[1-9]/.test(d)&&parseInt(d,10)||d,e.type=(a[\"content-type\"]||\"\").split(\";\")[0].toLowerCase(),e.dns=e.request=e.response=e.download=e.time=i,e.dnsTime>0&&(e.dns=e.dnsTime-e.startTime+\"ms\",e.requestTime>0&&(e.request=e.requestTime-e.dnsTime+\"ms\"),e.responseTime>0&&(!e.requestTime||e.requestTime>e.responseTime?e.response=e.responseTime-e.dnsTime+\"ms\":e.response=e.responseTime-e.requestTime+\"ms\",o>0&&(e.download=o-e.responseTime+\"ms\",e.time=o-e.startTime+\"ms\"))),n._hasError=e.reqError,r._hasError=e.resError,n.rawHeaders=j(n.headers,n.rawHeaderNames),r.rawHeaders=j(r.headers,r.rawHeaderNames),r.rawTrailers=j(r.trailers,r.rawTrailerNames),Q(e),e.rules&&e.pipe&&(e.rules.pipe=e.pipe),e.path)e.useH2?e.protocol=\"H2\":\"H2\"===e.protocol&&(e.protocol=e.isHttps?\"HTTP\":we.getProtocol(t));else{e.isHttps?e.protocol=we.getTransProto(r)||we.getTransProto(n)||\"HTTP\":e.protocol=e.useH2?\"H2\":we.getProtocol(t),e.hostname=e.isHttps?\"Tunnel to\":we.getHost(t);var p=t.indexOf(\"://\");-1!==p?(p=t.indexOf(\"/\",p+3),e.path=-1===p?\"/\":t.substring(p)):e.path=t,e.path.length>Ge&&(e.shortPath=e.path.substring(0,Ge)+\"...\")}!e.useHttp||\"HTTPS\"!==e.protocol&&\"WSS\"!==e.protocol||(e.protocol=e.protocol+\" > \"+e.protocol.slice(0,-1)),!e.frames&&B(e)&&(e.frames=[])}function P(e){var t=e&&e.url;return t&&\"string\"==typeof t&&-1===t.indexOf(\"#\")?e.isHttps?-1===t.indexOf(\"/\")&&-1===t.indexOf(\"?\"):jt.test(t):!1}function Y(){return ze.length-ve.MAX_COUNT-1}function G(){return le?Y()>0||t.stopRefresh?-1:oe||\"0\":se?-2:\"\"}function W(e){return je.length?(e=e&&e.server)?(qe=0,t.setServerInfo&&t.setServerInfo(e),Z&&(Z.strictMode!=e.strictMode&&(Z.strictMode=e.strictMode,ye.trigger(\"updateStrictMode\")),(Z.version!==e.version||Z.latestVersion!==e.latestVersion||Z.latestClientVersion!==e.latestClientVersion)&&(Z.version=e.version,\nZ.latestVersion=e.latestVersion,Z.latestClientVersion=e.latestClientVersion,ye.trigger(\"updateVersion\",e))),Z&&Z.version==e.version&&Z.rulesMode===e.rulesMode&&Z.cmdName===e.cmdName&&Z.networkMode===e.networkMode&&Z.pluginsMode===e.pluginsMode&&Z.multiEnv===e.multiEnv&&Z.baseDir==e.baseDir&&Z.username==e.username&&Z.nodeVersion==e.nodeVersion&&Z.port==e.port&&Z.host==e.host&&Z.pid==e.pid&&Z.whistleId==e.whistleId&&Z.ipv6Only==e.ipv6Only&&Z.ipv4.sort().join()==e.ipv4.sort().join()&&Z.ipv6.sort().join()==e.ipv6.sort().join()?void(Z=e):(Z=e,void je.forEach(function(t){t(e)}))):(++qe,void(qe>=Je&&(Z=e,je.forEach(function(e){e(!1)})))):void(qe=0)}function X(e){return he||et[e.slice(0,-1)]}function _(e){var t=pe&&pe[e];return Array.isArray(t)||(t=[]),he?t:(Object.keys($e).forEach(function(n){var r=$e[n],o=r[e];if(o){var i=n.slice(0,-1);et[i]||o.forEach(function(e){e.title=i+\" extension menu\",e.mtime=r.mtime,e.priority=r.priority,e._key=n,e._urlPattern=we.toRegExp(e.urlPattern)||we.toRegExp(e.namePattern),t.push(e)})}}),t.length>1?t.sort(we.comparePlugin):t)}function q(e){return\"string\"==typeof e?e:JSON.stringify(e)}function J(e,n){var r=e&&e.whistleId,o=e&&e.hasWhistleToken;if(!o!=!t.hasWhistleToken&&(t.hasWhistleToken=o,ye.trigger(\"hasWhistleTokenChanged\",o)),r!==t.whistleId){if(n){if(fe)return e&&(e.whistleId=void 0),void(fe=!1)}else fe=!0;t.whistleId=r,ye.trigger(\"whistleIdChanged\",r)}}function K(e){var t=e&&e.whistleId;J(e,!0),t&&ut!==e.rulesMFlag&&(ut=e.rulesMFlag,ye.trigger(\"rulesMFlagChanged\",ut))}var Z,$,ee,te,ne,re,oe,ie,ae,se,le,ce,ue,de,pe,he,ge,fe,me,Ae=n(18),Me=n(201),we=n(202),ve=n(211),be=n(210),ye=n(199),xe=n(212),Te=n(206),Ce=xe.updateWorkers,Ne=Me.createCgi,Ie=5120,Ee=5120,De=t.MAX_FRAMES_LENGTH=256,Se=36e3,Le=Se+1e4,ke=[],je=[],Ue=[],Be=[],Re=[],ze=[],Qe=[],Oe=[],He=ve.setDataCenter,Fe=new ve(ze),Ve=-2,Pe=-2,Ye=1e6,Ge=1024,We=360,Xe=[],_e=0,qe=0,Je=4,Ke=18e4,Ze=1==be.get(\"onlyViewOwnData\"),$e={},et={},tt=[],nt=[],rt=[],ot=[],it=[],at=[],st=[],lt=0,ct={},ut=\"\",dt={timeout:Se,xhrFields:{withCredentials:!0},data:{}},pt=window.Symbol?window.Symbol(\"hasRules\"):\"__hasRules\";t.HAS_RULES_KEY=pt,t.enabledRulesCount=0,t.setComposerItem=function(e){ge=e},t.checkPluginUpdates=function(e,n){return t.whistleId?void n(!0):n()},t.clientIp=\"127.0.0.1\",t.MAX_INCLUDE_LEN=Ie,t.MAX_EXCLUDE_LEN=Ee,t.MAX_LOG_LENGTH=We-20,t.changeLogId=function(e){ce=e},t.getPort=function(){return ue},t.setDumpCount=function(e){_e=e>0?e:0},t.clearLogList=function(){Qe=[]},t.clearSvgLogList=function(){Oe=[]},t.setOnlyViewOwnData=function(e){Ze=e!==!1,be.set(\"onlyViewOwnData\",Ze?1:0)},t.isOnlyViewOwnData=function(){return Ze},t.filterIsEnabled=function(){if(Ze)return!0;var e=s();if(!(!e||e.disabledFilterText&&e.disabledExcludeText)){var t=!e.disabledFilterText&&e.filterText.trim();return t||!e.disabledExcludeText&&e.excludeText.trim()}},i(),Ae(window).on(\"hashchange\",i),t.setFilterText=a,t.getFilterText=s,t.setNetworkColumns=l,t.getNetworkColumns=c;var ht=/^(m|i|h|b|c|d|H):/,gt={m:\"method\",i:\"ip\",h:\"headers\",b:\"body\",c:\"body\",d:\"host\",H:\"host\"},ft=[],mt=Ae.extend({type:\"post\"},dt),At=Ae.extend({cache:!1},dt),Mt=Me({getData:\"cgi-bin/get-data\",getInitial:\"cgi-bin/init\"},At);t.createCgi=function(e,t,n){return Ne({url:e,mode:t?\"cancel\":null},n?mt:At)};var wt=Me({remove:\"cgi-bin/certs/remove\",active:{url:\"cgi-bin/certs/active\",contentType:\"application/json\"},upload:{url:\"cgi-bin/certs/upload\",contentType:\"application/json\"},all:{url:\"cgi-bin/certs/all\",type:\"get\"}},mt);t.certs=wt,t.uploadCerts=function(e,t){return\"string\"!=typeof e&&(e=JSON.stringify(e)),wt.upload(e,function(e,n){return e?void(\"function\"==typeof t?t(e):ye.trigger(\"showCustomCerts\")):we.showSystemError(n)})},t.values=Me({recycleList:{type:\"get\",url:\"cgi-bin/values/recycle/list\"},recycleView:{type:\"get\",url:\"cgi-bin/values/recycle/view\"},recycleRemove:\"cgi-bin/values/recycle/remove\",moveTo:{mode:\"chain\",url:\"cgi-bin/values/move-to\"},list:{type:\"get\",url:\"cgi-bin/values/list\"},add:\"cgi-bin/values/add\",remove:\"cgi-bin/values/remove\",rename:\"cgi-bin/values/rename\"},mt),t.plugins=Me({disablePlugin:\"cgi-bin/plugins/disable-plugin\",disableAllPlugins:\"cgi-bin/plugins/disable-all-plugins\",getRegistryList:\"cgi-bin/plugins/registry-list\",installPlugins:\"cgi-bin/plugins/install\",uninstallPlugins:\"cgi-bin/plugins/uninstall\",addRegistry:\"cgi-bin/plugins/add-registry\"},mt),t.installPluginsFromService=function(e,n){e&&t.whistleId&&(e=we.isString(e)?e.trim().split(/\\s*,\\s*/):Array.isArray(e)?e:[],e=e.map(function(e){return-1===e.indexOf(\"/\")?t.whistleId+\"/\"+e:e}).join(),e&&t.plugins.installPlugins({registry:/^https?:\\/\\/[^/]/.test(n)?n:\"\",plugins:e},we.showHandlePluginInfo))},t.rules=Me({disableAllRules:\"cgi-bin/rules/disable-all-rules\",recycleList:{type:\"get\",url:\"cgi-bin/rules/recycle/list\"},recycleView:{type:\"get\",url:\"cgi-bin/rules/recycle/view\"},recycleRemove:\"cgi-bin/rules/recycle/remove\",moveTo:{mode:\"chain\",url:\"cgi-bin/rules/move-to\"},getEnabledRules:{url:\"cgi-bin/rules/enabled\",mode:\"cancel\"},list:{type:\"get\",url:\"cgi-bin/rules/list\"},add:\"cgi-bin/rules/add\",disableDefault:\"cgi-bin/rules/disable-default\",enableDefault:\"cgi-bin/rules/enable-default\",remove:\"cgi-bin/rules/remove\",rename:\"cgi-bin/rules/rename\",select:\"cgi-bin/rules/select\",unselect:\"cgi-bin/rules/unselect\",allowMultipleChoice:{mode:\"ignore\",url:\"cgi-bin/rules/allow-multiple-choice\"},enableBackRulesFirst:{mode:\"ignore\",url:\"cgi-bin/rules/enable-back-rules-first\"},setSysHosts:\"cgi-bin/rules/set-sys-hosts\"},mt),t.log=Me({set:\"cgi-bin/log/set\"},mt);var vt=Ae.extend({type:\"post\",contentType:\"application/json\",processData:!1},dt);t.createCompose=f;var bt=f(),yt=f(!0);t.composeInner=function(e,t,n){return e.reqId=U(),m(e,t,n,yt)},t.createComposeInterrupt=function(){var e=f(!0);return function(t,n,r){return m(t,n,r,e)}},t.compose=function(e,t,n){return m(e,t,n,bt)},Ae.extend(t,Me({interceptHttpsConnects:\"cgi-bin/intercept-https-connects\",enableHttp2:\"cgi-bin/enable-http2\",abort:\"cgi-bin/abort\",setCustomColumn:\"cgi-bin/set-custom-column\",addRulesAndValues:{url:\"cgi-bin/add-rules-values\",contentType:\"application/json\"},createTempFile:{url:\"cgi-bin/temp/create\",contentType:\"application/json\"},saveSessions:{url:\"cgi-bin/saved/save\",contentType:\"application/json\"},removeSavedSessions:{url:\"cgi-bin/saved/remove\",contentType:\"application/json\"},setDnsOrder:\"cgi-bin/set-dns-order\",save:{url:\"cgi-bin/service/save\",contentType:\"application/json\"},login:\"cgi-bin/service/login\",logout:\"cgi-bin/service/logout\",updateClient:\"cgi-bin/update\"},mt)),Ae.extend(t,Me({donotShowAgain:\"cgi-bin/do-not-show-again\",checkUpdate:\"cgi-bin/check-update\",importRemote:\"cgi-bin/import-remote\",getHistory:\"cgi-bin/history\",getCookies:\"cgi-bin/cookies\",getTempFile:\"cgi-bin/temp/get\",getComposeData:\"cgi-bin/compose-data\",getSavedList:\"cgi-bin/saved/list\",getSavedSessions:\"cgi-bin/saved/sessions\"},At));var xt,Tt=t.getSavedList,Ct=0,Nt=-1;t.getSavedListSafe=A,t.socket=Ae.extend(Me({changeStatus:{mode:\"cancel\",url:\"cgi-bin/socket/change-status\"},abort:{mode:\"ignore\",url:\"cgi-bin/socket/abort\"},send:{mode:\"ignore\",url:\"cgi-bin/socket/data\"}},mt)),t.getReqTabs=function(){return tt},t.getResTabs=function(){return nt},t.getTabs=function(){return rt},t.getToolTabs=function(){return ot},t.getComTabs=function(){return it},t.getAccount=function(){return pe};var It=1500;t.getInitialData=function(e){if(!$){$=Ae.Deferred();var n=function o(){Mt.getInitial(function(e){if(!e)return setTimeout(o,1e3);b(),t.isCapture=!!e.interceptHttpsConnects;var n=e.server;ue=n&&n.port,pe=n&&n.account,K(n),me=n&&n.hasUpdater,t.version=n&&n.version,t.supportH2=e.supportH2,t.isWin=n&&n.isWin,r(e.rules),t.custom1=e.custom1,t.custom2=e.custom2,t.custom1Key=e.custom1Key,t.custom2Key=e.custom2Key,ee=e,de=e.clientId,dt.data.clientId=de,e.lastLogId&&(Ve=e.lastLogId),e.lastSvrLogId&&(Pe=e.lastSvrLogId),ne=e.curLogId,re=e.curSvrLogId,e.lastDataId&&(oe=e.lastDataId),t.whistleName=e.wName,t.account=e.account,t.disableInstaller=e.disableInstaller,t.upload=Me({importSessions:\"cgi-bin/sessions/import?clientId=\"+de,importRules:\"cgi-bin/rules/import?clientId=\"+de,importValues:\"cgi-bin/values/import?clientId=\"+de},Ae.extend({type:\"post\"},dt,{contentType:!1,processData:!1,timeout:Le})),$.resolve(e),e.clientIp&&(t.clientIp=e.clientIp),M(e)})};n()}$.done(e)};var Et=Date.now();t.getRawHeaders=j,window.getWhistlePageId=window.getWhistleClientId=function(){return de},t.getReqId=U,t.onComposeData=function(e,t){var n=we.isString(e)&&\"function\"==typeof t?e.indexOf(de+\"/\"):-1;if(!n&&(n=e.substring(de.length+1),!/[^\\d]/.test(n)&&n>=0&&lt>n)){var r=ct[e]||[];return-1===r.indexOf(t)&&r.push(t),ct[e]=r,!0}},t.offComposeData=function(e,t){var n=ct[e];if(n)if(t){var r=n.indexOf(t);-1!==r&&n.splice(r,1)}else delete ct[e]},t.getPageId=function(){return de},t.isFrames=B;var Dt=\"alipay,baidu,brave,chrome,mac,android,ipad,iphone,windows,crmo,crios,cicc,edge,electron,firefox,huawei,opera,jd,pdd,qq,safari,uc,wework,wechat,dingtalk,weibo\".split(\",\"),St=/w[ex]work\\/|Alipay|Brave\\/|%e6%b7%98%e5%ae%9d\\/|opera|%e6%94%af%e4%bb%98%e5%ae%9d\\/|%e5%a4%a9%e7%8c%ab\\/|uc%e6%b5%8f%e8%a7%88%e5%99%a8\\/|pinduoduo|%e9%92%89%e9%92%89\\/|UCBrowser\\/|dingtalk|jd(?:4|mall)|weibo|tmall|qq\\/|Firefox\\/|FxiOS\\/|ciccwm\\/|WhistleClient\\/|edg(?:e|ios|a)?\\/|zztapp|baidu/i,Lt=/MicroMessenger|taobao|amap_sdk|Electron\\/|CFNetwork\\/|cronet/i,kt={plugin:1,pac:1,reqWrite:1,resWrite:1,reqWriteRaw:1,resWriteRaw:1,responseFor:1,style:1,G:1,ignore:1},jt=/^(?:https?|wss?):\\/\\//;t.addNetworkList=function(e){if(Array.isArray(e)&&e.length){var n,r=[],o=[];e.forEach(function(e){if(e&&e.startTime>=0&&e.req&&e.req.headers&&e.res&&P(e)){var t=e.req;delete e.active,delete e.selected,delete e.hide,delete e.order,delete t.json,delete e.res.json,delete e.data,delete e.stopRecordFrames,delete e.pauseRecordFrames,we.isString(e.fwdHost)||delete e.fwdHost,Array.isArray(e.frames)&&(e.frames=e.frames.filter(function(e){return e&&(delete e.json,delete e.data),e})),e.lost=!0,e.importedData=!0,e.highlight=!0,e.id=e.startTime+\"-\"+ ++Ye,V(e),ze.push(e),r.push(e.id),o.push(e),n=!0,xe.postMessage(e)}}),n&&(ye.trigger(\"autoRefreshNetwork\"),setTimeout(function(){o.forEach(function(e){delete e.highlight})},800),t.curNewIdList=r,ke.forEach(function(e){e(Fe)}))}},t.overflowCount=Y,t.networkModal=Fe,t.isDiableCustomCerts=function(){return Z&&Z.dcc},t.isMultiEnv=function(){return Z&&(Z.multiEnv||Z.notHTTPS)},t.isPureProxy=function(){return Z&&Z.pureProxy},t.needEnableHttps=function(){return!t.isMultiEnv()&&!t.isCapture},t.isStrictMode=function(){return Z&&Z.strictMode||!1},t.getServerInfo=function(){return Z||\"\"},t.on=function(e,t){k(),\"data\"==e?\"function\"==typeof t&&(ke.push(t),t(Fe)):\"serverInfo\"==e?\"function\"==typeof t&&je.push(t):\"log\"==e?\"function\"==typeof t&&(Be.push(t),t(Qe,Oe)):\"plugins\"===e||\"settings\"===e||\"rules\"===e?\"function\"==typeof t&&Re.push(t):\"framesUpdate\"==e&&\"function\"==typeof t&&Ue.push(t)},t.networkModal=Fe,t.stopNetworkRecord=function(e){!e&&t.pauseRefresh?Fe.clearNetwork=!1:Fe.clearNetwork=!e,t.pauseRefresh=!1,t.stopRefresh=e},t.pauseNetworkRecord=function(){Fe.clearNetwork=!1,t.pauseRefresh=!0,t.stopRefresh=!0},t.pauseConsoleRecord=function(){t.stopConsoleRefresh=!1,t.pauseConsoleRefresh=!0},t.stopConsoleRecord=function(e){t.pauseConsoleRefresh=!1,t.stopConsoleRefresh=e},t.pauseServerLogRecord=function(){t.stopServerLogRefresh=!1,t.pauseServerLogRefresh=!0},t.stopServerLogRecord=function(e){t.pauseServerLogRefresh=!1,t.stopServerLogRefresh=e},t.getPlugin=function(e){return X(e)?null:$e[e]},t.getInstalledPlugins=function(){return Object.keys($e).sort(we.getPluginComparator($e)).map(function(e){var t=$e[e],n=!!X(e);return e=e.slice(0,-1),{active:!et[e],disabled:n,name:e,moduleName:t.moduleName,version:t.version}})},t.setDisabledPlugins=function(e){et=e},t.getNetworkMenus=function(){return _(\"networkMenus\")},t.getRulesMenus=function(){return _(\"rulesMenus\")},t.getValuesMenus=function(){return _(\"valuesMenus\")},t.getPluginsMenus=function(){return _(\"pluginsMenus\")},t.getPluginColumns=function(){return at},t.getPluginRegistry=function(){var e=[];return Object.keys($e).forEach(function(t){var n=$e[t].registry;n&&-1===e.indexOf(n)&&e.push(n)}),e};var Ut;t.setValuesModal=function(e){Ut=e},t.getValuesModal=function(){return Ut},t.getRulesModal=function(){return t.rulesModal},t.getDataKeys=function(){var e=[];return t.custom1Key&&e.push(\"custom1\"),t.custom2Key&&e.push(\"custom2\"),e.concat(Xe)},t.getRemoteData=function(e,n){var r={url:e};t.importRemote(r,function(e,t){if(!e)return we.showSystemError(t),n(!0);if(0!==e.ec)return Te.error(e.em||\"Error\"),n(!0);try{var r=e.body||e.value;return e=r&&JSON.parse(r),n(!1,e||{})}catch(o){Te.error(o.message)}n(!0)})},t.showLatestClientVersion=function(){return me?(t.updateClient(function(e,t){return e?void(e.ec&&Te.error(e.em||\"Update failed\")):we.showSystemError(t)}),!0):void 0},t.triggerWhistleIdChanged=J,t.getRulesMFlag=function(){return ut||\"\"},t.saveToService=function(e,n){t.whistleId&&t.save(q(e),n)},He(t),xe.setup(Fe)},function(e,t,n){\"use strict\";function r(e,t){function n(l,c,u){var d={url:\"function\"==typeof e?e():e};if(\"function\"==typeof l?(u=c,c=l,l=null):d.data=l,u=i.extend(!0,{},t,u,d),o){var p=u.mode;if(\"ignore\"==p)return;if(\"cancel\"==p)o.abort();else if(\"chain\"==p)return a.push([l,c,u])}var h=function(e,t,i){o=null,c&&c.call(this,e,t,i);var s=a.shift();s&&n.apply(r,s)};return u.success=function(e,t,n){h.call(this,e,n)},u.error=function(e,t){e&&t&&(e.errMsg=t),h.call(this,!1,e,t)},d=u,s&&\"string\"==typeof d.url&&(d=i.extend({},d),d.url+=(-1===d.url.indexOf(\"?\")?\"?\":\"&\")+\"authorization=\"+s),o=i.ajax(d)}var r=this;\"string\"==typeof e&&(e={url:e}),t=i.extend({dataType:\"json\"},t,e),e=e.url;var o,a=[];return n}function o(e,t){var n={};return Object.keys(e).forEach(function(o){n[o]=r(e[o],t)}),n}var i=n(18),a=n(202),s=a.getQuery().authorization;e.exports=o,e.exports.createCgi=r},function(e,t,n){\"use strict\";function r(e){if(\"0\"==e)return!0;if(!Wt.test(e))return!1;var t=\"-\"===RegExp.$1,n=RegExp.$2;return n.length<16?!0:(t?Gt:Yt)>=n}function o(e){return\"\\\\r\"===e?\"\\r\":\"\\n\"}function i(e){return e}function a(e){return e?\"/\"===e[0]?e:\"/\"+e:\"/\"}function s(e,t){if(e&&t){try{var n=e.__whistleServiceApi;n||\"function\"!=typeof e.getServiceApiForWhistle||(n=e.getServiceApiForWhistle(t)||{},e.__whistleServiceApi=n)}catch(r){}return n}}function l(e){return e&&Pt.test(e)}function c(e,t){return e==t?0:e>t?-1:1}function u(e,t){return c(e.priority,t.priority)||c(t.mtime,e.mtime)||(e._key>t._key?1:e._key==t._key?0:-1)}function d(e){return function(t,n){var r=e[t],o=e[n];return r._key=t,o._key=n,u(r,o)}}function p(e){return\"string\"==typeof e}function h(e){return p(e)?e:\"\"}function g(e){return e&&\"string\"==typeof e}function f(e){return\"boolean\"==typeof e}function m(e,t){t?setTimeout(function(){xt.trigger(\"showService\",e)},100):xt.trigger(\"showService\",e)}function A(e,t){try{for(var n=window.getSelection(),r=0,o=n.rangeCount;o>r;r++){var i=n.getRangeAt(r),a=i.getBoundingClientRect();if(e-=a.x,t-=a.y,e>=0&&t>=0&&e<=a.width&&t<=a.height)return n.toString()}n.removeAllRanges()}catch(s){}}function M(){mt=gt=ft=null,xt.trigger(\"stopDrag\")}function w(e,t){if(e&&\"function\"==typeof t&&\"string\"==typeof e&&(e=e.trim())){var n=Rt[e]=Rt[e]||[];-1==wt.inArray(t,n)&&n.push(t)}}function v(e,t){var n=Rt[e];if(n){if(\"function\"==typeof t){var r=wt.inArray(t,n);return void(-1!=r&&n.splice(r,1))}delete Rt[e]}}function b(e,t,n){if(e&&(t||\"\"!==t)){\"string\"==typeof t&&(t=t.split(\".\"));for(var r=0,o=t.length-1;o>=r;r++){var i=t[r];if(!(i in e))return n;if(e=e[i],r==o)return e;if(!e)return n}}return n}function y(e){var t=e.hostIp;if(!t)return e.serverIp;if(e.realIp)e.serverIp=e.realIp+\", \"+t,delete e.realIp;else if(!e.serverIp){var n=e.res||\"\";n.phost&&n.phost!=t&&(t=n.phost+\", \"+t);var r=de(b(n,\"headers.x-whistle-response-for\"));r&&(t=r!==t&&-1===r.trim().split(/\\s*,\\s*/).indexOf(t)?r+\", \"+t:r,e.serverIp=t.trim().split(/\\s*,\\s*/).filter(i).join(\", \"))}return e.serverIp||t}function x(e,t,n){return n=n||t.name,\"hostIp\"===n?y(e):t.key?b(e,t.key):e[n]}function T(e){return!(!e||\"false\"===e)}function C(e,t){e=e||{};var n=e.status,r=t?Ct.error:Nt.alert;if(!n)return r(\"timeout\"===e.errMsg?\"Request timeout\":\"Please check whether the proxy and server are available\");var o=e.responseText||nn[n];return o?r(\"[\"+n+\"] \"+String(o).substring(0,1024)):void r(\"[\"+n+\"] Unknown error, try again later\")}function N(e){return e&&\"string\"!=typeof e&&(e=e[\"content-type\"]||e.contentType),\"string\"==typeof e?e.split(\";\")[0].trim().toLowerCase():\"\"}function I(e){if(e=N(e)){if(-1!=e.indexOf(\"javascript\"))return\"JS\";if(-1!=e.indexOf(\"css\"))return\"CSS\";if(-1!=e.indexOf(\"html\"))return\"HTML\";if(-1!=e.indexOf(\"json\"))return\"JSON\";if(-1!=e.indexOf(\"xml\"))return\"XML\";if(-1!=e.indexOf(\"text/\"))return\"TEXT\";if(-1!=e.indexOf(\"image/\"))return\"IMG\"}return null}function E(e){return e?(e=I(e),e&&\"IMG\"!==e):!0}function D(e){if(!e)return\"\";var t=e.indexOf(\"://\");t=-1==t?0:t+3;var n=e.indexOf(\"/\",t);return e=-1==n?e.substring(t):e.substring(t,n),!e||-1===e.indexOf(\"?\")&&-1===e.indexOf(\"#\")||(e=e.replace(/[?#].*$/,\"\")),e}function S(e,t,n,r,o){var i={};return window.___hasFormData=!1,e&&(e=(e+\"\").trim())?(t=t||\"&\",n=n||\"=\",e.split(t).forEach(function(e){e=e.split(n);var t=e[0],a=e.slice(1).join(\"=\");if(t||a){var s=a,l=t;r==decodeURIComponent&&(r=de);try{a=r?r(s):a}catch(c){}try{t=r?r(l):t}catch(c){}if(!o&&t in i){var u=i[t];Array.isArray(u)?u.push(a):i[t]=[u,a]}else i[t]=a;window.___hasFormData=!0}}),i):i}function L(e,t,n){if(!e||\"string\"==typeof e)return e||\"\";var r=Object.keys(e),o=n?r.indexOf(\"content-encoding\"):-1;return-1!==o&&r.splice(o,1),r.map(function(n){var r=e[n];return n=t&&t[n]||n,Array.isArray(r)?r.map(function(e){return n+\": \"+e}).join(\"\\r\\n\"):n+\": \"+r}).join(\"\\r\\n\")}function k(e){var t=e.realUrl;return/^(?:http|wss)s?:\\/\\//.test(t)?t:e.url}function j(e){return\"string\"==typeof e?e.trim().toLowerCase():e}function U(e){var t=j(e&&e[\"content-encoding\"]||e);return\"gzip\"===t||\"br\"===t||\"deflate\"===t?t:null}function B(e){var t=e.indexOf(\"://\");return-1==t?e:e.substring(t+3)}function R(e){if(!g(e))return\"\";e=B(e);var t=e.indexOf(\"/\");return-1==t?\"/\":e.substring(t)}function z(e,t){if(At=!1,\"string\"==typeof e&&(e=e.trim())){if(t){if(!/({[\\w\\W]*}|\\[[\\w\\W]*\\])/.test(e))return;e===RegExp.$1?At=!0:e=RegExp.$1}try{return tn(e,t)}catch(n){}}}function Q(e){return\"number\"==typeof e}function O(e){var t=e.indexOf(\": \");-1===t&&(t=e.indexOf(\":\"),-1===t&&(t=e.indexOf(\"=\")));var n,i;if(-1!=t){if(n=e.substring(0,t).trim(),i=e.substring(t+1).trim()){var a=i[0],s=i[i.length-1];a===s?('\"'===a||\"'\"===a||\"`\"===a)&&(i=i.slice(1,-1),i&&\"`\"===a&&(i=i.replace(Qt,o))):r(i)&&(i=parseInt(i,10))}}else n=e.trim(),i=\"\";return{name:n,value:i}}function H(e){if(\"string\"!=typeof e||!(e=e.trim()))return null;var t;return e.split(/\\r\\n|\\n|\\r/).forEach(function(e){if(e=e.trim()){var n=O(e),r=n.name,o=n.value,i=!Array.isArray(r);if(i||r.length<=1)return i?t=t||{}:(r=r[0],t=t||[]),void(t[r]=o);t=t||(Q(r[0])?[]:{}),n=t;var a=r.length-1;r.forEach(function(e,t){if(t===a)return void(n[e]=o);var i=n[e];i&&\"object\"===(\"undefined\"==typeof i?\"undefined\":Mt(i))||(i=Q(r[t+1])?[]:{}),n[e]=i,n=i})}}),t||{}}function F(e,t){window._$hasBigNumberJson=!1;var n=z(e,!0);if(n||!e||!t)return n;try{return z(t(e),!0)}catch(r){}}function V(e){return e.statusCode?\"string\"==typeof e.statusMessage?e.statusMessage:nn[e.statusCode]||\"unknown\":\"\"}function P(e){return/^post$/i.test(e.method)&&/urlencoded/i.test(e.headers&&e.headers[\"content-type\"])}function Y(e){return void 0===e?\"\":e+\"\"}function G(e){Vt&&\"function\"==typeof window.customWhistleEditor&&window.customWhistleEditor(e)!==!1||xt.trigger(\"openEditor\",e)}function W(){return document.documentElement.getAttribute(\"data-theme\")||\"light\"}function X(e){return e&&(p(e.value)||p(e.base64))&&(e.isFile||g(e.name))?e:void 0}function _(e){return!Array.isArray(e)||e.length>2||!g(e[0])?void 0:{rules:e[0].substring(0,16e3),values:X(e[1])}}function q(e){if(e){var t=e.whistleTimes||\"\",n=new Date(t.startTime||e.startedDateTime).getTime();if(!isNaN(n)){var r=e.request||{},o=e.response||{},i=te(r.headers),a=te(o.headers),s=e.clientIPAddress||\"127.0.0.1\",l=e.serverIPAddress||\"\",c=rn.test(r.httpVersion||o.httpVersion),u=c?\"2.0\":\"1.1\",d=r.postData||\"\",p={method:r.method,ip:s,port:r.port,httpVersion:u,unzipSize:d.size,size:r.bodySize>0?r.bodySize:0,headers:i.headers,rawHeaderNames:i.rawHeaderNames,body:\"\"},h=d.base64||d.text;h&&(d.base64?p.base64=h:p.body=h);var g=o.content,f={httpVersion:u,statusCode:o.statusCode||o.status,statusMessage:o.statusText,unzipSize:g.size,size:o.bodySize>0?o.bodySize:0,headers:a.headers,rawHeaderNames:a.rawHeaderNames,ip:l,port:o.port,body:\"\"},m=o.content,A=m&&m.text;A&&(m.base64?f.base64=m.base64:\"IMG\"===I(m.mimeType)||A.length%4===0&&/^[a-z\\d+/]+={0,2}$/i.test(A)?f.base64=A:f.body=A);var M={useH2:c,startTime:n,ttfb:e.ttfb,frames:e.frames,url:r.url,realUrl:e.whistleRealUrl,req:p,res:f,customData:e.whistleCustomData,fwdHost:e.whistleFwdHost,sniPlugin:e.whistleSniPlugin,rules:e.whistleRules||{},captureError:e.whistleCaptureError,isHttps:e.whistleIsHttps,reqError:e.whistleReqError,resError:e.whistleResError,version:e.whistleVersion,nodeVersion:e.whistleNodeVersion};if(t&&t.startTime)M.dnsTime=t.dnsTime,M.requestTime=t.requestTime,M.responseTime=t.responseTime,M.endTime=t.endTime;else{var w=e.timings||{},v=Math.round(n+ne(e.time));n=Math.floor(n+ne(w.dns)),M.dnsTime=n,n=Math.floor(n+ne(w.connect)+ne(w.ssl)+ne(w.send)+ne(w.blocked)+ne(w.wait)),M.requestTime=n,n=Math.floor(n+ne(w.receive)),M.responseTime=n,M.endTime=Math.max(n,v)}return M}}}function J(e){return an[e]}function K(e,t){if(\"function\"==typeof e.find)return e.find(t);for(var n=0,r=e.length;r>n;n++){var o=e[n];if(t(o,n,e))return o}}function Z(e,t,n){var r=e.pageX,o=e.pageY,i=document.documentElement,a=i.clientWidth;return r+t-window.scrollX>=a&&(r=Math.max(r-t,window.scrollX+1)),o+n-window.scrollY>=i.clientHeight-25&&(o=Math.max(o-n,window.scrollY+1)),{top:o,left:r,marginRight:a-r}}function $(e){if(!e.closed&&e.frames){var t=e.frames[e.frames.length-1];t&&(t.closed||t.err)&&(e.closed=!0,e.lastErr=t.err)}return e.closed}function ee(e){return-1===e.indexOf(\"'\")?\"'\"+e+\"'\":'\"'+e.replace(/\"/g,'\\\\\"')+'\"'}function te(e){var t={},n={};return Array.isArray(e)&&e.forEach(function(e){var r=e.name,o=r.toLowerCase();t[o]=e.value,n[o]=r}),{headers:t,rawHeaderNames:n}}function ne(e){return e>0?e:0}function re(e,t,n){return n.test?!n.test(e):-1===t.toLowerCase().indexOf(n)}function oe(e,t){if(!t.key1)return\"\";var n=e;return ae(re(n,e,t.key1),t.key1Not)?\" hide\":t.key2&&ae(re(n,e,t.key2),t.key2Not)?\" hide\":t.key3&&ae(re(n,e,t.key3),t.key3Not)?\" hide\":\"\"}function ie(e){var t=e.toLocaleString();if(!cn.test(t))return t;var n=RegExp[\"$&\"],r=e.getTime()%1e3;return t.replace(n,n+\".\"+Ge(r))}function ae(e,t){return t?!e:e}function se(e){if(e&&un.test(e))try{return new RegExp(RegExp.$1,RegExp.$2)}catch(t){}}function le(e){return e>0?new Array(e+1).join(\"0\"):\"\"}function ce(e,t){return e=e.toString(16).toUpperCase(),le(t-e.length)+e}function ue(e){for(var t,n,r=e.length,o=Math.max(6,r.toString(16).length),i=[],a=Math.ceil(r/16),s=0;a>s;s++){var l=16*s,c=Math.min(16+l,r);t=ce(Math.max(c-16,0),o)+\"  \";for(var u=\"\";c>l;l++)n=e[l],t+=\" \"+ce(n,2),u+=n>31&&127>n||n>159?String.fromCharCode(n):\".\";i.push(t+new Array(3*(17-u.length)).join(\" \")+u)}return i.join(\"\\n\")}function de(e,t){if(!e||\"string\"!=typeof e)return\"\";var n=e.replace(gn,\" \");try{return decodeURIComponent(n)}catch(r){}if(!t&&dn&&hn.test(n))try{var o=[];if(n.replace(pn,function(e){e.length>1?o.push(parseInt(e.substring(1),16)):o.push(String.fromCharCode(e))}),!t(o))return dn.decode(new window.Uint8Array(o))}catch(r){}return e}function pe(e){try{return encodeURIComponent(e)}catch(t){}return e}function he(e){try{return Et(e)}catch(t){}return[]}function ge(e){var t=he(e),n={hex:ue(t)};if(!Tt(t))try{n.text=dn.decode(t)}catch(r){}if(!n.text)try{n.text=St(e)}catch(r){n.text=e}return n}function fe(e){var t=N(e.headers);return t&&\"IMG\"===I(t)?t:\"\"}function me(e){if(e)try{return ue(he(e))}catch(t){}return e}function Ae(e){return\"Closed\"+(e.code?\" (\"+e.code+\")\":\"\")}function Me(e,t){if(!e[mn]||!e[An]){if(!e.base64){var n=e.body||e.text;if((e.closed||e.err)&&(n=String(e.err||Ae(e))),n)try{n=String(n),e.base64=Lt(n),e[mn]=n,e[An]=me(e.base64)}catch(r){}finally{delete e.body,delete e.bin,delete e.text}if(!e.base64||e._hasError)return}var o=!t&&fe(e);if(o)e[mn]=\"data:\"+o+\";base64,\"+e.base64,e[An]=me(e.base64);else{var i=ge(e.base64);e[mn]=i.text,e[An]=i.hex}}}function we(e,t,n){if(null==e[Mn]){var r=ve(e,t);r=r&&F(r,n),e[Mn]=r?{json:r,isJSONText:At,str:(window._$hasBigNumberJson?yt:JSON).stringify(r,null,\"    \")}:\"\"}return e[Mn]}function ve(e,t){return Me(e,t),e[mn]||\"\"}function be(e){var t=e.headers&&e.headers[\"content-type\"];return wn.test(t)||vn.test(ve(e))?RegExp.$1.toUpperCase():\"UTF8\"}function ye(e){if(e){var t=e.res,n=I(t.headers),r=\"IMG\"===n;if(r||\"HTML\"===n){var o=e.url;o=/^((?:http|ws)s?:)?\\/\\//i.test(o)?RegExp.$1?o.replace(/^ws/,\"http\"):\"http:\"+o:\"http://\"+o;var i=r?\"UTF8\":be(t);return o+=(-1===o.indexOf(\"?\")?\"\":\"&\")+\"???WHISTLE_PREVIEW_CHARSET=\"+i,o+\"???#\"+(r?ve(t):t.base64)}}}function xe(e,t){try{var n=JSON.parse(e);if(n&&\"object\"===(\"undefined\"==typeof n?\"undefined\":Mt(n)))return n;!t&&Ct.error(\"Error: invalid JSON format\")}catch(r){!t&&Ct.error(\"Error: \"+r.message)}}function Te(e){var t={};return e=e.split(jt),e.forEach(function(e){var n=e.indexOf(\":\"),r=\"\";if(-1!=n){r=e.substring(n+1).trim();var o=e.substring(0,n).trim(),i=t[o];i?(Array.isArray(i)||(t[o]=i=[i]),i.push(r)):t[o]=r}}),t}function Ce(e){return\"string\"!=typeof e?!1:(e=e.toUpperCase(),!(\"GET\"===e||\"HEAD\"===e||\"OPTIONS\"===e||\"CONNECT\"===e))}function Ne(e,t,n){return e=parseInt(e,10)||0,t=parseInt(t,10)||0,e===t?0:e>t?yn-n:n-yn}function Ie(e){var t=e.indexOf(\"  \")+2;return e.substring(t,e.indexOf(\"  \",t)).trim()}function Ee(e){try{var t=window.parent.onWhistlePageChange;\"function\"==typeof t&&xn!==e&&(xn=e,t(e,location.href))}catch(n){}}function De(e){var t=location.hash.substring(1),n=t.indexOf(\"?\");t=-1===n?\"\":t.substring(n),location.hash=e+t,Ee(e)}function Se(e,t,n){var r,o=new FileReader,i=function(e,n){return r?void 0:(r=!0,e?(o.abort(),Nt.alert(e.message)):void t(n))},a=\"text\"===n;return o[a?\"readAsText\":\"readAsArrayBuffer\"](e),o.onerror=i,o.onabort=function(){i(new Error(\"Aborted\"))},o.onload=function(){var e=o.result;try{a||(e=new window.Uint8Array(e),e=\"base64\"===n?Dt(e):e),i(null,e)}catch(t){i(t)}},o}function Le(e){var t=e[\"\"],n=Object.keys(e);if(t=t&&(Array.isArray(t)?t:Array.isArray(t.list)?t.list:null),!t)return n;delete e[\"\"];var r=[];t=t.concat(n);for(var o=0,i=t.length;i>o;o++){var a=t[o];g(a)&&-1===r.indexOf(a)&&r.push(a)}return r}function ke(e){return e>=1024?(e=(e/1024).toFixed(2),1024>e?e+\"k\":(e=(e/1024).toFixed(2),1024>e?e+\"m\":(e/1024).toFixed(2)+\"G\")):e}function je(e){return e?(e/100).toFixed(2):\"0\"}function Ue(e,t,n){var r=e.length,o=t&&t.length;if(!r||!o)return-1;var i=t[0],a=e.indexOf(i,n||0);if(1===o)return a;for(var s=-1;-1!==a;){s=a;for(var l=0;o>l;l++)if(t[l]!==e[l+a]){s=-1,a=e.indexOf(i,a+1);break}if(-1!==s)return s}return s}function Be(e,t,n){var r=e.length,o=t.length,i=new window.Uint8Array(r+o+(n?n.length:0));return i.set(e),i.set(t,r),n&&i.set(n,r+o),i}function Re(e){try{return e=kt(e),Et(e)}catch(t){}return null}function ze(e){try{return Et(e)}catch(t){}return null}function Qe(e){try{e=St(Dt(e)).split(\"\\r\\n\")}catch(t){return}if(Dn.test(e[0])){var n={name:RegExp.$1||RegExp.$2,value:\"\"};return Sn.test(e[0].replace(RegExp[\"$&\"],\"\\n\"))&&(n.value=RegExp.$1||RegExp.$2),Ln.test(e[1])&&(n.type=RegExp.$1),n}}function Oe(e){var t=[];return e.value&&t.push(e.value),e.type&&t.push(e.type),t=t.join(\"; \"),e.name+(t?\"\\r\\x00(\"+t+\")\":\"\")}function He(e,t,n){for(var r=\"--\"+t,o=Re(r+\"\\r\\n\"),i=Re(\"\\r\\n\"+r),a=o.length,s=Ue(e,o),l=n?{}:[];s>=0;){s+=a;var c=Ue(e,En,s-2);if(-1===c)return l;var u=Ue(e,i,c+2);if(-1===u)return l;var d=e.slice(s,c);c+=4;var p=c>=u?\"\":e.slice(c,u);if(d=d&&Qe(d))if(n){var h=Oe(d),g=d.value;if(p)try{g=St(Dt(p))||\"\"}catch(f){g=\"[Binary data]\"}var m=l[h];null!=m?(Array.isArray(m)||(m=l[h]=[m]),m.push(g)):l[h]=g}else{if(d.type)d.data=p||jn;else try{d.value=p&&St(Dt(p))}catch(f){}l.push(d)}s=Ue(e,o,u+2)}return l}function Fe(e){var t='Content-Disposition: form-data; name=\"'+e.name+'\"',n=e.data;return n?(t+='; filename=\"'+e.value+'\"',e.type&&(t+=\"\\r\\nContent-Type: \"+e.type)):n=e.value&&Re(e.value),t?(t=Re(t+\"\\r\\n\\r\\n\"),n?Be(t,n,kn):t):void 0}function Ve(){return\"----WhistleUploadForm\"+Date.now().toString(16)+Math.floor(1e11*Math.random()).toString(16)}function Pe(e){var t,n=\"\",r=e.length;for(t=2;r>t;t+=3)n+=Un[e[t-2]>>2],n+=Un[(3&e[t-2])<<4|e[t-1]>>4],n+=Un[(15&e[t-1])<<2|e[t]>>6],n+=Un[63&e[t]];return t===r+1&&(n+=Un[e[t-2]>>2],n+=Un[(3&e[t-2])<<4],n+=\"==\"),t===r&&(n+=Un[e[t-2]>>2],n+=Un[(3&e[t-2])<<4|e[t-1]>>4],n+=Un[(15&e[t-1])<<2],n+=\"=\"),n}function Ye(e){return 10>e?\"0\"+e:e}function Ge(e){return e>99?e:e>9?\"0\"+e:\"00\"+e}function We(e){e=e||new Date;var t=[];return t.push(e.getFullYear()),t.push(Ye(e.getMonth()+1)),t.push(Ye(e.getDate())),t.push(Ye(e.getHours())),t.push(Ye(e.getMinutes())),t.push(Ye(e.getSeconds())),t.push(Ge(e.getMilliseconds())),t.join(\"\")}function Xe(e){e=S(e,/;\\s*/,null,null,!0);var t={httpOnly:!1,secure:!1};for(var n in e)switch(n.toLowerCase()){case\"domain\":t.domain=e[n];break;case\"path\":t.path=e[n];break;case\"expires\":t.expires=e[n];break;case\"max-age\":t[\"max-age\"]=e[n],t.maxAge=e[n],t.maxage=e[n];break;case\"httponly\":t.httpOnly=!0,t.httponly=!0;break;case\"secure\":t.secure=!0;break;case\"partitioned\":t.partitioned=!0;break;case\"samesite\":t.sameSite=e[n],t.samesite=e[n];break;default:t[0]||(t.name=n,t.value=e[n])}return t}function _e(e,t){var n=[];return e&&(t=t||Bn,Object.keys(e).forEach(function(r){var o=e[r];r=t[r]||r,Array.isArray(o)?o.forEach(function(e){n.push({name:r,value:e+\"\"})}):n.push({name:r,value:o+\"\"})})),n}function qe(e,t){return e=S(e,t),_e(e)}function Je(e){var t=e.headers||\"\";return{size:e.unzipSize||e.size||-1,mimeType:t[\"content-type\"]||\"none\",params:[],text:\"\"}}function Ke(e){var t,n=e.req,r=e.url,o=n.headers||\"\",i=qe(o.cookie,/;\\s*/),a=r.indexOf(\"?\"),s=-1===a?[]:qe(r.substring(a+1)),l=P(n);if(l){var c=ve(n,!0);t=t||Je(n),t.text=c,t.base64=n.base64,t.params=qe(c)}else n.base64?(t=t||Je(n),t.base64=n.base64,t.text=ve(n,!0)):n.body&&(t=t||Je(n),t.text=n.body);return{method:e.method,url:r,ip:n.ip,port:n.port,httpVersion:e.useH2?\"HTTP/2.0\":\"HTTP/1.1\",cookies:i,headers:_e(o,n.rawHeaderNames),queryString:s,postData:t,headersSize:-1,bodySize:n.size||-1,comment:\"\"}}function Ze(e){var t=e.res,n=t.headers||\"\",r=n[\"set-cookie\"];return r=r?Array.isArray(r)?r.map(Xe):[Xe(r)]:[],{statusCode:t.statusCode,status:parseInt(t.statusCode,10)||0,ip:t.ip,port:t.port,statusText:V(t),httpVersion:e.useH2?\"HTTP/2.0\":\"HTTP/1.1\",cookies:r,headers:_e(n,t.rawHeaderNames),content:{size:t.unzipSize||t.size||-1,mimeType:n[\"content-type\"]||\"\",base64:t.base64,text:ve(t)},redirectURL:n.location||\"\",headersSize:-1,bodySize:t.size||-1,comment:\"\"}}function $e(e){e.children&&(e.expand=!0,e.pExpand=!0,e.children.forEach($e))}function et(e){e.children&&(e.expand=!1,e.pExpand=!1,e.children.forEach(et))}function tt(e,t){e.children&&(e.pExpand=t,t=e.expand&&t,e.children.forEach(function(e){tt(e,t)}))}function nt(e){e.expand=!0,tt(e,!0)}function rt(e){e.expand=!1,tt(e,!1)}function ot(e){return e&&\"\\r\"===e[0]}function it(e,t){return e.regexp?ae(e.regexp.test(t),e.not):ae(-1!==t.toLowerCase().indexOf(e.keyword),e.not)}function at(e,t,n){if(null==e)return!1;var r=\"undefined\"==typeof e?\"undefined\":Mt(e),o=1===n,i=n>1;if(\"string\"===r||\"number\"===r||\"boolean\"===r)return!o&&it(t,String(e));if(\"object\"!==r)return!1;if(Array.isArray(e)){for(var a=[],s=e.length-1;s>=0;s--)!i&&it(t,s+\"\")||at(e[s],t,n)?a.push(s):e.splice(s,1);return e._idx=a.reverse(),e.length}return Object.keys(e).forEach(function(r){var a=!i&&it(t,r);return o&&a?!0:void(at(e[r],t,n)||a||delete e[r]);\n}),Object.keys(e).length}function st(e){return e>=1024?e+\" (\"+ke(e)+\")\":e}function lt(e){var t=\"string\"==typeof e?e:e.moduleName;return t.substring(t.lastIndexOf(\".\")+1)}function ct(e,t){if(/^(?:https?:\\/\\/|data:image\\/)/.test(t))return t;e=lt(e);var n=\"plugin.\"+e;return 0===t.indexOf(\"whistle.\"+e)||0===t.indexOf(n)?t:n+\"/\"+t}function ut(e,t,n){return e=e&&e.replace(Fn,\"\").trim(),e&&-1!==e.indexOf(\"```\")?e.replace(Vn,function(e,r,o,i){return t&&null==t[o]&&(t[o]=i||\"\",n&&(n[o]=e.trim())),\"\"}):e}function dt(e){return e&&-1!==e.indexOf(\"#\")?e.replace(Ut,\"\").trim():e}function pt(e){return e.replace(Pn,function(e,t){return t.replace(gn,\" \")})}function ht(e){var t=/^zh-/i.test(navigator.language||navigator.userLanguage)?\"/\":\"/en/\";return\"https://wproxy.org\"+t+(e?\"docs/\":\"\")+(e||\"\")}var gt,ft,mt,At,Mt=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},wt=n(18),vt=n(1),bt=n(2).Base64,yt=n(203),xt=n(199),Tt=n(3),Ct=n(206),Nt=n(209),It=n(210),Et=vt.toByteArray,Dt=vt.fromByteArray,St=bt.decode,Lt=bt.encode,kt=bt.toBase64,jt=/\\r\\n|\\r|\\n/g,Ut=/#[^\\r\\n]*/g,Bt=/[:\\[][\\s\\n\\r]*-?[\\d.]{16,}[\\s\\n\\r]*[,\\}\\]]/,Rt={},zt=0,Qt=/\\\\n|\\\\r/g,Ot=[\"fatal\",\"error\",\"warn\",\"info\",\"debug\"],Ht=73728,Ft=(window.location.search||\"\").substring(1),Vt=-1!==Ft.indexOf(\"useCustomEditor\"),Pt=/^\\s*(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])\\s*$/,Yt=(Number.MAX_SAFE_INTEGER||\"9007199254740991\")+\"\",Gt=Math.abs(Number.MIN_SAFE_INTEGER||\"9007199254740991\")+\"\",Wt=/^([+-]?)([1-9]\\d{0,15})$/,Xt=\"# (From \",_t=Xt.length,qt=\"service/\";t.SOURCE_SEP=Xt,t.CRLF_RE=jt,t.isElectron=/Electron\\//i.test(window.navigator.userAgent),t.EDITOR_THEMES=[\"default\",\"neat\",\"elegant\",\"erlang-dark\",\"night\",\"monokai\",\"cobalt\",\"eclipse\",\"rubyblue\",\"lesser-dark\",\"xq-dark\",\"xq-light\",\"ambiance\",\"blackboard\",\"vibrant-ink\",\"solarized dark\",\"solarized light\",\"twilight\",\"midnight\"],t.noop=i,t.getServiceApi=s,t.getServiceUrl=function(e,t,n){var r=s(e,n),o=r;if(!r)try{o=-1!==e.location.href.indexOf(qt)&&\"complete\"===e.document.readyState}catch(i){}return!t&&r?null:qt+(o&&!r?\"?t=\"+Date.now():\"\")+\"#\"+a(t)},t.getQuery=function(){return\"string\"==typeof Ft&&(Ft=S(Ft)),Ft},t.likeJson=l,t.compare=c,t.comparePlugin=u,t.getPluginComparator=d,t.isString=p,t.getString=h,t.notEStr=g,t.isBool=f,t.parseLogs=function(e){if(\"string\"==typeof e)try{e=JSON.parse(e)}catch(t){}if(Array.isArray(e)){for(var n,r=0,o=0,i=e.length;i>o;o++){var a=e[o];if(a&&void 0!==a.text&&(n=n||[],a.id=++zt,a.date=a.date>0?a.date:0,g(a.level)?(a.level=a.level.toLowerCase(),-1===Ot.indexOf(a.level)&&(a.level=\"info\")):a.level=\"info\",n.push(a),++r>=100))return n}return n}},t.copyText=function(e,t){var n=wt(\"#copyTextBtn\");n.attr(\"data-clipboard-text\",e),n.removeClass().addClass(\"w-copy-text\"+(t?\"-with-tips\":\"\")),n.trigger(\"click\")},t.preventDefault=function(e){8==e.keyCode&&e.preventDefault()},t.preventBlur=function(e){e.preventDefault()},t.showService=m,t.hideService=function(){xt.trigger(\"hideService\")},t.getSelectedText=A;var Jt=[\"local.whistlejs.com\",\"local.wproxy.org\",\"rootca.pro\"];t.getCAHash=function(e,t){var n=e&&e.ipv4,r=e&&e.port||8899,o=[r],i=0;if(Array.isArray(n)&&n.forEach(function(e){e&&\"string\"==typeof e&&(o.push(e),t&&t.push(e),i+=e.length+1)}),-1===Jt.indexOf(location.hostname)){var a=location.href.replace(/[?#].*$/,\"\").replace(),s=a.lastIndexOf(\"/\");s&&(a=a.substring(0,s)),t&&t.push(a),a=encodeURIComponent(a),i+=a.length+1,o.push(a)}return i&&120>=i?\"#p=\"+o.join():\"\"},t.download=function(e,t){var n=document.createElement(\"a\");document.body.appendChild(n),n.style=\"display: none\";var r=new Blob([JSON.stringify(e)],{type:\"octet/stream\"}),o=window.URL.createObjectURL(r);n.href=o,n.download=t||\"data_\"+We()+\".txt\",n.click(),window.URL.revokeObjectURL(o),document.body.removeChild(n)};var Kt=[{name:\"Copy\"},{name:\"Copy Key\"},{name:\"Copy Value\"}];t.handlePropsContextMenu=function(e,t){var n=wt(e.target),r=n.closest(\"tr\");if(r.length){var o=A(e.clientX,e.clientY),a=r.attr(\"data-name\"),s=r.attr(\"data-value\");if(o||a||s){Kt[0].hide=!o,Kt[1].hide=!a,Kt[2].hide=!s,Kt[0].copyText=o,Kt[1].copyText=a,Kt[2].copyText=s;var l=10+(o?30:0)+(a?30:0)+(s?30:0),c=Z(e,110,l),u=Kt;n.closest(\"th\").length||(u=u.map(i),u[1]=Kt[2],u[2]=Kt[1]),c.list=u,e.preventDefault(),t.show(c)}}};var Zt=/[^\\da-f]+/gi;t.getBase64FromHexText=function(e){if(e=e&&e.trim().replace(Zt,\"\"),!e)return\"\";var t=e.length;t%2&&(e+=\"0\");for(var n=[],r=0;t>r;r+=2)n.push(parseInt(e.substring(r,r+2),16));try{return Dt(n)}catch(o){}return\"\"},wt(document).on(\"mousedown\",function(e){M();var t=wt(e.target);Object.keys(Rt).some(function(e){return gt=t.closest(e),gt.length?(mt=Rt[e],!0):void(gt=null)}),gt&&mt&&(ft=e,e.preventDefault())}).on(\"mousemove\",function(e){gt&&(mt.forEach(function(t){t(gt,e.clientX-ft.clientX,e.clientY-ft.clientY,ft.clientX,ft.clientY)}),ft=e)}).on(\"mouseup\",M).on(\"mouseout\",function(e){!e.relatedTarget&&M()}),t.addDragEvent=w,t.removeDragEvent=v;var $t=1;t.getKey=function(){return\"w-reactkey-\"+$t++},t.getProperty=b,t.getCellValue=x,t.getServerIp=y,t.getBool=T,t.shouldComponentUpdate=function(e){var t=T(this.props.hide);return t!=T(e.hide)||!t},t.stopPropagation=function(e){e.stopPropagation()},t.showSystemError=C,t.getClasses=function(e){var t=[];for(var n in e)e[n]&&t.push(n);return t.join(\" \")},t.getRawType=N,t.getExtension=function(e){var t,n=I(e);return\"XML\"===n&&(t=N(e),0===t.indexOf(\"image/\")&&(n=\"IMG\")),\"IMG\"!==n?n?\".\"+(\"TEXT\"===n?\"txt\":n.toLowerCase()):\"\":(t=t||N(e),t=t.substring(t.indexOf(\"/\")+1).toLowerCase(),/\\w+/.test(t)?\".\"+RegExp[\"$&\"]:\"\")},t.getContentType=I,t.isText=E;var en=/^head$/i;t.hasBody=function(e,t){if(t&&en.test(t.method))return!1;var n=e.statusCode;return!(204==n||n>=300&&400>n||n>=100&&199>=n)},t.getHostname=function(e){e=D(e);var t=e.lastIndexOf(\":\");return-1==t?e:e.substring(0,t)},t.getHost=D,t.getProtocol=function(e){var t=e.indexOf(\"://\");return-1==t?\"TUNNEL\":e.substring(0,t).toUpperCase()},t.getTransProto=function(e){var t=e.headers,n=t&&t[\"x-whistle-transport-protocol\"];if(n&&\"string\"==typeof n&&!(n.length>33)){try{return decodeURIComponent(n).toUpperCase()}catch(r){}return n.toUpperCase()}},t.ensureVisible=function(e,t,n){e=wt(e),t=wt(t);var r=e.offset().top-t.offset().top;if(r){var o,i=t[0].offsetHeight,a=e[0].offsetHeight;if(0>r)return void(n?(o=Math.ceil((i-a)/2),o=Math.max(0,t.scrollTop()+r-o),t.scrollTop(o)):t.scrollTop(t.scrollTop()+r-2));r+=a-i,r>0&&(n?(o=Math.ceil(i/2),o=Math.max(0,t.scrollTop()+r+o),t.scrollTop(o)):t.scrollTop(t.scrollTop()+r+2))}},t.parseQueryString=S,t.objectToString=L,t.getRealUrl=k,t.getReqRawHeaders=function(e){var t=e.req,n=k(e),r=L(t.headers,t.rawHeaderNames);return[t.method,\"CONNECT\"==t.method?r.host:R(n),\"HTTP/\"+(t.httpVersion||\"1.1\")].join(\" \")+\"\\r\\n\"+r},t.getResRawHeaders=function(e){var t=e.res||\"\",n=L(t.headers,t.rawHeaderNames),r=t.statusCode,o=\"captureError\"===r?\"(Most likely caused by SSL pinning)\":V(t);return[\"HTTP/\"+(e.req.httpVersion||\"1.1\"),r,o].join(\" \")+\"\\r\\n\"+n},t.getOriginalReqHeaders=function(e,t){var n=e.req,r=wt.extend({},n.headers,t||e.rulesHeaders,!0);return e.clientId&&!r[\"x-whistle-client-id\"]&&(r[\"x-whistle-client-id\"]=e.clientId),e.h2Id&&(r[\"x-whistle-alpn-protocol\"]=e.h2Id),U(r)&&delete r[\"content-encoding\"],L(r,n.rawHeaderNames)},t.removeProtocol=B,t.getPath=R;var tn=function(e,t){var n;return t&&Bt.test(e)?(window._$hasBigNumberJson=!0,n=yt.parse(e)):n=JSON.parse(e),\"object\"===(\"undefined\"==typeof n?\"undefined\":Mt(n))?n:null};t.parseJSON=z,t.parseJSON2=function(e){return z(e)||H(e)},t.unique=function(e,t){var n=[];if(t)for(var r=e.length-1;r>=0;r--){var o=e[r];-1==n.indexOf(o)&&n.unshift(o)}else e.forEach(function(e){-1==n.indexOf(e)&&n.push(e)});return n},t.getFilename=function(e,t){var n=e.url;if(e.isHttps)return n;if(t&&e.filename)return e.filename;if(e.simplePath)return e.simplePath;n=B(n.replace(/[?#].*/,\"\"));var r=n.lastIndexOf(\"/\"),o=-1!=r&&n.substring(r+1);return o||(t?(n=n.substring(0,r),r=n.lastIndexOf(\"/\"),o=-1===r?n:n.substring(r+1)):o=\"/\"),e[t?\"filename\":\"simplePath\"]=o,o};var nn={100:\"Continue\",101:\"Switching Protocols\",102:\"Processing\",200:\"OK\",201:\"Created\",202:\"Accepted\",203:\"Non-Authoritative Information\",204:\"No Content\",205:\"Reset Content\",206:\"Partial Content\",207:\"Multi-Status\",208:\"Already Reported\",226:\"IM Used\",300:\"Multiple Choices\",301:\"Moved Permanently\",302:\"Moved Temporarily\",303:\"See Other\",304:\"Not Modified\",305:\"Use Proxy\",307:\"Temporary Redirect\",308:\"Permanent Redirect\",400:\"Bad Request\",401:\"Unauthorized\",402:\"Payment Required\",403:\"Forbidden\",404:\"Not Found\",405:\"Method Not Allowed\",406:\"Not Acceptable\",407:\"Proxy Authentication Required\",408:\"Request Time-out\",409:\"Conflict\",410:\"Gone\",411:\"Length Required\",412:\"Precondition Failed\",413:\"Request Entity Too Large\",414:\"Request-URI Too Large\",415:\"Unsupported Media Type\",416:\"Requested Range Not Satisfiable\",417:\"Expectation Failed\",418:\"I'm a teapot\",422:\"Unprocessable Entity\",423:\"Locked\",424:\"Failed Dependency\",425:\"Unordered Collection\",426:\"Upgrade Required\",428:\"Precondition Required\",429:\"Too Many Requests\",431:\"Request Header Fields Too Large\",500:\"Internal Server Error\",501:\"Not Implemented\",502:\"Bad Gateway\",503:\"Service Unavailable\",504:\"Gateway Time-out\",505:\"HTTP Version Not Supported\",506:\"Variant Also Negotiates\",507:\"Insufficient Storage\",509:\"Bandwidth Limit Exceeded\",510:\"Not Extended\",511:\"Network Authentication Required\"};t.getStatusMessage=V,t.isUrlEncoded=P,t.toString=Y,t.getValue=function(e,t){t=t.split(\".\");var n,r=t.length;if(r>1){n=e;for(var o=0;r>o;o++){var i=n;if(n=i[t[o]],null==n&&(n=i[t[o].toLowerCase()],null==n))break}}else n=e[t[0]];return null==n?\"\":(n=String(n),n.length>1690?n.substring(0,1680)+\"...\":n)},t.openEditor=G,t.openInNewWin=function(e){var t=window.open(\"editor.html\");t.getValue=function(){return e},t.getWhistleTheme=W,t.setValue&&t.setValue(e)},t.pluginIsDisabled=function(e,t){var n=e.disabledPlugins||{};return!e.ndp&&(e.disabledAllPlugins||n[t])};var rn=/http\\/2\\.0/i;t.harToSession=q,t.handleImportData=function(e,t){if(e){if(\"setNetworkSettings\"===e.type)return xt.trigger(\"setNetworkSettings\",e),!0;if(\"setRulesSettings\"===e.type)return xt.trigger(\"setRulesSettings\",e),!0;if(\"setValuesSettings\"===e.type)return xt.trigger(\"setValuesSettings\",e),!0;if(\"setComposerData\"===e.type)return xt.trigger(\"setComposerData\",e),!0}var n=_(e);if(n)xt.trigger(\"showRulesDialog\",n);else if(e&&\"composerImportFile\"===t)if(Array.isArray(e))xt.trigger(\"composer\",e[0]);else{var r=e.log&&e.log.entries;if(Array.isArray(r)&&r.length){var o=q(r[0]);o&&xt.trigger(\"composer\",o)}}return n};var on=/['<> \"&]/g,an={'\"':\"&quot;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"&\":\"&amp;\",\" \":\"&nbsp;\",\"'\":\"&#39;\"},sn=/\\r?\\n/g,ln=/\\s/g;t.escape=function(e){return e?(e=(e+\"\").replace(on,J),e.replace(sn,\"<br />\").replace(ln,\"&nbsp;\")):e},t.findArray=K,t.isFocusEditor=function(){var e=document.activeElement,t=e&&e.nodeName;return\"INPUT\"!==t&&\"TEXTAREA\"!==t?!1:!e.readOnly&&!e.disabled},t.getMenuPosition=Z,t.socketIsClosed=$,t.canAbort=function(e){return e.lost||e.endTime?e.reqError||e.resError?!1:!!e.frames&&!$(e):!0},t.asCURL=function(e){if(!e)return e;var t=e.req,n=e.url.replace(/^ws/,\"http\"),r=t.method,o=[\"curl\",\"-X\",r,JSON.stringify(n)],i=t.headers,a=t.rawHeaderNames||{};Object.keys(i).forEach(function(e){\"content-length\"!==e&&\"content-encoding\"!==e&&\"accept-encoding\"!==e&&o.push(\"-H\",JSON.stringify((a[e]||e)+\": \"+i[e]))});var s=ve(t,!0);return s&&(s.length<=Ht||E(t.headers)||P(t))&&o.push(\"--data-raw\",ee(s)),o.join(\" \").replace(/!/g,\"\\\\!\")},t.parseHeadersFromHar=te,t.getTimeFromHar=ne,t.parseKeyword=function(e){e=e.split(/\\s+/);for(var t=\"\",n=0,r=0,o=e.length;o>r;r++){var i=e[r],a=\"!\"===i[0];a&&(i=i.substring(1)),i&&(0===i.indexOf(\"level:\")?t&&t.level||(i=i.substring(6),\"!\"===i[0]&&(a=!0,i=i.substring(1)),i&&(t=t||{},t.level=i.toLowerCase(),t.levelNot=a)):i&&3>n&&(++n,t=t||{},t[\"key\"+n]=se(i)||i.toLowerCase(),t[\"key\"+n+\"Not\"]=a))}return t},t.checkKey=re,t.hasVisibleLog=function(e){var t=e.length;if(!t)return!1;for(var n=0;t>n;n++)if(!e[n].hide)return!0},t.trimLogList=function(e,t,n){var r=e.length;if(n)for(var o=0;t>0&&r>o;)e[o].hide?(--r,--t,e.splice(o,1)):++o;return t>0&&e.splice(0,t),e};var cn=/\\b\\d\\d?:\\d\\d?:\\d\\d?\\b/;t.toLocaleString=ie,t.filterLogList=function(e,t,n){if(!e)return e;var r,o,i;return n&&(r={},o=[],i=function(e){r[e.id]||(o.push(e),r[e.id]=1)}),t?(e.forEach(function(e){var n=t.level;if(n&&ae(e.level!==n,t.levelNot))e.hide=!0;else{var r=\"Date: \"+ie(new Date(e.date))+e.logId+\"\\r\\n\"+e.text;e.hide=oe(r,t)}i&&i(e)}),o||e):(e.forEach(function(e){e.hide=!1,i&&i(e)}),o||e)},t.scrollAtBottom=function(e,t){return e.scrollTop+e.offsetHeight+5>t.offsetHeight},t.triggerListChange=function(e,t){xt.trigger(e+\"Change\",t);try{var n=window.parent[\"rules\"===e?\"onWhistleRulesChange\":\"onWhistleValuesChange\"];\"function\"==typeof n&&n(t)}catch(r){}};var un=/^\\/(.+)\\/([miu]{0,3})$/;t.toRegExp=se;var dn,pn=/%[a-f\\d]{2}|./gi,hn=/%[a-f\\d]{2}/i,gn=/\\+/g;if(window.TextDecoder)try{dn=new TextDecoder(\"GB18030\")}catch(fn){}t.decodeURIComponentSafe=de,t.encodeURIComponent=pe,t.base64Decode=St,t.decodeBase64=ge,t.joinBase64=function(e,t){return e&&t?(e=Et(e),t=Et(t),e=Be(e,t),Dt(e)):e||t};var mn=\"$body\",An=\"$hex\",Mn=\"$json\";window.Symbol&&(mn=window.Symbol[\"for\"](mn),An=window.Symbol[\"for\"](An),Mn=window.Symbol[\"for\"](Mn)),t.BODY_KEY=mn,t.HEX_KEY=An,t.JSON_KEY=Mn,t.getHexFromBase64=me,t.getJson=we,t.getJsonStr=function(e,t,n){var r=we(e,t,n);return r&&r.str},t.getBody=ve,t.getHex=function(e){return Me(e),e[An]||\"\"};var wn=/charset=([\\w-]+)/i,vn=/<meta\\s[^>]*\\bcharset=(?:'|\")?([\\w-]+)[^>]*>/i;t.getPreviewUrl=ye,t.openPreview=function(e){var t=ye(e);t&&window.open(t)},t.parseRawJson=xe,t.parseHeaders=function(e){return e=\"string\"==typeof e?e.trim():null,e?xe(e,!0)||Te(e):{}},t.hasRequestBody=Ce;var bn=/([^\\x00-\\xFF]|[\\r\\n%])/g;t.encodeNonLatin1Char=function(e){return e&&\"string\"==typeof e?e.replace(bn,pe):\"\"};var yn=3;t.compareVersion=function(e,t){if(e===t||!e||\"string\"!=typeof e)return 0;if(!t||\"string\"!=typeof t)return 3;e=e.split(\".\"),t=t.split(\".\");for(var n=0;3>n;n++){var r=Ne(e[n],t[n],n);if(r)return Math.max(r,0)}if(e=e[2],t=t[2],!e||!t)return 0;var o=e.indexOf(\"-\"),i=t.indexOf(\"-\"),a=-1===o?\"\":e.substring(o),s=-1===i?\"\":t.substring(i);return a===s?0:a&&s?a>s?1:0:a?0:1},t.getHexText=function(e){return e?e.split(\"\\n\").map(Ie).join(\"\\n\"):\"\"};var xn;t.triggerPageChange=Ee;var Tn;t.triggerRulesActiveChange=function(e){if(Tn!==e){Tn=e;try{var t=window.parent.onWhistleRulesActiveChange;\"function\"==typeof t&&t(e,location.href)}catch(n){}}};var Cn;t.triggerValuesActiveChange=function(e){if(Cn!==e){Cn=e;try{var t=window.parent.onWhistleValuesActiveChange;\"function\"==typeof t&&t(e,location.href)}catch(n){}}},t.changePageName=De,t.getTempName=function(){return Date.now()+\"\"+Math.floor(1e4*Math.random())},t.readFile=Se,t.readFileAsBase64=function(e,t){return Se(e,t,\"base64\")},t.readFileAsText=function(e,t){return Se(e,t,\"text\")},t.addPluginMenus=function(e,t,n,r,o,i){var a=e.list=t,s=a.length;if(s){e.hide=!1;for(var l=r,c=o||i,u=0;s>u;u++){var d=a[u],p=d._urlPattern;if(d.required||d.requiredTreeNode){var h=r&&(!d.requiredTreeNode||!o);h||!p||c&&p.test(c)||(h=!0),d.disabled=h,h||(l=!1)}else!p||c&&p.test(c)?l=d.disabled=!1:d.disabled=!0}var g=s-2;e.top=g>0?Math.min(n,g):void 0,e.disabled=l}else e.hide=!0},t.getText=function(e){if(e&&\"object\"===(\"undefined\"==typeof e?\"undefined\":Mt(e)))try{return JSON.stringify(e,null,\"  \")}catch(t){}return null==e?\"\":String(e)},t.parseImportData=function(e,t,n){var r,o=[],i=function(e,i){if(null!=i){if(n)if(\"object\"===(\"undefined\"==typeof i?\"undefined\":Mt(i)))try{i=JSON.stringify(i,null,\"  \")}catch(a){return}else i+=\"\";if(\"string\"==typeof i){var s,l=t&&t.get(e);l&&(s=l.value&&l.value!=i,r=r||s),o.push({name:e,value:i,isConflict:s})}}};if(Array.isArray(e)){var a={};e.forEach(function(e){var t=e&&e.name;if(t&&\"string\"==typeof t&&!a[t]){var n=ot(t)?\"\":null==e.value?e.content:e.value;a[t]=1,i(t,n)}})}else Le(e).forEach(function(t){t&&i(t,e[t])});return o.hasConflict=r,o},t.getSize=ke,t.getQps=je,t.toBase64=kt,t.base64ToByteArray=ze;var Nn=/^\\s*multipart\\//i,In=/boundary=(?:\"([^\"]+)\"|([^;]+))/i,En=Re(\"\\r\\n\\r\\n\"),Dn=/name=(?:\"([^\"]+)\"|([^;]+))/i,Sn=/filename=(?:\"([^\"]+)\"|([^;]+))/i,Ln=/^\\s*content-type:\\s*([^\\s]+)/i,kn=Re(\"\\r\\n\"),jn=Re(\"\");t.EMPTY_BUF=jn,t.isUploadForm=function(e){var t=e.headers&&e.headers[\"content-type\"];return Nn.test(t)},t.parseUploadBody=function(e,t){if(e.base64){var n=e.headers&&e.headers[\"content-type\"];if(In.test(n)){var r=RegExp.$1||RegExp.$2,o=ze(e.base64);return o&&He(o,r,t)}}};var Un=[\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\",\"Q\",\"R\",\"S\",\"T\",\"U\",\"V\",\"W\",\"X\",\"Y\",\"Z\",\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\",\"h\",\"i\",\"j\",\"k\",\"l\",\"m\",\"n\",\"o\",\"p\",\"q\",\"r\",\"s\",\"t\",\"u\",\"v\",\"w\",\"x\",\"y\",\"z\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"+\",\"/\"];t.bytesToBase64=Pe,t.getMultiBody=function(e){var t,n=Ve(),r=Re(\"--\"+n+\"\\r\\n\");return e.forEach(function(e){e=Fe(e),e&&(e=Be(r,e),t=t?Be(t,e):e)}),t=t&&Be(t,Re(\"--\"+n+\"--\")),{boundary:n,length:t?t.length:0,base64:t&&Pe(t)}},t.formatDate=We,t.formatTime=function(e){e=Math.floor(e/1e3);var t=Ye(e%60);e=Math.floor(e/60);var n=Ye(e%60);e=Math.floor(e/60);var r=Ye(e%24),o=Math.floor(e/24);return(o?Ye(o)+\" \":\"\")+r+\":\"+n+\":\"+t};var Bn={};t.toHar=function(e){var t=-1,n=-1,r=-1,o=-1;return e.dnsTime>=e.startTime&&(n=e.dnsTime-e.startTime,t=n,e.requestTime>=e.dnsTime&&(r=e.requestTime-e.dnsTime,e.responseTime>=e.requestTime?(o=e.responseTime-e.requestTime,t=e.endTime>=e.responseTime?e.endTime-e.startTime:e.responseTime-e.startTime):t=e.requestTime-e.startTime)),{startedDateTime:new Date(e.startTime).toISOString(),ttfb:e.ttfb,time:t,whistleCustomData:e.customData,whistleRules:e.rules,whistleFwdHost:e.fwdHost,whistleSniPlugin:e.sniPlugin,whistleVersion:e.version,whistleNodeVersion:e.nodeVersion,whistleRealUrl:e.realUrl,whistleCaptureError:e.captureError,whistleReqError:e.reqError,whistleIsHttps:e.isHttps,whistleResError:e.resError,whistleTimes:{startTime:e.startTime,dnsTime:e.dnsTime,requestTime:e.requestTime,responseTime:e.responseTime,endTime:e.endTime},request:Ke(e),response:Ze(e),frames:e.frames,cache:{},timings:{blocked:0,dns:n,connect:-1,send:r,wait:-1,receive:o,ssl:-1,comment:\"\"},clientIPAddress:e.clientIp,serverIPAddress:e.hostIp}},t.getUrl=function(e){return e&&-1===e.indexOf(\"/\")?\"tunnel://\"+e:e},t.expandAll=$e,t.collapseAll=et,t.expand=nt,t.collapse=rt;var Rn=/^((?:http|ws)s?:\\/\\/)[^/?]*/;t.getRawUrl=function(e){return e.fwdHost&&e.url.replace(Rn,\"$1\"+e.fwdHost)},t.isGroup=ot,t.filterJsonText=function(e,t,n){var r;if(t){if(!t.not&&!it(t,e))return{};r=JSON.parse(e),at(r,t,n)}return r};var zn=/^(?:([a-z0-9.+-]+:)?\\/\\/)?([^/?#]+)(\\/[^?#]*)?(\\?[^#]*)?(#.*)?$/i,Qn=/^(.+)(?::(\\d*))$/,On=/^\\[|\\]$/g;t.parseUrl=function(e){if(zn.test(e)){var t=RegExp.$1||\"http:\",n=RegExp.$2,r=RegExp.$3||\"/\",o=RegExp.$4,i=RegExp.$5||null,a=null,s=n;return Qn.test(n)&&(s=RegExp.$1,a=RegExp.$2),{protocol:t,slashes:!0,auth:null,host:n,port:a,hostname:s.replace(On,\"\"),hash:i,search:o||null,query:o?o.substring(1):null,pathname:r,path:r+o,href:e}}},t.replacQuery=function(e,t){var n=e.indexOf(\"#\"),r=\"\";return-1!==n&&(r=e.substring(n),e=e.substring(0,n)),t&&(t=\"?\"+t),n=e.indexOf(\"?\"),-1!==n&&(e=e.substring(0,n)),e+t+r},t.getDisplaySize=function(e,t){return t=e==t?\"\":ke(t),e=ke(e),t?e+\" / \"+t:e},t.formatSize=function(e,t){var n=st(e);return e&&t>=0&&t!=e&&(n+=\" / \"+st(t),n+=t?\" = \"+Number(100*e/t).toFixed(2)+\"%\":\"\"),n},t.getTabIcon=function(e){return e.icon&&ct(e.plugin,e.icon)},t.getPluginIcon=function(e,t){var n=e&&e[t||\"favicon\"];return n&&ct(e.moduleName,n)};var Hn=/[?&#]data(?:_url|Url)=([^&#]+)(?:&|#|$)/;t.getDataUrl=function(){var e=Hn.exec(location.href);return e&&de(e[1]).trim()},t.getSimplePluginName=lt,t.showJSONDialog=function(e,t){var n=e&&JSON.stringify(e);n&&xt.trigger(\"showJsonViewDialog\",[n,Array.isArray(t)?t:null])},t.handleFormat=function(e,t){e.shiftKey&&70===e.keyCode&&(e.metaKey||e.ctrlKey)&&(t(e),e.preventDefault())},t.handleTab=function(e){var t=e.target;if(!(9!==e.keyCode||e.altKey||t.readOnly||t.disabled)){e.preventDefault();var n=t.selectionStart,r=t.selectionEnd,o=t.value;t.value=o.substring(0,n)+\"  \"+o.substring(r),t.selectionStart=t.selectionEnd=n+2}},t.getPluginCgiUrl=ct,t.showHandlePluginInfo=function(e,t){return e?e.ec?Ct.error(e.em||\"Request error, please try again!\"):(Ct.success(\"Request successful - plugin list updating...\"),!0):(C(t),!1)},t.getDialogTitle=function(e,t){switch(t=t||\"Import\",e){case\"network\":return t+\" Network Sessions\";case\"networkSettings\":return t+\" Network Settings\";case\"composer\":return t+\" Composer Data\";case\"console\":return t+\" Console Logs\";case\"server\":return t+\" Server Logs\";case\"rules\":return t+\" Rules\";case\"rulesSettings\":return t+\" Rules Settings\";case\"values\":return t+\" Values\";case\"valuesSettings\":return t+\" Values Settings\";case\"mock\":return t+\" Mock Data\";default:return\"\"}};var Fn=/[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c]+/g,Vn=/^[^\\n\\r\\S]*(```+)[^\\n\\r\\S]*(\\S+)[^\\n\\r\\S]*[\\r\\n](?:([\\s\\S]*?)[\\r\\n])??[^\\n\\r\\S]*\\1\\s*$/gm;t.resolveInlineValues=ut;var Pn=/^\\s*line`\\s*[\\r\\n]([\\s\\S]*?)[\\r\\n]\\s*`\\s*?$/gm,Yn=/\\s*[\\r\\n]+\\s*/;t.removeRulesComments=dt,t.formatRules=function(e,t,n){return e=ut(e,t,n),e=dt(e),e=pt(e),e.trim().split(Yn)},t.handleClickLocate=function(e){var t=e&&e.indexOf(Xt);if(t>0){e=e.substring(t+_t,e.length-1),t=e.indexOf(\":\");var n=e.substring(0,t),r=e.substring(t+1).trim();if(!n||!r)return void(\"Service Rules\"===e?m(\"serviceRules\",!0):\"Mock Rules\"===e&&m(\"mockRules\",!0));\"File\"===n?xt.trigger(\"showRules\",r):\"Plugin\"===n&&xt.trigger(\"showPlugins\",r)}},t.getDocUrl=ht,t.UPDATE_URL=ht(\"faq.html#update\"),t.shakeElem=function(e){e.hasClass(\"w-shake-horizontal\")||(e.addClass(\"w-shake-horizontal\"),setTimeout(function(){e.removeClass(\"w-shake-horizontal\")},500))};var Gn=/[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c'<>:\"\\\\/|?*]+/g,Wn=/\\s+/g,Xn=/^\\s+/;t.formatFilename=function(e){return e.replace(Gn,\"\").replace(Xn,\"\").replace(Wn,\" \")};var _n;try{_n=wt.extend({},JSON.parse(It.get(\"shortcutsSettings\"))||{})}catch(fn){}_n=_n||{},t.shortcutsSettings=_n,t.saveShortcutsSettings=function(){It.set(\"shortcutsSettings\",JSON.stringify(_n))};var qn=[\"switchTabReverse\",\"switchTab\"];t.hasShortcut=function(e){if(-1!==qn.indexOf(e)){var t=document.activeElement,n=t&&t.nodeName;if(\"INPUT\"===n||\"TEXTAREA\"===n||t&&t.isContentEditable)return!1}return _n[e]!==!1},t.noModal=function(){return!wt(\".modal.in\").length}},function(e,t,n){\"use strict\";t.parse=n(204),t.stringify=n(205)},function(e,t){\"use strict\";var n=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};e.exports=function(){var e,t,r,o,i={'\"':'\"',\"\\\\\":\"\\\\\",\"/\":\"/\",b:\"\\b\",f:\"\\f\",n:\"\\n\",r:\"\\r\",t:\"\t\"},a=function(t){throw{name:\"SyntaxError\",message:t,at:e,text:r}},s=function(n){return n&&n!==t&&a(\"Expected '\"+n+\"' instead of '\"+t+\"'\"),t=r.charAt(e),e+=1,t},l=function(){var e,n=\"\";for(\"-\"===t&&(n=\"-\",s(\"-\"));t>=\"0\"&&\"9\">=t;)n+=t,s();if(\".\"===t)for(n+=\".\";s()&&t>=\"0\"&&\"9\">=t;)n+=t;if(\"e\"===t||\"E\"===t)for(n+=t,s(),(\"-\"===t||\"+\"===t)&&(n+=t,s());t>=\"0\"&&\"9\">=t;)n+=t,s();return n.length>15?(e=new String(n),e._$isNumber=!0,e):(e=+n,isFinite(e)?e:void a(\"Bad number\"))},c=function(){var e,n,r,o=\"\";if('\"'===t)for(;s();){if('\"'===t)return s(),o;if(\"\\\\\"===t)if(s(),\"u\"===t){for(r=0,n=0;4>n&&(e=parseInt(s(),16),isFinite(e));n+=1)r=16*r+e;o+=String.fromCharCode(r)}else{if(\"string\"!=typeof i[t])break;o+=i[t]}else o+=t}a(\"Bad string\")},u=function(){for(;t&&\" \">=t;)s()},d=function(){switch(t){case\"t\":return s(\"t\"),s(\"r\"),s(\"u\"),s(\"e\"),!0;case\"f\":return s(\"f\"),s(\"a\"),s(\"l\"),s(\"s\"),s(\"e\"),!1;case\"n\":return s(\"n\"),s(\"u\"),s(\"l\"),s(\"l\"),null}a(\"Unexpected '\"+t+\"'\")},p=function(){var e=[];if(\"[\"===t){if(s(\"[\"),u(),\"]\"===t)return s(\"]\"),e;for(;t;){if(e.push(o()),u(),\"]\"===t)return s(\"]\"),e;s(\",\"),u()}}a(\"Bad array\")},h=function(){var e,n={};if(\"{\"===t){if(s(\"{\"),u(),\"}\"===t)return s(\"}\"),n;for(;t;){if(e=c(),u(),s(\":\"),Object.hasOwnProperty.call(n,e)&&a(\"Duplicate key '\"+e+\"'\"),n[e]=o(),u(),\"}\"===t)return s(\"}\"),n;s(\",\"),u()}}a(\"Bad object\")};return o=function(){switch(u(),t){case\"{\":return h();case\"[\":return p();case'\"':return c();case\"-\":return l();default:return t>=\"0\"&&\"9\">=t?l():d()}},function(i,s){var l;return r=i,e=0,t=\" \",l=o(),u(),t&&a(\"Syntax error\"),\"function\"==typeof s?function c(e,t){var r,o,i=e[t];if(i&&\"object\"===(\"undefined\"==typeof i?\"undefined\":n(i)))for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(o=c(i,r),void 0!==o?i[r]=o:delete i[r]);return s.call(e,t,i)}({\"\":l},\"\"):l}}()},function(e,t){\"use strict\";function n(e){return 10>e?\"0\"+e:e}function r(){return this.valueOf()}function o(e){return s.lastIndex=0,s.test(e)?'\"'+e.replace(s,function(e){var t=u[e];return\"string\"==typeof t?t:\"\\\\u\"+(\"0000\"+e.charCodeAt(0).toString(16)).slice(-4)})+'\"':'\"'+e+'\"'}function i(e,t){var n,r,s,u,p,h=l,g=t[e];switch(g&&\"object\"===(\"undefined\"==typeof g?\"undefined\":a(g))&&\"function\"==typeof g.toJSON&&(g=g.toJSON(e)),\"function\"==typeof d&&(g=d.call(t,e,g)),\"undefined\"==typeof g?\"undefined\":a(g)){case\"string\":return o(g);case\"number\":return isFinite(g)?String(g):\"null\";case\"boolean\":case\"null\":return String(g);case\"object\":if(!g)return\"null\";if(l+=c,p=[],\"[object Array]\"===Object.prototype.toString.apply(g)){for(u=g.length,n=0;u>n;n+=1)p[n]=i(n,g)||\"null\";return s=0===p.length?\"[]\":l?\"[\\n\"+l+p.join(\",\\n\"+l)+\"\\n\"+h+\"]\":\"[\"+p.join(\",\")+\"]\",l=h,s}if(g._$isNumber)return g.toString();if(d&&\"object\"===(\"undefined\"==typeof d?\"undefined\":a(d)))for(u=d.length,n=0;u>n;n+=1)\"string\"==typeof d[n]&&(r=d[n],s=i(r,g),s&&p.push(o(r)+(l?\": \":\":\")+s));else for(r in g)Object.prototype.hasOwnProperty.call(g,r)&&(s=i(r,g),s&&p.push(o(r)+(l?\": \":\":\")+s));return s=0===p.length?\"{}\":l?\"{\\n\"+l+p.join(\",\\n\"+l)+\"\\n\"+h+\"}\":\"{\"+p.join(\",\")+\"}\",l=h,s}}var a=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},s=/[\\\\\"\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\"function\"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+\"-\"+n(this.getUTCMonth()+1)+\"-\"+n(this.getUTCDate())+\"T\"+n(this.getUTCHours())+\":\"+n(this.getUTCMinutes())+\":\"+n(this.getUTCSeconds())+\"Z\":null},Boolean.prototype.toJSON=r,Number.prototype.toJSON=r,String.prototype.toJSON=r);var l,c,u,d;u={\"\\b\":\"\\\\b\",\"\t\":\"\\\\t\",\"\\n\":\"\\\\n\",\"\\f\":\"\\\\f\",\"\\r\":\"\\\\r\",'\"':'\\\\\"',\"\\\\\":\"\\\\\\\\\"},e.exports=function(e,t,n){var r;if(l=\"\",c=\"\",\"number\"==typeof n)for(r=0;n>r;r+=1)c+=\" \";else\"string\"==typeof n&&(c=n);if(d=t,t&&\"function\"!=typeof t&&(\"object\"!==(\"undefined\"==typeof t?\"undefined\":a(t))||\"number\"!=typeof t.length))throw new Error(\"JSON.stringify\");return i(\"\",{\"\":e})}},function(e,t,n){\"use strict\";function r(e,t){\"warn\"===t?t=\"warning\":\"error\"===t&&(t=\"danger\");var n=i[t];return n||(n=o('<div class=\"alert alert-'+t+' w-message\"></div>'),i[t]=n),n.appendTo(document.body),n.text(e),n.stop(!0,!0).show(),n.css(\"marginLeft\",-n[0].offsetWidth/2),n.delay(2e3).fadeOut(1600),n}var o=n(18);n(207);var i={};o(document).on(\"click\",\".w-message\",function(){o(this).stop(!0,!0).hide()}),[\"error\",\"warn\",\"info\",\"success\"].forEach(function(e){t[e]=function(t){return r(t,e)}})},function(e,t,n){var r=n(208);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-message{position:fixed;top:30px;left:50%;z-index:calc(var(--z-max) + 2);padding:8px 20px!important;max-width:460px;white-space:pre-wrap;cursor:default}\",\"\"])},function(e,t,n){\"use strict\";function r(){return s||(s=u('<div class=\"modal fade w-win-dialog\" tabindex=\"-1\" role=\"dialog\"><div class=\"modal-dialog\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-body\"><pre class=\"alert alert-danger\"></pre></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button><button type=\"button\" class=\"btn btn-primary w-copy-text-with-tips\" data-dismiss=\"modal\"></button></div></div></div></div>')),s}function o(){return l||(l=u('<div class=\"modal fade w-win-dialog\" tabindex=\"-1\" role=\"dialog\"><div class=\"modal-dialog\" role=\"document\"><div class=\"modal-content\"><div class=\"modal-body\"><pre></pre></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default w-win-cancel\" data-dismiss=\"modal\">Cancel</button><button type=\"button\" class=\"btn btn-danger w-win-delete-all\" data-dismiss=\"modal\">Delete All</button><button type=\"button\" class=\"btn btn-primary w-win-confirm\" data-dismiss=\"modal\">Confirm</button></div></div></div></div>'),l.on(\"click\",\".w-win-cancel\",function(){\"function\"==typeof c&&c(!1),c=null}),l.on(\"click\",\".w-win-delete-all\",function(){\"function\"==typeof c&&c(2),c=null}),l.on(\"click\",\".w-win-confirm\",function(){\"function\"==typeof c&&c(1),c=null})),l}function i(e,t,n){r(),s.find(\"pre\").text(e),s.modal(\"show\");var o=s.find(\".w-copy-text-with-tips\");t&&\"string\"==typeof t?(o.text(n||\"Copy\"),o.show().attr(\"data-clipboard-text\",t)):o.hide()}function a(e,t,n,r){o(),l.is(\":visible\")||(l.find(\".w-win-delete-all\")[n?\"show\":\"hide\"](),c=t,l.find(\"pre\").text(e),l.modal(\"show\"),r?l.attr(\"data-confirm-flag\",r):l.removeAttr(\"data-confirm-flag\"))}var s,l,c,u=n(18);t.alert=i,t.confirm=a},function(e,t){\"use strict\";function n(e){return r+\"?\"+e}var r=location.href.replace(/[?#].*$/,\"\").replace(/\\/index.html$/i,\"/\"),o={};t.set=function(e,t){e=n(e),null==t?t=\"\":t+=\"\",o[e]=t;try{localStorage[e]=t}catch(r){}},t.get=function(e,t){e=n(e);try{return t?localStorage[e]:o[e]||localStorage[e]}catch(r){}return o[e]},t.remove=function(e){try{localStorage.removeItem(n(e))}catch(t){}}},function(e,t,n){\"use strict\";function r(e){this.list=C(e),this.isTreeView=\"1\"===D.get(\"isTreeView\"),this.clearRoot()}function o(e){if(e=\"string\"!=typeof e?\"\":e.trim()){var t=\"!\"===e[0];t&&(e=e.substring(1));var n;if(R.test(e)&&(n=RegExp.$1.toLowerCase(),e=RegExp.$2.trim(),\"!\"===e[0]&&(t=!0,e=e.substring(1))),e||n)return{not:t,type:n||\"url\",keyword:e.toLowerCase(),regexp:E.toRegExp(e)}}}function i(e){if(e=\"string\"!=typeof e?\"\":e.trim()){var t,n=function(e){e&&(t=t||[],t.push(e))};if(z.test(e)){var r=RegExp.$1,i=RegExp.$2,a=RegExp.$3;n(o(r)),n(o(i)),n(o(a))}else n(o(e));return t}}function a(e,t){return e?t.keyword?t.regexp?t.regexp.test(e):-1!==e.toLowerCase().indexOf(t.keyword):!0:!1}function s(e,t){if(a((e.isHttps?\"tunnel://\":\"\")+e.url,t))return!0;var n=E.getRawUrl(e);return a(n,t)}function l(e,t){if(c(s(e,t),t.not))return!1;var n=N&&N.getDataKeys(),r=n&&n.length;if(r)for(var o=0;r>o;o++){var i=E.getProperty(e,n[o]);if(null!=i&&c(a(String(i),t),t.not))return!1}return!0}function c(e,t){return t?!e:e}function u(e){var t=e.res&&e.res.statusCode;return e.reqError||e.resError||e.customData&&e.customData.error||t&&(!/^\\d+$/.test(t)||t>=400)}function d(e,t){switch(t.type){case\"mark\":return!e.mark||l(e,t);case\"c\":case\"content\":case\"b\":case\"body\":var n=E.getBody(e.req,!0),r=E.getBody(e.res);return c(!a(n,t)&&!a(r,t),t.not);case\"headers\":case\"h\":return c(!m(e.req.headers,t)&&!m(e.res.headers,t),t.not);case\"type\":case\"t\":var o=e.res.headers;return o=o&&o[\"content-type\"],c(!(\"string\"==typeof o&&a(o,t)),t.not);case\"domain\":case\"host\":case\"d\":case\"H\":return c(!a(e.isHttps?e.url:E.getHost(e.url),t),t.not);case\"ip\":case\"i\":return c(!a(e.req.ip,t)&&!a(e.res.ip,t),t.not);case\"status\":case\"s\":case\"result\":case\"r\":var i=e.res.statusCode;return c(!a(null==i?\"-\":String(i),t),t.not);case\"method\":case\"m\":return c(!a(e.req.method,t),t.not);case\"app\":case\"a\":return c(!a(e.appName,t),t.not);\ncase\"e\":case\"error\":return!u(e)||l(e,t);case\"style\":return!e.style||l(e,t);case\"fc\":case\"composer\":return!e.fc||l(e,t);default:return l(e,t)}}function p(e){return null==e?\"\":e+\"\"}function h(e,t){if(!t)return!0;var n=\"Other\"===t;if(n||\"WS\"===t){if(Q.test(e.url))return!n;if(!n)return!1}if(n||\"Tunnel\"===t){if(e.isHttps)return!n;if(!n)return!1}if(n||\"Rules\"===t){if(e[N.HAS_RULES_KEY])return!n;if(!n)return!1}for(var r,o=E.getRawType(e.res.headers),i=0,a=P.length;a>i;i++)if(r=P[i],n||t===r){var s=\"Font\"===r?O:\"Media\"===r?H:F;if(s.test(o))return!n;if(!n)return!1}var l=E.getContentType(o);for(i=0,a=V.length;a>i;i++)if(r=V[i],n||t===r){if(l===(i?r:\"IMG\"))return!n;if(!n)return!1}if(n||\"Error\"===t){if(u(e))return!n;if(!n)return!1}if(n||\"Mock\"===t){var c=e.rules||\"\";if(c.rule&&c.rule.isLoc)return!n;if(!n)return!1}if(n||\"Import\"===t){if(e.importedData)return!n;if(!n)return!1}if(n||\"Composer\"===t){if(e.fc)return!n;if(!n)return!1}return!0}function g(e,t,n,r){return e==t?0:\"-\"==e?1:\"-\"==t?-1:\"asc\"==n?f(e,t,r):-f(e,t,r)}function f(e,t,n){var r=null==t||\"\"==t;if(null==e||\"\"==e)return r?0:-1;if(r)return 1;var o=-1!==\"dns,request,response,download,time\".indexOf(n);if(!o&&e>t)return 1;var i=\"undefined\"==typeof e?\"undefined\":I(e),a=\"undefined\"==typeof t?\"undefined\":I(t);return o&&\"string\"===i&&\"string\"===a?e.replace(\"ms\",\"\")-t.replace(\"ms\",\"\")>0?1:-1:i!=a&&\"number\"==i?1:-1}function m(e,t){for(var n in e){if(a(n,t))return!0;var r=e[n];if(a(null==r?\"\":String(r),t))return!0}return!1}function A(e,t){var n=e.children;return n?(n.forEach(function(e){A(e,t)}),t):(t[e.data.id]=1,t)}function M(e,t,n){if(t>0){var r=w(e);if(n){for(var o=0,i=e.length;t>0&&i>o;)e[o].hide?(--i,--t,e.splice(o,1)):++o;t=e.length-U-2}t>0&&e.splice(0,t),r&&-1===e.indexOf(r)&&(e[0]=r)}}function w(e){for(var t=0,n=e.length;n>t;t++){var r=e[t];if(r.active)return r}}function v(e,t){for(;e>=0;e--){var n=t[e-1];if(!n||!n.selected&&!n.active)return e}return e}function b(e,t){for(var n=t.length;n>e;e++){var r=t[e+1];if(r&&r.data&&(r=r.data),!r||!r.selected&&!r.active)return e}return e}function y(e){var t=e.indexOf(\"?\"),n=\"\";if(-1!==t&&(n=e.substring(t),e=e.substring(0,t)),t=e.indexOf(\"://\"),-1===t)return[\"tunnel://\"+e,\"/\"];if(t=e.indexOf(\"/\",t+3),-1===t)return[e,\"/\"];var r=e.substring(t).split(\"/\");r[0]=e.substring(0,t);var o=r.length-1;return r[o]=\"/\"+r[o]+n,r}function x(e){var t=e.children;if(!t||e.hide)return e.hide;for(var n=!0,r=0,o=t.length;o>r;r++){var i=t[r];x(i)?i.hide=!0:(n=!1,i.hide=!1)}return e.hide=n,n}function T(e,t){for(var n=e.children,r=0,o=n.length;o>r;r++){var i=n[r];i.hide||(t.push(i),i.children&&T(i,t))}return e}function C(e,t){var n=e.length;if(n&&(t||!e[n-1].order)){var r=e[0].order||1;e.forEach(function(e,t){e.order=r+t})}return e}var N,I=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},E=n(202),D=n(210),S=n(199),L=[500,1e3,1500,2e3,2500,3e3],k=parseInt(D.get(\"maxNetworkRows\"),10)||1500,j=-1===L.indexOf(k)?1500:k,U=j+100,B=\"__whistle_\"+location.href.replace(/\\/[^/]*([#?].*)?$/,\"/\")+\"__\",R=/^(e|error|style|url|host|h|domain|d|u|composer|fc|content|c|b|body|headers|h|ip|i|status|a|app|result|s|r|method|m|mark|type|t):(.*)$/i,z=/([^\\s]+)(?:\\s+([^\\s]+)(?:\\s+([\\S\\s]+))?)?/,Q=/^wss?:\\/\\//,O=/font\\//i,H=/audio\\/|video\\/|application\\/vnd.apple.mpegurl|application\\/dash+xml/i,F=/application\\/wasm/i,V=[\"Img\",\"HTML\",\"CSS\",\"JS\",\"JSON\"],P=[\"Font\",\"Media\",\"Wasm\"];r.MAX_COUNT=U,r.setMaxRows=function(e){e=parseInt(e,10),-1!==L.indexOf(e)&&(j=e,U=j+100,r.MAX_COUNT=U,D.set(\"maxNetworkRows\",e))},r.getMaxRows=function(){return j};var Y=r.prototype;Y.search=function(e){return this._keyword=i(e),this.filter(),e},Y.setFilterType=function(e){return this._filterType=e,this.filter(),e},Y.hasKeyword=function(){return this._keyword},Y.setSortColumns=function(e){this._columns=e,this.filter(!1)},Y.hasUnmarked=function(){for(var e=this.list,t=e.length-1;t>=0;--t)if(!e[t].mark)return!0},Y.getList=function(){return this._list||this.list},Y.getTreeLeafs=function(e){if(e){var t=!e.indexOf(\"tunnel://\");t?e=e.substring(9):e+=\"/\";var n=this.list.filter(function(n){return t?n.url===e:!n.url.indexOf(e)});return n.length?n:void 0}},Y.filter=function(){var e=this,t=e.list,n=e._keyword,r=e._filterType;t.forEach(function(e){h(e,r)?n?e.hide=d(e,n[0])||n[1]&&d(e,n[1])||n[2]&&d(e,n[2]):e.hide=!1:e.hide=!0});var o=e._columns;if(o&&o.length){var i=o.length;e._list=e.list.slice().sort(function(e,t){for(var n=0;i>n;n++){var r=o[n],a=\"body\"===r.name,s=\"app\"===r.name?\"appName\":null,l=a?e.bodySize:E.getCellValue(e,r,s),c=a?t.bodySize:E.getCellValue(t,r,s);if(a){if(l===c)return 0;var u=l>c?1:-1;return\"asc\"==r.order?u:-u}r.key&&(l=p(l),c=p(c));var d=g(l,c,r.order,r.name);if(d)return d}return e.order>t.order?-1:1})}else e._list=null;return this.updateTree(),this.updateDisplayCount(),t};var G=60;Y.updateDisplayCount=function(){window.name=B+this.list.length},Y.getDisplayCount=function(){var e=window.name;if(\"string\"!=typeof e||0!==e.indexOf(B))return 0;var t=parseInt(e.substring(B.length));return t>=0&&G>=t?t:G},Y.clear=function(){var e=this.list.length;return this.clearNetwork=!0,this.list.splice(0,e),this._list=null,this.updateTree(),this.updateDisplayCount(),e&&S.trigger(\"selectedSessionChange\"),this},Y.removeByHostList=function(e){for(var t=this.list,n=t.length-1;n>=0;--n){var r=t[n];-1!==e.indexOf(r.isHttps?r.path:r.hostname)&&t.splice(n,1)}this.update(),this.updateDisplayCount()},Y.removeTreeNode=function(e,t){var n=this.getTreeNode(e);if(n){for(var r=A(n,{}),o=this.list,i=o.length-1;i>=0;--i)(t?!r[o[i].id]:r[o[i].id])&&o.splice(i,1);return this.update(),this.updateDisplayCount(),!0}},Y.removeByUrlList=function(e){for(var t=this.list,n=t.length-1;n>=0;--n)-1!==e.indexOf(t[n].url.replace(/\\?.*$/,\"\").substring(0,1024))&&t.splice(n,1);this.update(),this.updateDisplayCount()},Y.removeSelectedItems=function(){for(var e,t=-1,n=this.list,r=n.length-1;r>=0;r--){var o=n[r];o.selected&&!o.hide?(e=!0,-1==t&&(t=r),r||n.splice(r,t-r+1)):-1!=t&&(n.splice(r+1,t-r),t=-1)}return e?(this.update(!1,!0),!0):void 0},Y.remove=function(e){var t=this.list,n=t.indexOf(e);-1!==n&&(t.splice(n,1),this.update(!1,!0))},Y.removeOthers=function(e){var t=this.list,n=t.indexOf(e);-1!==n&&(t.splice(n+1,t.length-n),0!==n&&t.splice(0,n),this.update(!1,!0))},Y.removeUnselectedItems=function(){for(var e,t=-1,n=this.list,r=n.length-1;r>=0;r--){var o=n[r];o.selected?-1!=t&&(n.splice(r+1,t-r),t=-1):(e=!0,-1==t&&(t=r),r||n.splice(r,t-r+1))}return e?(this.update(!1,!0),!0):void 0},Y.removeUnmarkedItems=function(){for(var e,t=-1,n=this.list,r=n.length-1;r>=0;r--){var o=n[r];o.mark?-1!=t&&(n.splice(r+1,t-r),t=-1):(e=!0,-1==t&&(t=r),r||n.splice(r,t-r+1))}return e?(this.update(!1,!0),!0):void 0},Y.prev=function(){var e=this.getList(),t=e.length;if(t){var n,r,o=this.getActive(),i=o?e.indexOf(o):t-1;for(n=i-1;n>=0;n--)if(r=e[n],!r.hide)return r;for(n=t-1;n>i;n--)if(r=e[n],!r.hide)return r}},Y.next=function(){var e=this.getList(),t=e.length;if(t){var n,r,o=this.getActive(),i=o?e.indexOf(o):0;for(n=i+1;t>n;n++)if(r=e[n],!r.hide)return r;for(n=0;i>n;n++)if(r=e[n],!r.hide)return r}},Y.start=function(){for(var e=this.getList(),t=0,n=e.length;n>t;t++){var r=e[t];if(!r.hide)return r}},Y.end=function(){for(var e=this.getList(),t=e.length-1;t>=0;t++){var n=e[t];if(!n.hide)return n}},Y.update=function(e,t){if(C(this.list,t),e&&!this.isTreeView){var n=Math.min(this.list.length-j,100);M(this.list,n,this.hasKeyword())}return this.filter(),!this.isTreeView&&this.list.length>j},Y.hasSelected=function(){for(var e=this.list,t=0,n=e.length;n>t;t++){var r=e[t];if(!r.hide&&r.selected)return!0}return!1},Y.hasUnselected=function(){for(var e=this.list,t=0,n=e.length;n>t;t++){var r=e[t];if(!r.hide&&!r.selected)return!0}return!1},Y.getSelected=function(){return this.getActive()},Y.getActive=function(){return w(this.list)||this.getSelectedList()[0]},Y.getItem=function(e){if(e)for(var t=this.list,n=0,r=t.length;r>n;n++){var o=t[n];if(o.id===e)return o}},Y.setSelected=function(e,t){e.selected=t!==!1},Y.getSelectedList=function(){return this.list.filter(function(e){return!e.hide&&e.selected})},Y.setSelectedList=function(e,t,n){var r=this.getList();if(this.isTreeView&&(r=this.root.list,e=this.getTreeNode(e.id),t=this.getTreeNode(t.id)),e=r.indexOf(e),t=r.indexOf(t),e>t){var o=b(e,r);e=t,t=o}else e=v(e,r);for(var i=0,a=r.length;a>i;i++){var s=r[i];s=s.data||s,i>=e&&t>=i?(s.selected=!0,n(s,!0)):s.selected=!1}},Y.clearSelection=function(){this.list.forEach(function(e){e.selected=!1})},Y.clearActive=function(){this.list.forEach(function(e){e.active=!1})},Y.clearRoot=function(){var e={children:[],map:{},list:[]};return this.root=e,e},Y.getListByPath=function(e){var t=0===e.indexOf(\"tunnel://\");return t?e=e.substring(9):e+=\"/\",this.list.filter(function(n){return!n.hide&&(t?n.url===e:!n.url.indexOf(e))})},Y.updateTree=function(){if(!this.isTreeView)return this._updateOnTreeView=!0,this.root;this._updateOnTreeView=!1;var e=this.list,t=e.length;if(!t)return this.clearRoot();for(var n=this.root,r=this.clearRoot(),o=0;t>o;o++){for(var i,a,s=e[o],l=y(s.url),c=l.length-1,u=r,d=n,p=0;c>p;p++){var h=l[p],g=u.map[h],f=d&&d.map[h];i=p?i+\"/\"+h:h,g||(g={depth:p,path:i,value:h,children:[],map:{}},f&&(g.expand=f.expand,g.pExpand=f.pExpand),u.map[h]=g,u.children.push(g)),p?g.parent=u:a=g,u=g,d=f}var m={depth:c,parent:u,value:l[c],hide:s.hide,data:s};a.map[s.id]=m,u.children.push(m)}return r.children.forEach(x),T(r,r.list),r},Y.setTreeView=function(e,t){e=e!==!1,this.isTreeView!==e&&(this.isTreeView=e,!t&&D.set(\"isTreeView\",e?\"1\":\"\"),e&&this._updateOnTreeView&&this.updateTree())},Y.getTree=function(){return this.root},Y.getTreeNode=function(e){for(var t=this.root.list,n=0,r=t.length;r>n;n++){var o=t[n];if(o.data?o.data.id===e:o.path===e)return o}},r.setDataCenter=function(e){N=e},e.exports=r},function(e,t,n){\"use strict\";function r(e){return e.endTime||e.lost}function o(e,t){\"function\"==typeof Object.assign?Object.assign(e,t):Object.keys(t).forEach(function(n){e[n]=t[n]})}function i(e){return e&&\"string\"==typeof e?e:void 0}function a(e,t,n){var r=e.customData||{};e.customData=r,o(r,t);var a=r.style;if(a){var s=i(a.color),l=i(a.fontStyle),c=i(a.backgroundColor||a.bgColor);(s||l||c)&&(a=e.style||{},e.style=a,a.color=a.color||s,a.fontStyle=a.fontStyle||l,a.backgroundColor=a.backgroundColor||c)}p.notEStr(r.appName)&&(e.appName=p.getPluginCgiUrl(n,r.appName))}function s(e){if(!h[e]&&window.Worker){var t=new Worker(\"web-worker.js?id=\"+e),n=function(){o&&(clearTimeout(o),o=null,l(t))},o=setTimeout(n,16e3);t.onerror=n,t.onmessage=function(n){var i=n.data;if(i===!0)return void(o&&(clearTimeout(o),o=null,u.list.forEach(function(e){r(e)&&t.postMessage(e)}),h[e]=t));if(i&&i.id&&i.data){var s=u.getItem(i.id);s&&(a(s,i.data,i.plugin),d.trigger(\"updateUIThrottle\"))}}}}function l(e){e.terminate(),e.onmessage=null,e.onerror=null,e.onmessageerror=null}function c(e){var t=h[e];t&&(delete h[e],l(t))}var u,d=n(199),p=n(202),h={};t.updateWorkers=function(e){Object.keys(h).forEach(function(t){-1===e.indexOf(t)&&c(t)}),e.forEach(s)},t.postMessage=function(e){r(e)&&Object.keys(h).forEach(function(t){h[t].postMessage(e)})},t.setup=function(e){u=e}},function(e,t,n){\"use strict\";var r=n(24),o=r.createClass({displayName:\"Icon\",shouldComponentUpdate:function(e){var t=this.props;return t.name!==e.name||t.className!==e.className||t[\"data-name\"]!==e[\"data-name\"]||t[\"data-clipboard-text\"]!==e[\"data-clipboard-text\"]||t.title!==e.title||t.onClick!==e.onClick},render:function(){var e=this.props;return r.createElement(\"span\",{className:\"glyphicon glyphicon-\"+e.name+(e.className?\" \"+e.className:\"\"),\"data-name\":e[\"data-name\"],\"data-clipboard-text\":e[\"data-clipboard-text\"],title:e.title,onClick:e.onClick})}});e.exports=o},function(e,t,n){\"use strict\";var r=n(24),o=r.createClass({displayName:\"CloseBtn\",shouldComponentUpdate:function(e){var t=this.props;return t.onClick!==e.onClick||t.className!==e.className},render:function(){var e=this.props.onClick;return r.createElement(\"button\",{type:\"button\",className:\"close \"+(this.props.className||\"\"),\"data-dismiss\":e?null:\"modal\",onClick:e},\"×\")}});e.exports=o},function(e,t,n){\"use strict\";function r(e){var t=e.target,n=t.nodeName;return\"A\"===n?t:(t=t.parentNode,t&&(n=t.nodeName,\"A\"===n)?t:void 0)}function o(e){return\"string\"!=typeof e?\"\":e.substring(e.indexOf(\"_\")+1)}function i(e,t){var n=r(e);t&&!n&&(n=u(N(t)).find(\"a:last\")[0]);var i=n&&n.getAttribute(\"data-name\");if(i){var s=a(e);return s&&i.toLowerCase()!==s?{target:n,toName:o(i)}:void 0}}function a(e){var t=d.findArray(e.dataTransfer.types,function(e){return 0===e.indexOf(L)?!0:void 0});return t&&t.substring(L.length)}function s(e){if(\"string\"!=typeof e)return\"\";var t=e.lastIndexOf(\".\");return-1==t?\"\":e.substring(t+1)}var l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e};n(216),n(218);var c,u=n(18),d=n(202),p=n(24),h=n(57),g=n(206),f=n(220),m=n(223),A=n(290),M=n(293),w=n(200),v=n(199),b=n(296),y=n(210),x=n(335),T=n(336),C=n(213),N=h.findDOMNode,I=-1!==window.location.href.indexOf(\"hideEnableHTTPSTips=1\"),E=-1!==window.location.href.indexOf(\"disabledEditor=1\"),D=[{name:\"Copy\",list:[{name:\"Name\"},{name:\"Rules\"}]},{name:\"Enable\",action:\"Save\"},{name:\"Create\",action:\"Rule\"},{name:\"Rename\"},{name:\"Delete\"},{name:\"Import\"},{name:\"Export\"},{name:\"Trash\"},{name:\"Others\",action:\"Plugins\",list:[]},{name:\"Help\",sep:!0}],S=[{name:\"Copy\",list:[{name:\"Key\",action:\"CopyKey\"},{name:\"Value\"}]},{name:\"Save\"},{name:\"Create\",action:\"Key\"},{name:\"Rename\"},{name:\"Delete\"},{name:\"JSON\",list:[{name:\"Validate\"},{name:\"Format\"},{name:\"Inspect\"}]},{name:\"Import\"},{name:\"Export\"},{name:\"Trash\"},{name:\"Others\",action:\"Plugins\",list:[]},{name:\"Help\",sep:!0}],L=\"listmodal$\",k=/^\\s*(?:[\\{｛][\\w\\W]+[\\}｝]|\\[[\\w\\W]+\\])\\s*$/;u(document).on(\"drop\",function(){c&&(c.style.background=\"\"),c=null});var j=p.createClass({displayName:\"List\",getInitialState:function(){var e=d.parseJSON(y.get(this.getCollapseKey())),t={};return this.collapseGroups=Array.isArray(e)?e.filter(function(e){return d.isGroup(e)&&e[1]&&!t[e]?(t[e]=1,!0):void 0}):[],{}},componentDidMount:function(){function e(e){t.onDoubleClick(e)}var t=this,n=!t.props.hide;u(window).keydown(function(r){if(n&&(r.ctrlKey||r.metaKey)){var o=t.props.modal;if(83===r.keyCode&&d.hasShortcut(\"save\"+(t.isRules()?\"Rules\":\"Values\")+\"Changes\"))return o.getChangedList().forEach(e),!1}}).on(\"hashchange\",function(){var e=-1!==window.location.href.indexOf(\"disabledEditor=1\");e!==E&&(E=e,t.setState({}))});var r=t.props.modal;this.curListLen=r.list.length,this.curActiveItem=r.getActive(),u(N(t.refs.list)).focus().on(\"keydown\",function(e){var n;if(38==e.keyCode?n=r.prev():40==e.keyCode&&(n=r.next()),n){var o=t.getCurGroup(n);o&&t.expandGroup(o.name),e.shiftKey?t.setState({}):t.onClick(n),t.isRules()&&v.trigger(\"updateUI\"),e.preventDefault()}});var o=t.isRules()?\"Rules\":\"Values\";v.on(\"toggleCommentInEditor\",function(){var e=r.getActive();e&&v.trigger(\"save\"+o,e)}),t.isRules()&&v.on(\"enabledRulesCountChange\",function(){t.setState({})}),v.on(\"reload\"+o+\"RecycleBin\",function(){t.reloadRecycleBin(o)}),v.on(\"expand\"+o+\"Group\",function(e,n){var r=t.getGroupByName(n);r&&t.expandGroup(r.name)}),v.on(\"reqTabsChange\",function(){t.setState({})});var i=function(){N(t.refs.list).scrollTop=1e9},a=function(){N(t.refs.list).focus()};v.on(\"scroll\"+o+\"Bottom\",function(){i()}),v.on(\"focus\"+o+\"List\",function(){a()}),v.on(o.toLowerCase()+\"NameChanged\",function(e,n,r){var o=n===r?-1:t.collapseGroups.indexOf(n);-1!==o&&(-1!==t.collapseGroups.indexOf(r)?t.collapseGroups.splice(o,1):t.collapseGroups[o]=r,y.set(t.getCollapseKey(),JSON.stringify(t.collapseGroups)))}),v.on(\"focus\"+(t.isRules()?\"Rules\":\"Values\")+\"FilterInput\",function(){t.refs.filterInput.focus()}),t.ensureVisible(!0)},expandGroup:function(e){var t=this.collapseGroups.indexOf(e);-1!==t&&(this.collapseGroups.splice(t,1),y.set(this.getCollapseKey(),JSON.stringify(this.collapseGroups)))},shouldComponentUpdate:d.shouldComponentUpdate,componentDidUpdate:function(){var e=this.props.modal,t=e.list.length,n=e.getActiveObj(),r=n.curActiveItem,o=n.groupItem;o?this.ensureVisible(!1,o):(t>this.curListLen||r!==this.curActiveItem)&&this.ensureVisible(),this.curListLen=t,this.curActiveItem=r,this.props.hide&&this.refs.recycleBinDialog.hide()},ensureVisible:function(e,t){if(t=t||this.props.modal.getActive()){var n=N(this.refs[t.name]),r=N(this.refs.list);d.ensureVisible(n,r,e)}},onClick:function(e){var t=this;if(\"function\"!=typeof t.props.onActive||t.props.onActive(e)!==!1){var n=t.props.modal;n.setActive(e.name),t.setState({activeItem:e}),t.expandGroup(n.getGroupName(e.name))}},toggleGroup:function(e){var t=this.collapseGroups.indexOf(e.name);-1===t?this.collapseGroups.push(e.name):this.collapseGroups.splice(t,1),y.set(this.getCollapseKey(),JSON.stringify(this.collapseGroups)),this.setState({})},onClickGroup:function(e){var t=e.target.getAttribute(\"data-group\"),n=this.props.modal.groups,r=n[t];r||(r=n[t]={}),r.expand=!r.expand,this.setState({})},onDoubleClick:function U(e,t){e.selected&&!e.changed||t?this.onUnselect(e):this.onSelect(e);var U=this.props.onDoubleClick;\"function\"==typeof U&&U(e)},onSelect:function B(e){var B=this.props.onSelect;\"function\"==typeof B&&B(e)},onUnselect:function R(e){var R=this.props.onUnselect;\"function\"==typeof R&&R(e)},onChange:function(e){var t=this.props.modal,n=t.getActive();if(n){var r=n.value||\"\",o=e.getValue()||\"\";if(o!=r){var i=t.hasChanged();n.changed=!0,n.value=o,this.setState({selectedItem:n}),i||v.trigger(\"updateGlobal\")}}},onFilterChange:function(e){this.props.modal.search(e,\"rules\"!=this.props.name),this.setState({filterText:e})},getItemByKey:function(e){return this.props.modal.getByKey(e)},onDragStart:function(e){var t=r(e),n=t&&t.getAttribute(\"data-name\");n&&(e.dataTransfer.setData(L+n,1),e.dataTransfer.setData(\"-\"+L,n))},onDragEnter:function(e){var t=i(e);t&&(c=t.target,c.style.background=\"var(--b-active)\")},onDragLeave:function(e){var t=i(e);t&&(t.target.style.background=\"\")},onDrop:function(e){var t=i(e,this.refs.list);if(e.stopPropagation(),t){var n=o(e.dataTransfer.getData(\"-\"+L)),r=-1!==this.collapseGroups.indexOf(n),a=t.toName,s={from:n,to:a,group:r};t.target.style.background=\"\";var l=this.isRules()&&\"Default\"===a;if(l&&(a=this.props.modal.list[1],s.to=a,s.toTop=!0),this.props.modal.moveTo(n,a,r,l)){var c=\"rules\"===this.props.name?\"rules\":\"values\";w[c].moveTo(s,function(e,t){return e?void(2===e.ec&&v.trigger(c+\"Changed\")):void d.showSystemError(t)}),this.setState({}),this.triggerChange(\"move\")}}},formatJson:function(e){var t=e&&e.value||\"\";if(/\\S/.test(t)){var n=d.parseRawJson(t);n&&(n=JSON.stringify(n,null,\"  \"),t!==n&&(e.changed=!0,e.value=n,v.trigger(\"updateGlobal\")))}},reloadRecycleBin:function(e){this.refs.recycleBinDialog.isVisible()&&(this._pendingRecycle=!1,this.showRecycleBin(e))},showRecycleBin:function(e){var t=this;t._pendingRecycle||(t._pendingRecycle=!0,w[e.toLowerCase()].recycleList(function(n,r){return t._pendingRecycle=!1,n?n.list.length?void t.refs.recycleBinDialog.show({name:e,list:n.list}):g.info(\"Trash is empty\"):void d.showSystemError(r)}))},getGroupByName:function(e){var t=this.props.modal,n=t.data[e];if(!n||d.isGroup(n.name))return n;for(var r=t.list.indexOf(e)-1;r>=0;r--)if(n=t.data[t.list[r]],d.isGroup(n.name))return n},getCurGroup:function(e){return e=e||this.currentFocusItem,e&&this.getGroupByName(e.name)},onClickContextMenu:function(e,t,n,r){var o=this,i=\"rules\"===o.props.name?\"Rules\":\"Values\";switch(n||e){case\"Save\":v.trigger(\"save\"+i,o.currentFocusItem);break;case\"Rename\":v.trigger(\"rename\"+i,o.currentFocusItem);break;case\"Delete\":v.trigger(\"delete\"+i,o.currentFocusItem);break;case\"Rule\":v.trigger(\"createRules\",[o.getCurGroup(),o.currentFocusItem]);break;case\"Key\":v.trigger(\"createValues\",[o.getCurGroup(),o.currentFocusItem]);break;case\"Export\":v.trigger(\"exportData\");break;case\"Import\":v.trigger(\"showImportDialog\");break;case\"Trash\":o.showRecycleBin(i);break;case\"Validate\":var a=o.currentFocusItem;if(a)if(k.test(a.value))try{JSON.parse(a.value),g.success(\"Valid JSON object\")}catch(t){g.error(\"Warning: Invalid JSON format in the value of '\"+a.name+\"'. \"+t.message)}else g.error(\"Invalid JSON format\");break;case\"Format\":o.formatJson(o.currentFocusItem);break;case\"Inspect\":o.currentFocusItem&&v.trigger(\"showJsonViewDialog\",o.currentFocusItem.value);break;case\"Help\":window.open(d.getDocUrl(\"gui/\"+(o.props.name||\"values\")+\".html\"));break;case\"Plugins\":var s=o.props.modal,l=o.currentFocusItem;b.fork(e,{port:w.getPort(),type:\"rules\"===o.props.name?\"rules\":\"values\",name:r,list:s&&s.getList(),activeItem:l,selectedItem:s&&s.getActive(),setValue:function(e){e=e||\"\",l&&e!=l.value&&(l.changed=!0,l.value=e,v.trigger(\"updateGlobal\"))}})}},triggerChange:function(e){var t=this.props.modal.data,n=this.props.modal.list.map(function(e){var n=t[e];return{name:e,value:n&&n.value||\"\"}});d.triggerListChange(this.props.name||\"values\",{type:e,url:location.href,list:n})},isRules:function(){return\"rules\"==this.props.name},getCollapseKey:function(){return this.isRules()?\"collapseRulesGroups\":\"collapseValuesGroups\"},onContextMenu:function(e){var t=u(e.target).closest(\"a\").attr(\"data-name\"),n=this.props.modal;t=t&&o(t);var r=n.get(t);r||(t=void 0),this.currentFocusItem=r;var i,a=!t,s=this.isRules(),l=s?D[8]:S[9];d.addPluginMenus(l,w[s?\"getRulesMenus\":\"getValuesMenus\"](),s?7:8),s||(S[0].list[0].name=t&&d.isGroup(t)?\"Name\":\"Key\");var c=(s?280:315)-(l.hide?30:0);l.maxHeight=c+30;var p=d.getMenuPosition(e,110,c);p.className=\"w-contenxt-menu-list\",s?(p.list=D,p.list[1].disabled=a,p.list[1].name=\"Save\",r&&!r.changed&&(w.isMultiEnv()&&\"Default\"!==t||d.isGroup(t)?p.list[1].disabled=!0:p.list[1].name=r.selected?\"Disable\":\"Enable\"),r&&r.isDefault&&(i=!0),p.list[5].disabled=!n.list.length):(p.list=S,p.list[1].disabled=!r||!r.changed,p.list[5].disabled=a,p.list[6].disabled=!n.list.length);var h=p.list[0];h.disabled=a,a||(h.list[0].copyText=t,r.value?(h.list[1].disabled=!1,h.list[1].copyText=r.value):h.list[1].disabled=!0),p.list[3].disabled=i||a,p.list[4].disabled=i||a,this.refs.contextMenu.show(p),e.preventDefault()},onAddRule:function(e){this.props.modal.setActive(e),this.setState({})},enableAllRules:function(){var e=this;e._pendingEnableRules||(e._pendingEnableRules=setTimeout(function(){e._pendingEnableRules=null},2e3),u(\".w-enable-rules-menu\").trigger(\"click\"),v.trigger(\"disableAllRules\"))},showHttpsSettingsDialog:function(){v.trigger(\"showHttpsSettingsDialog\")},parseList:function(){var e,t,n,r=this.isRules(),o=this.props.modal,i=o.list,a=o.data,s=0,l=0,c=function(){e&&(e.changed=t,e.childCount=s,e.selectedCount=l,s=0,l=0,t=!1)};return i.forEach(function(o,i){var u=a[o];d.isGroup(u.name)?(c(),u.isGroup=!0,e&&(e.activeGroup=n),e=u,n=!1,e.activeGroup=!1):e&&(++s,t=t||u.changed,n=n||u.active,r&&u.selected&&++l)}),e&&(e.activeGroup=n),c(),i},onFormat:function(e){this.formatJson(this.props.modal.getActive()),e.preventDefault()},onInspect:function(e){var t=this.props.modal.getActive();t&&(v.trigger(\"showJsonViewDialog\",t.value),e.preventDefault())},switchTab:function(e){var t=e.target.getAttribute(\"data-name\");t&&t!==this.state.activeTab&&this.setState({activeTab:t})},showEnabledRules:function(){var e=this;w.rules.getEnabledRules(function(t,n){if(!t)return void d.showSystemError(n);var r=[];t.list.forEach(function(e){var t=e[1]?d.SOURCE_SEP+e[1]+\")\":\"\";r.push(e[0]+t)}),e.refs.enabledRulesDialog.show(r)})},render:function(){var e,t,n=this,r=n.props.modal,o=r.list,i=r.data,a=n.props,c=a.disabled,u=n.state.filterText,h=r.getActive(!0)||\"\",g=n.isRules(),v=!1,b=h?h.name:\"\",y=h.selected,N=w.enabledRulesCount||0;return o=n.parseList(),g?(v=o.length>2,d.triggerRulesActiveChange(b)):o.length>1&&(v=!0,d.triggerValuesActiveChange(b)),p.createElement(\"div\",{className:\"v-box fill\"+(y&&g?\" w-has-selected-rules\":\"\")+(c?\" w-has-selected-disabled\":\"\")+(a.hide?\" hide\":\"\")},c||w.needEnableHttps()&&!I&&!w.isPureProxy()?p.createElement(\"div\",{className:\"w-record-status\"},c?\"All rules are currently disabled\":\"Full functionality of the rules requires activation of the 'Enable HTTPS (Capture Tunnel Traffic)' option\",p.createElement(\"button\",{className:\"btn btn-primary\",onClick:c?n.enableAllRules:n.showHttpsSettingsDialog},c?\"Enable\":\"Settings\")):null,p.createElement(f,{leftWidth:\"230\"},p.createElement(\"div\",{className:\"fill v-box w-list-left\"},g?p.createElement(\"div\",{className:\"w-enabled-rules-btn\"+(N?\"\":\" w-disabled\"),onClick:N?n.showEnabledRules:null},p.createElement(C,{name:\"ok\",\"data-name\":\"enabledRules\"}),\"Enabled Rules (\",N,\")\"):null,p.createElement(\"div\",{ref:\"list\",tabIndex:\"0\",onContextMenu:this.onContextMenu,onDrop:n.onDrop,className:\"fill v-box w-list-data \"+(a.className||\"\")+(c?\" w-disabled\":\"\"),style:{background:u?\"var(--b-filtered)\":void 0}},o.map(function(r,o){var a=i[r],s=g&&0===o,l=a.isGroup,c=l?r.substring(1):r;return e=e||l,l&&(t=!u&&-1!==n.collapseGroups.indexOf(r)),p.createElement(\"a\",{tabIndex:\"0\",ref:r,\"data-name\":o+\"_\"+r,onDragStart:s?void 0:n.onDragStart,onDragEnter:n.onDragEnter,onDragLeave:n.onDragLeave,onDrop:n.onDrop,style:{display:a.hide?\"none\":null},key:a.key,\"data-key\":a.key,title:c,draggable:s?!1:v,onClick:function(){l?n.toggleGroup(a):n.onClick(a)},onDoubleClick:l?null:function(e){n.onDoubleClick(a),e.preventDefault()},className:d.getClasses({\"w-active\":!l&&a.active,\"w-changed\":a.changed,\"w-selected\":!l&&a.selected,\"w-list-group\":l,\"w-list-sub\":!l&&e,\"w-list-group-active\":l&&a.activeGroup,\"w-hide\":!l&&t,\"w-group-empty\":l&&!a.childCount})},l?p.createElement(C,{name:\"triangle-\"+(t?\"right\":\"bottom\")}):null,c,l?p.createElement(\"span\",{className:d.getClasses({\"w-group-child-num\":!0,\"w-exists-selected\":a.selectedCount>0})},\"(\",a.selectedCount>0?a.selectedCount+\"/\":\"\",a.childCount,\")\"):p.createElement(C,{name:\"ok\"}))})),p.createElement(A,{ref:\"filterInput\",onChange:this.onFilterChange}),p.createElement(M,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}),p.createElement(x,{ref:\"recycleBinDialog\"}),p.createElement(T,{ref:\"enabledRulesDialog\"})),p.createElement(m,l({},n.props,{onChange:n.onChange,readOnly:!h||h.hide||E,value:h.hide?\"\":h.value,mode:g?\"rules\":s(h.name),onFormat:g?null:this.onFormat,onInspect:g?null:this.onInspect}))))}});e.exports=j},function(e,t,n){var r=n(217);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-divider-con .w-divider{border:none}.w-list-data{background:var(--b-bar);overflow-x:hidden;overflow-y:auto;outline:0}.w-list-data a{display:block;padding-left:10px;line-height:2pc;position:relative;border-bottom:1px solid var(--c-border);color:var(--c-default);text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:var(--b-default);outline:0;cursor:default}.w-list-data a .glyphicon-ok{position:absolute;top:50%;right:10px;margin-top:-8px;color:var(--c-ok);display:none}.w-list-content{border-bottom:1px solid var(--c-border);border-right:1px solid var(--c-border);cursor:text}.w-list-data .w-changed:before{content:'*';color:var(--c-error);margin-right:2px}.w-list-data .w-list-group.w-changed:before{margin-right:0}.w-list-data a:hover{color:var(--c-link)}.w-list-data a:hover:not(.w-list-group){background:var(--b-hover)}.w-list-data .w-active{background:var(--c-link)!important;color:var(--c-active)!important}.w-list-data .w-selected{font-weight:500}.w-list-data .w-selected .glyphicon-ok{display:inline-block}.w-list-data span.w-whistle-tpl{display:block;width:8px;height:8px;position:absolute;right:3px;top:2px;background:center center no-repeat;background-size:100%}.w-list-data .w-list-group{background:var(--b-heavy);color:var(--c-heavy);cursor:default;padding-left:5px}.w-list-data .w-list-group.w-list-group-active{background:var(--b-heavy-active)}.w-list-data .w-list-group:active,.w-list-data .w-list-group:hover{color:var(--c-default)}.w-list-group .glyphicon,.w-list-group-icon.glyphicon-triangle-right{font-size:10px;opacity:.5;transform:scale(0.8);margin-right:2px}.w-list-group-icon.glyphicon-triangle-right{margin-right:-2px}.w-list-sub{padding-left:20px!important}.w-list-group .w-group-child-num{position:absolute;right:6px;font-size:10px;font-weight:500;color:var(--c-thin)}.w-list-group .w-exists-selected{color:var(--c-ok)}.w-list-group.w-group-empty{background-color:var(--b-hover);color:var(--c-gray)}.w-has-selected-rules .CodeMirror{border-top:3px solid var(--b-ok)}.w-has-selected-rules.w-has-selected-disabled .CodeMirror{border-top-color:var(--c-border)}.nav-tabs>.w-nav-normal-tab:first-child{font-weight:700}.w-enabled-rules-btn{height:28px;display:flex;align-items:center;justify-content:center;background:var(--b-title);border-bottom:1px solid var(--c-border);cursor:pointer;font-weight:700}.w-enabled-rules-btn .glyphicon-ok[data-name=enabledRules]{color:var(--c-ok);margin-right:5px;margin-top:-2px}.w-enabled-rules-btn.w-disabled{cursor:not-allowed;color:var(--c-gray)}.w-enabled-rules-dialog .modal-dialog{width:80%;min-width:860px}.w-enabled-rules-dialog .w-props pre{font-size:13px;line-height:1.5}\",\"\"])},function(e,t,n){var r=n(219);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-files-dialog .modal-dialog{width:900px}.w-files-order{width:36px}.w-files-date{width:205px}.w-files-path{word-break:break-all;word-wrap:break-word}.w-files-operation{width:15pc}.w-files-operation a{margin-right:15px;display:inline-block;white-space:nowrap}.w-files-operation a:last-child{color:var(--c-risk)}.w-files-operation a:last-child:hover{color:var(--c-error)}.w-files-dialog thead th{padding:8px}\",\"\"])},function(e,t,n){\"use strict\";n(221);var r=n(18),o=n(24),i=n(57),a=n(202),s={display:\"none\"};a.addDragEvent(\".w-divider\",function(e,t,n){e=e.parent();var r=e.parent(),o=!r.hasClass(\"box\"),i=e.hasClass(\"w-divider-right\"),a=o?e[0].offsetHeight-(i?n:-n):e[0].offsetWidth-(i?t:-t),s=r[0][o?\"offsetHeight\":\"offsetWidth\"];e[o?\"height\":\"width\"](Math.min(s-5,Math.max(5,a)))});var l=o.createClass({displayName:\"Divider\",componentDidMount:function(){this.reset()},componentDidUpdate:function(){this._needReset&&this.reset()},triggerDOMReady:function(){this.__inited||(this.__inited=!0,this.props.onDOMReady&&this.props.onDOMReady())},reset:function(){var e=this,t=i.findDOMNode(e.refs.divider);if(!t.offsetHeight)return void(e._needReset=!0);e._needReset=!1;var n=a.getBool(e.props.vertical),o=n?\"height\":\"width\",s=r(t),l=s.children(\".w-divider-left\"),c=s.children(\".w-divider-right\");if(l.add(c).css({height:\"auto\",width:\"auto\"}),e._leftWidth>0)return l[o](e._leftWidth),void e.triggerDOMReady();var u=parseInt(e.props.rightWidth,10);return u>0?(c[o](Math.max(u,5)),void e.triggerDOMReady()):void setTimeout(function(){var r=e.props.splitRatio;u=(n?t.offsetHeight:t.offsetWidth)*(r>0?r:.5),c[o](Math.max(u,5)),e.triggerDOMReady()},10)},render:function(){var e=a.getBool(this.props.vertical),t=o.createElement(\"div\",{className:\"w-divider\",onDoubleClick:this.reset}),n=this.props.hideLeft,r=this.props.hideRight,i=parseInt(this.props.leftWidth,10);i>0?this._leftWidth=i:i=0;var l=i||n;return o.createElement(\"div\",{ref:\"divider\",className:(e?\"v-box\":\"box\")+\" fill w-divider-con \"+(this.props.className||\"\")+(a.getBool(this.props.hide)?\" hide\":\"\")},o.createElement(\"div\",{style:n?s:void 0,className:(i?\"\":\"fill \")+\"w-divider-left v-box \"+(this.props.leftClassName||\"\")},i&&!r?t:null,this.props.children[0]),o.createElement(\"div\",{style:r?s:void 0,className:(l?\"fill \":\"\")+\"w-divider-right v-box \"+(this.props.rightClassName||\"\")},l?null:t,this.props.children[1]))}});e.exports=l},function(e,t,n){var r=n(222);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-divider-left,.w-divider-right{overflow:hidden;position:relative}.w-divider-con.box>.w-divider-right{min-width:5px!important}.w-divider-con.v-box>.w-divider-right{min-height:5px!important}.w-divider{position:absolute;z-index:1}.w-divider-con.box>div>.w-divider{top:0;width:6px;height:100%;cursor:ew-resize}.w-divider-con.box>.w-divider-left>.w-divider{right:0;border-right:1px solid var(--c-border)}.w-divider-con.box>.w-divider-right>.w-divider{left:0;border-left:1px solid var(--c-border)}.w-divider-con.v-box>div>.w-divider{left:0;height:5px;width:100%;cursor:ns-resize}.w-divider-con.v-box>.w-divider-left>.w-divider{bottom:0;border-bottom:1px solid var(--c-border)}.w-divider-con.v-box>.w-divider-right>.w-divider{top:0;border-top:1px solid var(--c-border)}\",\"\"]);\n},function(e,t,n){\"use strict\";function r(e){return document.querySelector?document.querySelector(e):o(e).length}n(224),n(226),n(228),n(230),n(232),n(234),n(236),n(238),n(240),n(242),n(244),n(246),n(248),n(250),n(252),n(254),n(256),n(258),n(260),n(262),n(264),n(266);var o=n(18),i=n(24),a=n(57),s=n(268),l=n(206),c=n(202),u=a.findDOMNode,d=c.EDITOR_THEMES,p=16384,h=[\"CodeMirror-linenumbers\",\"CodeMirror-foldgutter\",\"CodeMirror-lint-markers\"];n(269),n(270),n(271),n(272),n(273),n(275),n(276),n(277),n(278),n(279),n(280),n(281),n(282),n(283);var g=n(284),f=n(199);n(289);var m=\"cobalt\",A=\"14px\",M=/^(\\s*)#\\s*/,w=/^(\\s*)\\/\\/+\\s?/,v=/\\S/,b=[\"javascript\",\"htmlmixed\",\"css\"],y={caseFold:!0,multiline:!0},x=i.createClass({displayName:\"Editor\",getThemes:function(){return d},setMode:function(e){/^(javascript|css|xml|rules|markdown)$/i.test(e)?e=RegExp.$1.toLowerCase():/^(js|pac|jsx|json)$/i.test(e)?e=\"javascript\":/^(html|wtpl)$/i.test(e)?e=\"htmlmixed\":/^md$/i.test(e)&&(e=\"markdown\"),this._mode!==e&&(this._mode=e,this._editor&&this._editor.setOption(\"mode\",e),this._foldGutter&&(this._editor.setOption(\"foldGutter\",!1),this._editor.setOption(\"foldGutter\",!0)),this.setFoldGutter(this.props.foldGutter))},setValue:function(e){e=this._value=null==e?\"\":e+\"\",this._editor&&this._editor.getValue()!=e&&this._editor.setValue(e)},setHistory:function(e,t){var n=this.props.modal;if(n){var r=n.getActive(),o=n.list,i=o.length,a=this._editorHistoryMap||{};if(i?i!==this._listLen&&Object.keys(a).forEach(function(e){-1===o.indexOf(e)&&delete a[e]}):this._editorHistoryMap={},this._listLen=i,r){var s=r.name;this._editorHistoryMap=a,e?(this._editorCurrentHistoryKey=s,this._editor.clearHistory()):this._editorCurrentHistoryKey!==s&&(a[this._editorCurrentHistoryKey]=t,this._editorCurrentHistoryKey=s,this._editor.clearHistory(),a[s]&&(this._editor.setHistory(a[s]),delete a[s]))}}},getValue:function(){return this._editor?\"\":this._editor.getValue()},setTheme:function(e){e=this._theme=e||m,this._editor&&this._editor.setOption(\"theme\",e)},setFontSize:function(e){e=this._fontSize=e||A,this._editor&&(u(this.refs.editor).style.fontSize=e)},showLineNumber:function(e){e=this._showLineNumber=e===!1?!1:!0,this._editor&&this._editor.setOption(\"lineNumbers\",e)},showLineWrapping:function(e){e=this._showLineNumber=e===!1?!1:!0,this._editor&&this._editor.setOption(\"lineWrapping\",e)},setReadOnly:function(e){e=this._readOnly=e===!1||\"false\"===e?!1:!0,this._editor&&this._editor.setOption(\"readOnly\",e)},handleKeyUp:function(e,t){clearTimeout(this._timer);var n=8===t.keyCode;if(n||13===t.keyCode){var o=this._editor;this._timer=setTimeout(function(){r(\".CodeMirror-hints\")||(o._byDelete=!0,o._byEnter=!n,o.execCommand(\"autocomplete\"))},300)}},setAutoComplete:function(){var e=this.isRulesEditor(),t=e&&!this.props.readOnly?g.getExtraKeys():{};/\\(Macintosh;/i.test(window.navigator.userAgent)||(t[\"Ctrl-F\"]=\"findPersistent\"),t[\"Cmd-F\"]=\"findPersistent\";var n=this._editor;n.setOption(\"extraKeys\",t),n.off(\"keyup\",this.handleKeyUp),e&&n.on(\"keyup\",this.handleKeyUp)},setFoldGutter:function(e){\"rules\"!==this.props.mode&&(e=e!==!1&&-1!==b.indexOf(this._mode),this._foldGutter!==e&&this._editor&&(this._foldGutter=e,this._editor.setOption(\"foldGutter\",e),this._editor.setOption(\"gutters\",e?h:[])))},isRulesEditor:function(){return\"rules\"===this.props.mode||\"rules\"===this._mode},componentDidMount:function(){function e(){n=null;var t=c.offsetHeight||0,r=c.offsetWidth||0;10>t||10>r?(n&&clearTimeout(n),n=setTimeout(e,300)):p.setSize(r,t)}function t(){n=n||setTimeout(e,30)}var n,r,i,a,l=this,c=u(l.refs.editor),d=o(c),p=s(c),h=function b(e,t,n){if(d.is(\":visible\")){var r=n?null:p.getCursor(),o=p.getValue();if(e!==i)r&&a&&i&&r.ch===a.ch&&r.line===a.line&&(r.ch=Math.max(r.ch+(t?i.length:-i.length),0)),i=e;else if(t)if(r)r.ch=Math.max(r.ch-i.length,0);else{var s=o.length;r={line:s,ch:s}}e?f.editorMatchedCount=o.toLowerCase().split(e.toLowerCase()).length-1:f.editorMatchedCount=0,r=p.getSearchCursor(e,r,y),a=null;var l=t?r.findPrevious():r.findNext();l?(p.setSelection(r.from(),r.to()),p.scrollIntoView({from:r.from(),to:r.to()},20)):n?(a=p.getCursor(),a&&p.setSelection(a,a)):b(e,t,!0),a=a||p.getCursor()}},m=function(e,t){h(t,\"findEditorPrev\"===e.type)};l._editor=p,f.on(\"findEditorNext\",m),f.on(\"findEditorPrev\",m),f.on(\"updatePlugins\",function(){l.isRulesEditor()&&(r&&clearTimeout(r),l.props.hide?(r=null,l._waitingUpdate=!0):r=setTimeout(function(){r=null,l.isRulesEditor()&&(l.props.hide?l._waitingUpdate=!0:(l._waitingUpdate=!1,p.setOption(\"mode\",\"\"),p.setOption(\"mode\",\"rules\")))},600))}),p.on(\"change\",function(e){\"function\"==typeof l.props.onChange&&p.getValue()!==(l.props.value||\"\")&&l.props.onChange.call(l,e)}),p.on(\"mousedown\",function(e,t){if(t.ctrlKey||t.metaKey){var n=o(t.target);(n.hasClass(\"cm-js-type\")||n.hasClass(\"cm-js-at\")||n.hasClass(\"cm-js-http-url\"))&&t.preventDefault()}}),l._init(!0),o(c).find(\".CodeMirror\").addClass(\"fill\"),setTimeout(e,10),o(window).on(\"resize\",t),f.on(\"editorResize\",t);var A=function(e,t){return Math.max(0,e+t)};o(c).on(\"dblclick\",\".CodeMirror-linenumber\",function(e){var t=parseInt(o(e.target).text(),10);if(t>0){var n=t-1,r=p.getLine(n);if(r&&r.trim()){var i=l.isRulesEditor(),a=i?M:w,s=r;r=a.test(r)?r.replace(a,\"$1\"):(i?\"#\":\"//\")+(/^\\s/.test(r)?\"\":\" \")+r;var c,u=p.listSelections(),d=u&&u.length,h=r.length-s.length;if(u&&u.length)for(var g=0;d>g;g++){var m=u[g],v=m.head.line,b=m.anchor.line;if(v===n){c=!0,m.head.ch=A(m.head.ch,h),b===v&&m.head!==m.anchor&&(m.anchor.ch=A(m.anchor.ch,h));break}if(b===n){c=!0,m.anchor.ch=A(m.anchor.ch,h);break}}p.replaceRange(r+\"\\n\",{line:n,ch:0},{line:t,ch:0}),c&&p.setSelections(u),f.trigger(\"toggleCommentInEditor\")}}}),o(c).on(\"keydown\",function(e){var t=l.isRulesEditor(),n=\"javascript\"==l._mode;if(t){var r={name:l.props.mode,url:location.href};if(!e.ctrlKey&&!e.metaKey&&112===e.keyCode){var o=g.getHelpUrl(l._editor,r);if(!o)return;return window.open(o),e.stopPropagation(),e.preventDefault(),!0}try{var i=window.parent.onWhistleRulesEditorKeyDown;if(\"function\"==typeof i&&i(e,r)===!1)return e.stopPropagation(),e.preventDefault(),!0}catch(e){}}if(e.shiftKey&&(e.metaKey||e.ctrlKey)){var a=l.props.onFormat,s=l.props.onInspect;\"function\"==typeof a&&70===e.keyCode?a(e):\"function\"==typeof s&&73===e.keyCode&&s(e)}if((t||n)&&(e.ctrlKey||e.metaKey)&&191==e.keyCode){var c=p.listSelections();if(c&&c.length){var u,d=t?M:w,h=e.shiftKey,f=[];c.forEach(function(e){var n,r,o,i=e.anchor,a=e.head,s=[];i.line>a.line&&(o=i,i=a,a=o);for(var l=i.line;l<=a.line;l++){var c=p.getLine(l);d.test(c)?n=!0:v.test(c)&&(r=!0),s.push(c)}if(!(u=!n&&!r)){var g,m,A;r?(g=s.length-1,m=s[0],A=s[g],s=s.map(function(e){return v.test(e)?h&&d.test(e)?e.replace(d,\"$1\"):(t?\"# \":\"// \")+e:e})):(m=s[0],g=s.length-1,A=s[g],s=s.map(function(e){return e.replace(d,\"$1\")})),0!=i.ch&&(i.ch+=s[0].length-m.length,i.ch<0&&(i.ch=0)),0!=a.ch&&a!=i&&(a.ch+=s[g].length-A.length,a.ch<0&&(a.ch=0)),o?(p.replaceRange(s.join(\"\\n\")+\"\\n\",{line:a.line+1,ch:0},{line:i.line,ch:0}),f.push({anchor:a,head:i})):(p.replaceRange(s.join(\"\\n\")+\"\\n\",{line:i.line,ch:0},{line:a.line+1,ch:0}),f.push({anchor:i,head:a}))}}),u||p.setSelections(f)}}})},_init:function(e){var t=this,n=t.props.mode;t._waitingUpdate&&\"rules\"===n&&(t._editor.setOption(\"mode\",\"\"),t._mode=\"\"),t.setMode(n),t._waitingUpdate=!1;var r=t.props.value,o=t._editor.getHistory();if(e&&r&&r.length>p){var i=l.info(\"Loading...\");t.timer=setTimeout(function(){i.hide(),t.timer=null,t.setValue(t.props.value),t.setHistory(e,o)},500)}else t.timer||(t.setValue(r),t.setHistory(e,o));t.setTheme(t.props.theme),t.setFontSize(t.props.fontSize),t.setTheme(t.props.theme),t.showLineNumber(t.props.lineNumbers||!1),t.showLineWrapping(t.props.lineWrapping||!1),t.setReadOnly(t.props.readOnly||!1),t.setAutoComplete(),t.setFoldGutter(t.props.foldGutter)},componentDidUpdate:function(){this._init()},render:function(){return i.createElement(\"div\",{tabIndex:\"0\",ref:\"editor\",className:\"fill v-box w-list-content w-fix-drag\"})}});e.exports=x},function(e,t,n){var r=n(225);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:transparent}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:transparent}.cm-fat-cursor{caret-color:transparent}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta,.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none}\",\"\"])},function(e,t,n){var r=n(227);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-neat span.cm-comment{color:#a86}.cm-s-neat span.cm-keyword{line-height:1em;font-weight:700;color:blue}.cm-s-neat span.cm-string{color:#a22}.cm-s-neat span.cm-builtin{line-height:1em;font-weight:700;color:#077}.cm-s-neat span.cm-special{line-height:1em;font-weight:700;color:#0aa}.cm-s-neat span.cm-variable{color:#000}.cm-s-neat span.cm-atom,.cm-s-neat span.cm-number{color:#3a3}.cm-s-neat span.cm-meta{color:#555}.cm-s-neat span.cm-link{color:#3a3}.cm-s-neat .CodeMirror-activeline-background{background:#e8f2ff}.cm-s-neat .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important}\",\"\"])},function(e,t,n){var r=n(229);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-elegant span.cm-atom,.cm-s-elegant span.cm-number,.cm-s-elegant span.cm-string{color:#762}.cm-s-elegant span.cm-comment{color:#262;font-style:italic;line-height:1em}.cm-s-elegant span.cm-meta{color:#555;font-style:italic;line-height:1em}.cm-s-elegant span.cm-variable{color:#000}.cm-s-elegant span.cm-variable-2{color:#b11}.cm-s-elegant span.cm-qualifier{color:#555}.cm-s-elegant span.cm-keyword{color:#730}.cm-s-elegant span.cm-builtin{color:#30a}.cm-s-elegant span.cm-link{color:#762}.cm-s-elegant span.cm-error{background-color:#fdd}.cm-s-elegant .CodeMirror-activeline-background{background:#e8f2ff}.cm-s-elegant .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important}\",\"\"])},function(e,t,n){var r=n(231);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-erlang-dark.CodeMirror{background:#002240;color:#fff}.cm-s-erlang-dark div.CodeMirror-selected{background:#b36539}.cm-s-erlang-dark .CodeMirror-line::selection,.cm-s-erlang-dark .CodeMirror-line>span::selection,.cm-s-erlang-dark .CodeMirror-line>span>span::selection{background:rgba(179,101,57,.99)}.cm-s-erlang-dark .CodeMirror-line::-moz-selection,.cm-s-erlang-dark .CodeMirror-line>span::-moz-selection,.cm-s-erlang-dark .CodeMirror-line>span>span::-moz-selection{background:rgba(179,101,57,.99)}.cm-s-erlang-dark .CodeMirror-gutters{background:#002240;border-right:1px solid #aaa}.cm-s-erlang-dark .CodeMirror-guttermarker{color:#fff}.cm-s-erlang-dark .CodeMirror-guttermarker-subtle,.cm-s-erlang-dark .CodeMirror-linenumber{color:#d0d0d0}.cm-s-erlang-dark .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-erlang-dark span.cm-quote{color:#ccc}.cm-s-erlang-dark span.cm-atom{color:#f133f1}.cm-s-erlang-dark span.cm-attribute{color:#ff80e1}.cm-s-erlang-dark span.cm-bracket{color:#ff9d00}.cm-s-erlang-dark span.cm-builtin{color:#eaa}.cm-s-erlang-dark span.cm-comment{color:#77f}.cm-s-erlang-dark span.cm-def{color:#e7a}.cm-s-erlang-dark span.cm-keyword{color:#ffee80}.cm-s-erlang-dark span.cm-meta{color:#50fefe}.cm-s-erlang-dark span.cm-number{color:#ffd0d0}.cm-s-erlang-dark span.cm-operator{color:#d55}.cm-s-erlang-dark span.cm-property,.cm-s-erlang-dark span.cm-qualifier{color:#ccc}.cm-s-erlang-dark span.cm-special{color:#fbb}.cm-s-erlang-dark span.cm-string{color:#3ad900}.cm-s-erlang-dark span.cm-string-2{color:#ccc}.cm-s-erlang-dark span.cm-tag{color:#9effff}.cm-s-erlang-dark span.cm-variable{color:#50fe50}.cm-s-erlang-dark span.cm-variable-2{color:#e0e}.cm-s-erlang-dark span.cm-type,.cm-s-erlang-dark span.cm-variable-3{color:#ccc}.cm-s-erlang-dark span.cm-error{color:#9d1e15}.cm-s-erlang-dark .CodeMirror-activeline-background{background:#013461}.cm-s-erlang-dark .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(233);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-night.CodeMirror{background:#0a001f;color:#f8f8f8}.cm-s-night div.CodeMirror-selected{background:#447}.cm-s-night .CodeMirror-line::selection,.cm-s-night .CodeMirror-line>span::selection,.cm-s-night .CodeMirror-line>span>span::selection{background:rgba(68,68,119,.99)}.cm-s-night .CodeMirror-line::-moz-selection,.cm-s-night .CodeMirror-line>span::-moz-selection,.cm-s-night .CodeMirror-line>span>span::-moz-selection{background:rgba(68,68,119,.99)}.cm-s-night .CodeMirror-gutters{background:#0a001f;border-right:1px solid #aaa}.cm-s-night .CodeMirror-guttermarker{color:#fff}.cm-s-night .CodeMirror-guttermarker-subtle{color:#bbb}.cm-s-night .CodeMirror-linenumber{color:#f8f8f8}.cm-s-night .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-night span.cm-comment{color:#8900d1}.cm-s-night span.cm-atom{color:#845dc4}.cm-s-night span.cm-attribute,.cm-s-night span.cm-number{color:#ffd500}.cm-s-night span.cm-keyword{color:#599eff}.cm-s-night span.cm-string{color:#37f14a}.cm-s-night span.cm-meta{color:#7678e2}.cm-s-night span.cm-tag,.cm-s-night span.cm-variable-2{color:#99b2ff}.cm-s-night span.cm-def,.cm-s-night span.cm-type,.cm-s-night span.cm-variable-3{color:#fff}.cm-s-night span.cm-bracket{color:#8da6ce}.cm-s-night span.cm-builtin,.cm-s-night span.cm-special{color:#ff9e59}.cm-s-night span.cm-link{color:#845dc4}.cm-s-night span.cm-error{color:#9d1e15}.cm-s-night .CodeMirror-activeline-background{background:#1c005a}.cm-s-night .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(235);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-monokai.CodeMirror{background:#272822;color:#f8f8f2}.cm-s-monokai div.CodeMirror-selected{background:#49483e}.cm-s-monokai .CodeMirror-line::selection,.cm-s-monokai .CodeMirror-line>span::selection,.cm-s-monokai .CodeMirror-line>span>span::selection{background:rgba(73,72,62,.99)}.cm-s-monokai .CodeMirror-line::-moz-selection,.cm-s-monokai .CodeMirror-line>span::-moz-selection,.cm-s-monokai .CodeMirror-line>span>span::-moz-selection{background:rgba(73,72,62,.99)}.cm-s-monokai .CodeMirror-gutters{background:#272822;border-right:0}.cm-s-monokai .CodeMirror-guttermarker{color:#fff}.cm-s-monokai .CodeMirror-guttermarker-subtle,.cm-s-monokai .CodeMirror-linenumber{color:#d0d0d0}.cm-s-monokai .CodeMirror-cursor{border-left:1px solid #f8f8f0}.cm-s-monokai span.cm-comment{color:#75715e}.cm-s-monokai span.cm-atom,.cm-s-monokai span.cm-number{color:#ae81ff}.cm-s-monokai span.cm-comment.cm-attribute{color:#97b757}.cm-s-monokai span.cm-comment.cm-def{color:#bc9262}.cm-s-monokai span.cm-comment.cm-tag{color:#bc6283}.cm-s-monokai span.cm-comment.cm-type{color:#5998a6}.cm-s-monokai span.cm-attribute,.cm-s-monokai span.cm-property{color:#a6e22e}.cm-s-monokai span.cm-keyword{color:#f92672}.cm-s-monokai span.cm-builtin{color:#66d9ef}.cm-s-monokai span.cm-string{color:#e6db74}.cm-s-monokai span.cm-variable{color:#f8f8f2}.cm-s-monokai span.cm-variable-2{color:#9effff}.cm-s-monokai span.cm-type,.cm-s-monokai span.cm-variable-3{color:#66d9ef}.cm-s-monokai span.cm-def{color:#fd971f}.cm-s-monokai span.cm-bracket{color:#f8f8f2}.cm-s-monokai span.cm-tag{color:#f92672}.cm-s-monokai span.cm-header,.cm-s-monokai span.cm-link{color:#ae81ff}.cm-s-monokai span.cm-error{background:#f92672;color:#f8f8f0}.cm-s-monokai .CodeMirror-activeline-background{background:#373831}.cm-s-monokai .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(237);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-cobalt.CodeMirror{background:#002240;color:#fff}.cm-s-cobalt div.CodeMirror-selected{background:#b36539}.cm-s-cobalt .CodeMirror-line::selection,.cm-s-cobalt .CodeMirror-line>span::selection,.cm-s-cobalt .CodeMirror-line>span>span::selection{background:rgba(179,101,57,.99)}.cm-s-cobalt .CodeMirror-line::-moz-selection,.cm-s-cobalt .CodeMirror-line>span::-moz-selection,.cm-s-cobalt .CodeMirror-line>span>span::-moz-selection{background:rgba(179,101,57,.99)}.cm-s-cobalt .CodeMirror-gutters{background:#002240;border-right:1px solid #aaa}.cm-s-cobalt .CodeMirror-guttermarker{color:#ffee80}.cm-s-cobalt .CodeMirror-guttermarker-subtle,.cm-s-cobalt .CodeMirror-linenumber{color:#d0d0d0}.cm-s-cobalt .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-cobalt span.cm-comment{color:#08f}.cm-s-cobalt span.cm-atom{color:#845dc4}.cm-s-cobalt span.cm-attribute,.cm-s-cobalt span.cm-number{color:#ff80e1}.cm-s-cobalt span.cm-keyword{color:#ffee80}.cm-s-cobalt span.cm-string{color:#3ad900}.cm-s-cobalt span.cm-meta{color:#ff9d00}.cm-s-cobalt span.cm-tag,.cm-s-cobalt span.cm-variable-2{color:#9effff}.cm-s-cobalt .cm-type,.cm-s-cobalt span.cm-def,.cm-s-cobalt span.cm-variable-3{color:#fff}.cm-s-cobalt span.cm-bracket{color:#d8d8d8}.cm-s-cobalt span.cm-builtin,.cm-s-cobalt span.cm-special{color:#ff9e59}.cm-s-cobalt span.cm-link{color:#845dc4}.cm-s-cobalt span.cm-error{color:#9d1e15}.cm-s-cobalt .CodeMirror-activeline-background{background:#002d57}.cm-s-cobalt .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(239);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-eclipse span.cm-meta{color:#ff1717}.cm-s-eclipse span.cm-keyword{line-height:1em;font-weight:700;color:#7f0055}.cm-s-eclipse span.cm-atom{color:#219}.cm-s-eclipse span.cm-number{color:#164}.cm-s-eclipse span.cm-def{color:#00f}.cm-s-eclipse span.cm-variable{color:#000}.cm-s-eclipse span.cm-type,.cm-s-eclipse span.cm-variable-2,.cm-s-eclipse span.cm-variable-3{color:#0000c0}.cm-s-eclipse span.cm-operator,.cm-s-eclipse span.cm-property{color:#000}.cm-s-eclipse span.cm-comment{color:#3f7f5f}.cm-s-eclipse span.cm-string{color:#2a00ff}.cm-s-eclipse span.cm-string-2{color:#f50}.cm-s-eclipse span.cm-qualifier{color:#555}.cm-s-eclipse span.cm-builtin{color:#30a}.cm-s-eclipse span.cm-bracket{color:#cc7}.cm-s-eclipse span.cm-tag{color:#170}.cm-s-eclipse span.cm-attribute{color:#00c}.cm-s-eclipse span.cm-link{color:#219}.cm-s-eclipse span.cm-error{color:red}.cm-s-eclipse .CodeMirror-activeline-background{background:#e8f2ff}.cm-s-eclipse .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important}\",\"\"])},function(e,t,n){var r=n(241);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-rubyblue.CodeMirror{background:#112435;color:#fff}.cm-s-rubyblue div.CodeMirror-selected{background:#38566f}.cm-s-rubyblue .CodeMirror-line::selection,.cm-s-rubyblue .CodeMirror-line>span::selection,.cm-s-rubyblue .CodeMirror-line>span>span::selection{background:rgba(56,86,111,.99)}.cm-s-rubyblue .CodeMirror-line::-moz-selection,.cm-s-rubyblue .CodeMirror-line>span::-moz-selection,.cm-s-rubyblue .CodeMirror-line>span>span::-moz-selection{background:rgba(56,86,111,.99)}.cm-s-rubyblue .CodeMirror-gutters{background:#1f4661;border-right:7px solid #3e7087}.cm-s-rubyblue .CodeMirror-guttermarker{color:#fff}.cm-s-rubyblue .CodeMirror-guttermarker-subtle{color:#3e7087}.cm-s-rubyblue .CodeMirror-linenumber{color:#fff}.cm-s-rubyblue .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-rubyblue span.cm-comment{color:#999;font-style:italic;line-height:1em}.cm-s-rubyblue span.cm-atom{color:#f4c20b}.cm-s-rubyblue span.cm-attribute,.cm-s-rubyblue span.cm-number{color:#82c6e0}.cm-s-rubyblue span.cm-keyword{color:#f0f}.cm-s-rubyblue span.cm-string{color:#f08047}.cm-s-rubyblue span.cm-meta{color:#f0f}.cm-s-rubyblue span.cm-tag,.cm-s-rubyblue span.cm-variable-2{color:#7bd827}.cm-s-rubyblue span.cm-def,.cm-s-rubyblue span.cm-type,.cm-s-rubyblue span.cm-variable-3{color:#fff}.cm-s-rubyblue span.cm-bracket{color:#f0f}.cm-s-rubyblue span.cm-link{color:#f4c20b}.cm-s-rubyblue span.CodeMirror-matchingbracket{color:#f0f!important}.cm-s-rubyblue span.cm-builtin,.cm-s-rubyblue span.cm-special{color:#ff9d00}.cm-s-rubyblue span.cm-error{color:#af2018}.cm-s-rubyblue .CodeMirror-activeline-background{background:#173047}\",\"\"])},function(e,t,n){var r=n(243);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-lesser-dark{line-height:1.3em}.cm-s-lesser-dark.CodeMirror{background:#262626;color:#ebefe7;text-shadow:0 -1px 1px #262626}.cm-s-lesser-dark div.CodeMirror-selected{background:#45443b}.cm-s-lesser-dark .CodeMirror-line::selection,.cm-s-lesser-dark .CodeMirror-line>span::selection,.cm-s-lesser-dark .CodeMirror-line>span>span::selection{background:rgba(69,68,59,.99)}.cm-s-lesser-dark .CodeMirror-line::-moz-selection,.cm-s-lesser-dark .CodeMirror-line>span::-moz-selection,.cm-s-lesser-dark .CodeMirror-line>span>span::-moz-selection{background:rgba(69,68,59,.99)}.cm-s-lesser-dark .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-lesser-dark pre{padding:0 8px}.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket{color:#7efc7e}.cm-s-lesser-dark .CodeMirror-gutters{background:#262626;border-right:1px solid #aaa}.cm-s-lesser-dark .CodeMirror-guttermarker{color:#599eff}.cm-s-lesser-dark .CodeMirror-guttermarker-subtle,.cm-s-lesser-dark .CodeMirror-linenumber{color:#777}.cm-s-lesser-dark span.cm-header{color:#a0a}.cm-s-lesser-dark span.cm-quote{color:#090}.cm-s-lesser-dark span.cm-keyword{color:#599eff}.cm-s-lesser-dark span.cm-atom{color:#c2b470}.cm-s-lesser-dark span.cm-number{color:#b35e4d}.cm-s-lesser-dark span.cm-def{color:#fff}.cm-s-lesser-dark span.cm-variable{color:#d9bf8c}.cm-s-lesser-dark span.cm-variable-2{color:#669199}.cm-s-lesser-dark span.cm-type,.cm-s-lesser-dark span.cm-variable-3{color:#fff}.cm-s-lesser-dark span.cm-operator,.cm-s-lesser-dark span.cm-property{color:#92a75c}.cm-s-lesser-dark span.cm-comment{color:#666}.cm-s-lesser-dark span.cm-string{color:#bcd279}.cm-s-lesser-dark span.cm-string-2{color:#f50}.cm-s-lesser-dark span.cm-meta{color:#738c73}.cm-s-lesser-dark span.cm-qualifier{color:#555}.cm-s-lesser-dark span.cm-builtin{color:#ff9e59}.cm-s-lesser-dark span.cm-bracket{color:#ebefe7}.cm-s-lesser-dark span.cm-tag{color:#669199}.cm-s-lesser-dark span.cm-attribute{color:#81a4d5}.cm-s-lesser-dark span.cm-hr{color:#999}.cm-s-lesser-dark span.cm-link{color:#7070e6}.cm-s-lesser-dark span.cm-error{color:#9d1e15}.cm-s-lesser-dark .CodeMirror-activeline-background{background:#3c3a3a}.cm-s-lesser-dark .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(245);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-xq-dark.CodeMirror{background:#0a001f;color:#f8f8f8}.cm-s-xq-dark div.CodeMirror-selected{background:#27007a}.cm-s-xq-dark .CodeMirror-line::selection,.cm-s-xq-dark .CodeMirror-line>span::selection,.cm-s-xq-dark .CodeMirror-line>span>span::selection{background:rgba(39,0,122,.99)}.cm-s-xq-dark .CodeMirror-line::-moz-selection,.cm-s-xq-dark .CodeMirror-line>span::-moz-selection,.cm-s-xq-dark .CodeMirror-line>span>span::-moz-selection{background:rgba(39,0,122,.99)}.cm-s-xq-dark .CodeMirror-gutters{background:#0a001f;border-right:1px solid #aaa}.cm-s-xq-dark .CodeMirror-guttermarker{color:#ffbd40}.cm-s-xq-dark .CodeMirror-guttermarker-subtle,.cm-s-xq-dark .CodeMirror-linenumber{color:#f8f8f8}.cm-s-xq-dark .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-xq-dark span.cm-keyword{color:#ffbd40}.cm-s-xq-dark span.cm-atom{color:#6c8cd5}.cm-s-xq-dark span.cm-number{color:#164}.cm-s-xq-dark span.cm-def{color:#fff;text-decoration:underline}.cm-s-xq-dark span.cm-variable{color:#fff}.cm-s-xq-dark span.cm-variable-2{color:#eee}.cm-s-xq-dark span.cm-type,.cm-s-xq-dark span.cm-variable-3{color:#ddd}.cm-s-xq-dark span.cm-comment{color:gray}.cm-s-xq-dark span.cm-string{color:#9fee00}.cm-s-xq-dark span.cm-meta{color:#ff0}.cm-s-xq-dark span.cm-qualifier{color:#fff700}.cm-s-xq-dark span.cm-builtin{color:#30a}.cm-s-xq-dark span.cm-bracket{color:#cc7}.cm-s-xq-dark span.cm-tag{color:#ffbd40}.cm-s-xq-dark span.cm-attribute{color:#fff700}.cm-s-xq-dark span.cm-error{color:red}.cm-s-xq-dark .CodeMirror-activeline-background{background:#27282e}.cm-s-xq-dark .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(247);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-xq-light span.cm-keyword{line-height:1em;font-weight:700;color:#5a5cad}.cm-s-xq-light span.cm-atom{color:#6c8cd5}.cm-s-xq-light span.cm-number{color:#164}.cm-s-xq-light span.cm-def{text-decoration:underline}.cm-s-xq-light span.cm-type,.cm-s-xq-light span.cm-variable,.cm-s-xq-light span.cm-variable-2,.cm-s-xq-light span.cm-variable-3{color:#000}.cm-s-xq-light span.cm-comment{color:#0080ff;font-style:italic}.cm-s-xq-light span.cm-string{color:red}.cm-s-xq-light span.cm-meta{color:#ff0}.cm-s-xq-light span.cm-qualifier{color:grey}.cm-s-xq-light span.cm-builtin{color:#7ea656}.cm-s-xq-light span.cm-bracket{color:#cc7}.cm-s-xq-light span.cm-tag{color:#3f7f7f}.cm-s-xq-light span.cm-attribute{color:#7f007f}.cm-s-xq-light span.cm-error{color:red}.cm-s-xq-light .CodeMirror-activeline-background{background:#e8f2ff}.cm-s-xq-light .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important;background:#ff0}\",\"\"])},function(e,t,n){var r=n(249);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,'.cm-s-ambiance .cm-header{color:blue}.cm-s-ambiance .cm-quote{color:#24c2c7}.cm-s-ambiance .cm-keyword{color:#cda869}.cm-s-ambiance .cm-atom{color:#cf7ea9}.cm-s-ambiance .cm-number{color:#78cf8a}.cm-s-ambiance .cm-def{color:#aac6e3}.cm-s-ambiance .cm-variable{color:#ffb795}.cm-s-ambiance .cm-variable-2{color:#eed1b3}.cm-s-ambiance .cm-type,.cm-s-ambiance .cm-variable-3{color:#faded3}.cm-s-ambiance .cm-property{color:#eed1b3}.cm-s-ambiance .cm-operator{color:#fa8d6a}.cm-s-ambiance .cm-comment{color:#555;font-style:italic}.cm-s-ambiance .cm-string{color:#8f9d6a}.cm-s-ambiance .cm-string-2{color:#9d937c}.cm-s-ambiance .cm-meta{color:#d2a8a1}.cm-s-ambiance .cm-qualifier{color:#ff0}.cm-s-ambiance .cm-builtin{color:#99c}.cm-s-ambiance .cm-bracket{color:#24c2c7}.cm-s-ambiance .cm-tag{color:#fee4ff}.cm-s-ambiance .cm-attribute{color:#9b859d}.cm-s-ambiance .cm-hr{color:pink}.cm-s-ambiance .cm-link{color:#f4c20b}.cm-s-ambiance .cm-special{color:#ff9d00}.cm-s-ambiance .cm-error{color:#af2018}.cm-s-ambiance .CodeMirror-matchingbracket{color:#0f0}.cm-s-ambiance .CodeMirror-nonmatchingbracket{color:#f22}.cm-s-ambiance div.CodeMirror-selected{background:hsla(0,0%,100%,.15)}.cm-s-ambiance .CodeMirror-line::selection,.cm-s-ambiance .CodeMirror-line>span::selection,.cm-s-ambiance .CodeMirror-line>span>span::selection,.cm-s-ambiance.CodeMirror-focused div.CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-ambiance .CodeMirror-line::-moz-selection,.cm-s-ambiance .CodeMirror-line>span::-moz-selection,.cm-s-ambiance .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-ambiance.CodeMirror{line-height:1.4em;color:#e6e1dc;background-color:#202020;box-shadow:inset 0 0 10px #000}.cm-s-ambiance .CodeMirror-gutters{background:#3d3d3d;border-right:1px solid #4d4d4d;box-shadow:0 10px 20px #000}.cm-s-ambiance .CodeMirror-linenumber{text-shadow:0 1px 1px #4d4d4d;color:#111;padding:0 5px}.cm-s-ambiance .CodeMirror-guttermarker{color:#aaa}.cm-s-ambiance .CodeMirror-guttermarker-subtle{color:#111}.cm-s-ambiance .CodeMirror-cursor{border-left:1px solid #7991e8}.cm-s-ambiance .CodeMirror-activeline-background{background:none repeat scroll 0 0 hsla(0,0%,100%,.031)}.cm-s-ambiance .CodeMirror-gutters,.cm-s-ambiance.CodeMirror{background-image:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC\")}',\"\"]);\n},function(e,t,n){var r=n(251);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-blackboard.CodeMirror{background:#0c1021;color:#f8f8f8}.cm-s-blackboard div.CodeMirror-selected{background:#253b76}.cm-s-blackboard .CodeMirror-line::selection,.cm-s-blackboard .CodeMirror-line>span::selection,.cm-s-blackboard .CodeMirror-line>span>span::selection{background:rgba(37,59,118,.99)}.cm-s-blackboard .CodeMirror-line::-moz-selection,.cm-s-blackboard .CodeMirror-line>span::-moz-selection,.cm-s-blackboard .CodeMirror-line>span>span::-moz-selection{background:rgba(37,59,118,.99)}.cm-s-blackboard .CodeMirror-gutters{background:#0c1021;border-right:0}.cm-s-blackboard .CodeMirror-guttermarker{color:#fbde2d}.cm-s-blackboard .CodeMirror-guttermarker-subtle,.cm-s-blackboard .CodeMirror-linenumber{color:#888}.cm-s-blackboard .CodeMirror-cursor{border-left:1px solid #a7a7a7}.cm-s-blackboard .cm-keyword{color:#fbde2d}.cm-s-blackboard .cm-atom,.cm-s-blackboard .cm-number{color:#d8fa3c}.cm-s-blackboard .cm-def{color:#8da6ce}.cm-s-blackboard .cm-variable{color:#ff6400}.cm-s-blackboard .cm-operator{color:#fbde2d}.cm-s-blackboard .cm-comment{color:#aeaeae}.cm-s-blackboard .cm-string,.cm-s-blackboard .cm-string-2{color:#61ce3c}.cm-s-blackboard .cm-meta{color:#d8fa3c}.cm-s-blackboard .cm-attribute,.cm-s-blackboard .cm-builtin,.cm-s-blackboard .cm-tag{color:#8da6ce}.cm-s-blackboard .cm-header{color:#ff6400}.cm-s-blackboard .cm-hr{color:#aeaeae}.cm-s-blackboard .cm-link{color:#8da6ce}.cm-s-blackboard .cm-error{background:#9d1e15;color:#f8f8f8}.cm-s-blackboard .CodeMirror-activeline-background{background:#3c3636}.cm-s-blackboard .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(253);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-vibrant-ink.CodeMirror{background:#000;color:#fff}.cm-s-vibrant-ink div.CodeMirror-selected{background:#35493c}.cm-s-vibrant-ink .CodeMirror-line::selection,.cm-s-vibrant-ink .CodeMirror-line>span::selection,.cm-s-vibrant-ink .CodeMirror-line>span>span::selection{background:rgba(53,73,60,.99)}.cm-s-vibrant-ink .CodeMirror-line::-moz-selection,.cm-s-vibrant-ink .CodeMirror-line>span::-moz-selection,.cm-s-vibrant-ink .CodeMirror-line>span>span::-moz-selection{background:rgba(53,73,60,.99)}.cm-s-vibrant-ink .CodeMirror-gutters{background:#002240;border-right:1px solid #aaa}.cm-s-vibrant-ink .CodeMirror-guttermarker{color:#fff}.cm-s-vibrant-ink .CodeMirror-guttermarker-subtle,.cm-s-vibrant-ink .CodeMirror-linenumber{color:#d0d0d0}.cm-s-vibrant-ink .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-vibrant-ink .cm-keyword{color:#cc7832}.cm-s-vibrant-ink .cm-atom{color:#fc0}.cm-s-vibrant-ink .cm-number{color:#ffee98}.cm-s-vibrant-ink .cm-def{color:#8da6ce}.cm-s-vibrant span.cm-def,.cm-s-vibrant span.cm-tag,.cm-s-vibrant span.cm-type,.cm-s-vibrant-ink span.cm-variable-2,.cm-s-vibrant-ink span.cm-variable-3{color:#ffc66d}.cm-s-vibrant-ink .cm-operator{color:#888}.cm-s-vibrant-ink .cm-comment{color:gray;font-weight:700}.cm-s-vibrant-ink .cm-string{color:#a5c25c}.cm-s-vibrant-ink .cm-string-2{color:red}.cm-s-vibrant-ink .cm-meta{color:#d8fa3c}.cm-s-vibrant-ink .cm-attribute,.cm-s-vibrant-ink .cm-builtin,.cm-s-vibrant-ink .cm-tag{color:#8da6ce}.cm-s-vibrant-ink .cm-header{color:#ff6400}.cm-s-vibrant-ink .cm-hr{color:#aeaeae}.cm-s-vibrant-ink .cm-link{color:#5656f3}.cm-s-vibrant-ink .cm-error{border-bottom:1px solid red}.cm-s-vibrant-ink .CodeMirror-activeline-background{background:#27282e}.cm-s-vibrant-ink .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(255);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".solarized.base03{color:#002b36}.solarized.base02{color:#073642}.solarized.base01{color:#586e75}.solarized.base00{color:#657b83}.solarized.base0{color:#839496}.solarized.base1{color:#93a1a1}.solarized.base2{color:#eee8d5}.solarized.base3{color:#fdf6e3}.solarized.solar-yellow{color:#b58900}.solarized.solar-orange{color:#cb4b16}.solarized.solar-red{color:#dc322f}.solarized.solar-magenta{color:#d33682}.solarized.solar-violet{color:#6c71c4}.solarized.solar-blue{color:#268bd2}.solarized.solar-cyan{color:#2aa198}.solarized.solar-green{color:#859900}.cm-s-solarized{line-height:1.45em;color-profile:sRGB;rendering-intent:auto}.cm-s-solarized.cm-s-dark{color:#839496;background-color:#002b36}.cm-s-solarized.cm-s-light{background-color:#fdf6e3;color:#657b83}.cm-s-solarized .CodeMirror-widget{text-shadow:none}.cm-s-solarized .cm-header{color:#586e75}.cm-s-solarized .cm-quote{color:#93a1a1}.cm-s-solarized .cm-keyword{color:#cb4b16}.cm-s-solarized .cm-atom,.cm-s-solarized .cm-number{color:#d33682}.cm-s-solarized .cm-def{color:#2aa198}.cm-s-solarized .cm-variable{color:#839496}.cm-s-solarized .cm-variable-2{color:#b58900}.cm-s-solarized .cm-type,.cm-s-solarized .cm-variable-3{color:#6c71c4}.cm-s-solarized .cm-property{color:#2aa198}.cm-s-solarized .cm-operator{color:#6c71c4}.cm-s-solarized .cm-comment{color:#586e75;font-style:italic}.cm-s-solarized .cm-string{color:#859900}.cm-s-solarized .cm-string-2{color:#b58900}.cm-s-solarized .cm-meta{color:#859900}.cm-s-solarized .cm-qualifier{color:#b58900}.cm-s-solarized .cm-builtin{color:#d33682}.cm-s-solarized .cm-bracket{color:#cb4b16}.cm-s-solarized .CodeMirror-matchingbracket{color:#859900}.cm-s-solarized .CodeMirror-nonmatchingbracket{color:#dc322f}.cm-s-solarized .cm-tag{color:#93a1a1}.cm-s-solarized .cm-attribute{color:#2aa198}.cm-s-solarized .cm-hr{color:transparent;border-top:1px solid #586e75;display:block}.cm-s-solarized .cm-link{color:#93a1a1;cursor:pointer}.cm-s-solarized .cm-special{color:#6c71c4}.cm-s-solarized .cm-em{color:#999;text-decoration:underline;text-decoration-style:dotted}.cm-s-solarized .cm-error,.cm-s-solarized .cm-invalidchar{color:#586e75;border-bottom:1px dotted #dc322f}.cm-s-solarized.cm-s-dark div.CodeMirror-selected{background:#073642}.cm-s-solarized.cm-s-dark.CodeMirror ::selection{background:rgba(7,54,66,.99)}.cm-s-dark .CodeMirror-line>span::-moz-selection,.cm-s-dark .CodeMirror-line>span>span::-moz-selection,.cm-s-solarized.cm-s-dark .CodeMirror-line::-moz-selection{background:rgba(7,54,66,.99)}.cm-s-light .CodeMirror-line>span::selection,.cm-s-light .CodeMirror-line>span>span::selection,.cm-s-solarized.cm-s-light .CodeMirror-line::selection,.cm-s-solarized.cm-s-light div.CodeMirror-selected{background:#eee8d5}.cm-s-light .CodeMirror-line>span::-moz-selection,.cm-s-light .CodeMirror-line>span>span::-moz-selection,.cm-s-solarized.cm-s-light .CodeMirror-line::-moz-selection{background:#eee8d5}.cm-s-solarized.CodeMirror{box-shadow:inset 7px 0 9pt -6px #000}.cm-s-solarized .CodeMirror-gutters{border-right:0}.cm-s-solarized.cm-s-dark .CodeMirror-gutters{background-color:#073642}.cm-s-solarized.cm-s-dark .CodeMirror-linenumber{color:#586e75}.cm-s-solarized.cm-s-light .CodeMirror-gutters{background-color:#eee8d5}.cm-s-solarized.cm-s-light .CodeMirror-linenumber{color:#839496}.cm-s-solarized .CodeMirror-linenumber{padding:0 5px}.cm-s-solarized .CodeMirror-guttermarker-subtle{color:#586e75}.cm-s-solarized.cm-s-dark .CodeMirror-guttermarker{color:#ddd}.cm-s-solarized.cm-s-light .CodeMirror-guttermarker{color:#cb4b16}.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text{color:#586e75}.cm-s-solarized .CodeMirror-cursor{border-left:1px solid #819090}.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor{background:#7e7}.cm-s-solarized.cm-s-light .cm-animate-fat-cursor{background-color:#7e7}.cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor{background:#586e75}.cm-s-solarized.cm-s-dark .cm-animate-fat-cursor{background-color:#586e75}.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background{background:hsla(0,0%,100%,.06)}.cm-s-solarized.cm-s-light .CodeMirror-activeline-background{background:rgba(0,0,0,.06)}\",\"\"])},function(e,t,n){var r=n(257);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-twilight.CodeMirror{background:#141414;color:#f7f7f7}.cm-s-twilight div.CodeMirror-selected{background:#323232}.cm-s-twilight .CodeMirror-line::selection,.cm-s-twilight .CodeMirror-line>span::selection,.cm-s-twilight .CodeMirror-line>span>span::selection{background:rgba(50,50,50,.99)}.cm-s-twilight .CodeMirror-line::-moz-selection,.cm-s-twilight .CodeMirror-line>span::-moz-selection,.cm-s-twilight .CodeMirror-line>span>span::-moz-selection{background:rgba(50,50,50,.99)}.cm-s-twilight .CodeMirror-gutters{background:#222;border-right:1px solid #aaa}.cm-s-twilight .CodeMirror-guttermarker{color:#fff}.cm-s-twilight .CodeMirror-guttermarker-subtle,.cm-s-twilight .CodeMirror-linenumber{color:#aaa}.cm-s-twilight .CodeMirror-cursor{border-left:1px solid #fff}.cm-s-twilight .cm-keyword{color:#f9ee98}.cm-s-twilight .cm-atom{color:#fc0}.cm-s-twilight .cm-number{color:#ca7841}.cm-s-twilight .cm-def{color:#8da6ce}.cm-s-twilight span.cm-def,.cm-s-twilight span.cm-tag,.cm-s-twilight span.cm-type,.cm-s-twilight span.cm-variable-2,.cm-s-twilight span.cm-variable-3{color:#607392}.cm-s-twilight .cm-operator{color:#cda869}.cm-s-twilight .cm-comment{color:#777;font-style:italic;font-weight:400}.cm-s-twilight .cm-string{color:#8f9d6a;font-style:italic}.cm-s-twilight .cm-string-2{color:#bd6b18}.cm-s-twilight .cm-meta{background-color:#141414;color:#f7f7f7}.cm-s-twilight .cm-builtin{color:#cda869}.cm-s-twilight .cm-tag{color:#997643}.cm-s-twilight .cm-attribute{color:#d6bb6d}.cm-s-twilight .cm-header{color:#ff6400}.cm-s-twilight .cm-hr{color:#aeaeae}.cm-s-twilight .cm-link{color:#ad9361;font-style:italic;text-decoration:none}.cm-s-twilight .cm-error{border-bottom:1px solid red}.cm-s-twilight .CodeMirror-activeline-background{background:#27282e}.cm-s-twilight .CodeMirror-matchingbracket{outline:1px solid grey;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(259);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".cm-s-midnight .CodeMirror-activeline-background{background:#253540}.cm-s-midnight.CodeMirror{background:#0f192a;color:#d1edff}.cm-s-midnight div.CodeMirror-selected{background:#314d67}.cm-s-midnight .CodeMirror-line::selection,.cm-s-midnight .CodeMirror-line>span::selection,.cm-s-midnight .CodeMirror-line>span>span::selection{background:rgba(49,77,103,.99)}.cm-s-midnight .CodeMirror-line::-moz-selection,.cm-s-midnight .CodeMirror-line>span::-moz-selection,.cm-s-midnight .CodeMirror-line>span>span::-moz-selection{background:rgba(49,77,103,.99)}.cm-s-midnight .CodeMirror-gutters{background:#0f192a;border-right:1px solid}.cm-s-midnight .CodeMirror-guttermarker{color:#fff}.cm-s-midnight .CodeMirror-guttermarker-subtle,.cm-s-midnight .CodeMirror-linenumber{color:#d0d0d0}.cm-s-midnight .CodeMirror-cursor{border-left:1px solid #f8f8f0}.cm-s-midnight span.cm-comment{color:#428bdd}.cm-s-midnight span.cm-atom{color:#ae81ff}.cm-s-midnight span.cm-number{color:#d1edff}.cm-s-midnight span.cm-attribute,.cm-s-midnight span.cm-property{color:#a6e22e}.cm-s-midnight span.cm-keyword{color:#e83737}.cm-s-midnight span.cm-string{color:#1dc116}.cm-s-midnight span.cm-variable,.cm-s-midnight span.cm-variable-2{color:#ffaa3e}.cm-s-midnight span.cm-def{color:#4dd}.cm-s-midnight span.cm-bracket{color:#d1edff}.cm-s-midnight span.cm-tag{color:#449}.cm-s-midnight span.cm-link{color:#ae81ff}.cm-s-midnight span.cm-error{background:#f92672;color:#f8f8f0}.cm-s-midnight .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important}\",\"\"])},function(e,t,n){var r=n(261);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".CodeMirror-dialog{position:absolute;left:0;right:0;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit}.CodeMirror-dialog-top{border-bottom:1px solid #eee;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{border:none;outline:0;background:transparent;width:20em;color:inherit;font-family:monospace}.CodeMirror-dialog button{font-size:70%}\",\"\"])},function(e,t,n){var r=n(263);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".CodeMirror-search-match{background:gold;border-top:1px solid orange;border-bottom:1px solid orange;box-sizing:border-box;opacity:.5}\",\"\"])},function(e,t,n){var r=n(265);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,'.CodeMirror-foldmarker{color:blue;text-shadow:#b9f 1px 1px 2px,#b9f -1px -1px 2px,#b9f 1px -1px 2px,#b9f -1px 1px 2px;font-family:arial;line-height:.3;cursor:pointer}.CodeMirror-foldgutter{width:.7em}.CodeMirror-foldgutter-folded,.CodeMirror-foldgutter-open{cursor:pointer}.CodeMirror-foldgutter-open:after{content:\"\\\\25BE\"}.CodeMirror-foldgutter-folded:after{content:\"\\\\25B8\"}',\"\"])},function(e,t,n){var r=n(267);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".CodeMirror-hints li.CodeMirror-hint-active:after{content:'F1';position:absolute;right:0;font-size:9pt}.CodeMirror-hints .CodeMirror-hint{max-width:unset;position:relative;padding-right:20px;font-family:consolas,monospace;font-size:14px}.CodeMirror-search-label{white-space:nowrap;width:calc(100% - 20pc)}.CodeMirror-search-label .CodeMirror-search-field{width:calc(100% - 50px)!important}.cm-s-cobalt.CodeMirror{color:var(--c-cobalt)}.cm-s-cobalt .CodeMirror-gutters,.cm-s-cobalt.CodeMirror{background:var(--b-cobalt)}\",\"\"])},function(e,t,n){!function(t,n){e.exports=n()}(this,function(){\"use strict\";function e(e){return new RegExp(\"(^|\\\\s)\"+e+\"(?:$|\\\\s)\\\\s*\")}function t(e){for(var t=e.childNodes.length;t>0;--t)e.removeChild(e.firstChild);return e}function n(e,n){return t(e).appendChild(n)}function r(e,t,n,r){var o=document.createElement(e);if(n&&(o.className=n),r&&(o.style.cssText=r),\"string\"==typeof t)o.appendChild(document.createTextNode(t));else if(t)for(var i=0;i<t.length;++i)o.appendChild(t[i]);return o}function o(e,t,n,o){var i=r(e,t,n,o);return i.setAttribute(\"role\",\"presentation\"),i}function i(e,t){if(3==t.nodeType&&(t=t.parentNode),e.contains)return e.contains(t);do if(11==t.nodeType&&(t=t.host),t==e)return!0;while(t=t.parentNode)}function a(e){var t,n=e.ownerDocument||e;try{t=e.activeElement}catch(r){t=n.body||null}for(;t&&t.shadowRoot&&t.shadowRoot.activeElement;)t=t.shadowRoot.activeElement;return t}function s(t,n){var r=t.className;e(n).test(r)||(t.className+=(r?\" \":\"\")+n)}function l(t,n){for(var r=t.split(\" \"),o=0;o<r.length;o++)r[o]&&!e(r[o]).test(n)&&(n+=\" \"+r[o]);return n}function c(e){return e.display.wrapper.ownerDocument}function u(e){return d(e.display.wrapper)}function d(e){return e.getRootNode?e.getRootNode():e.ownerDocument}function p(e){return c(e).defaultView}function h(e){var t=Array.prototype.slice.call(arguments,1);return function(){return e.apply(null,t)}}function g(e,t,n){t||(t={});for(var r in e)!e.hasOwnProperty(r)||n===!1&&t.hasOwnProperty(r)||(t[r]=e[r]);return t}function f(e,t,n,r,o){null==t&&(t=e.search(/[^\\s\\u00a0]/),-1==t&&(t=e.length));for(var i=r||0,a=o||0;;){var s=e.indexOf(\"\t\",i);if(0>s||s>=t)return a+(t-i);a+=s-i,a+=n-a%n,i=s+1}}function m(e,t){for(var n=0;n<e.length;++n)if(e[n]==t)return n;return-1}function A(e,t,n){for(var r=0,o=0;;){var i=e.indexOf(\"\t\",r);-1==i&&(i=e.length);var a=i-r;if(i==e.length||o+a>=t)return r+Math.min(a,t-o);if(o+=i-r,o+=n-o%n,r=i+1,o>=t)return r}}function M(e){for(;ns.length<=e;)ns.push(w(ns)+\" \");return ns[e]}function w(e){return e[e.length-1]}function v(e,t){for(var n=[],r=0;r<e.length;r++)n[r]=t(e[r],r);return n}function b(e,t,n){for(var r=0,o=n(t);r<e.length&&n(e[r])<=o;)r++;e.splice(r,0,t)}function y(){}function x(e,t){var n;return Object.create?n=Object.create(e):(y.prototype=e,n=new y),t&&g(t,n),n}function T(e){return/\\w/.test(e)||e>\"\"&&(e.toUpperCase()!=e.toLowerCase()||rs.test(e))}function C(e,t){return t?t.source.indexOf(\"\\\\w\")>-1&&T(e)?!0:t.test(e):T(e)}function N(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return!1;return!0}function I(e){return e.charCodeAt(0)>=768&&os.test(e)}function E(e,t,n){for(;(0>n?t>0:t<e.length)&&I(e.charAt(t));)t+=n;return t}function D(e,t,n){for(var r=t>n?-1:1;;){if(t==n)return t;var o=(t+n)/2,i=0>r?Math.ceil(o):Math.floor(o);if(i==t)return e(i)?t:n;e(i)?n=i:t=i+r}}function S(e,t,n,r){if(!e)return r(t,n,\"ltr\",0);for(var o=!1,i=0;i<e.length;++i){var a=e[i];(a.from<n&&a.to>t||t==n&&a.to==t)&&(r(Math.max(a.from,t),Math.min(a.to,n),1==a.level?\"rtl\":\"ltr\",i),o=!0)}o||r(t,n,\"ltr\")}function L(e,t,n){var r;is=null;for(var o=0;o<e.length;++o){var i=e[o];if(i.from<t&&i.to>t)return o;i.to==t&&(i.from!=i.to&&\"before\"==n?r=o:is=o),i.from==t&&(i.from!=i.to&&\"before\"!=n?r=o:is=o)}return null!=r?r:is}function k(e,t){var n=e.order;return null==n&&(n=e.order=as(e.text,t)),n}function j(e,t){return e._handlers&&e._handlers[t]||ss}function U(e,t,n){if(e.removeEventListener)e.removeEventListener(t,n,!1);else if(e.detachEvent)e.detachEvent(\"on\"+t,n);else{var r=e._handlers,o=r&&r[t];if(o){var i=m(o,n);i>-1&&(r[t]=o.slice(0,i).concat(o.slice(i+1)))}}}function B(e,t){var n=j(e,t);if(n.length)for(var r=Array.prototype.slice.call(arguments,2),o=0;o<n.length;++o)n[o].apply(null,r)}function R(e,t,n){return\"string\"==typeof t&&(t={type:t,preventDefault:function(){this.defaultPrevented=!0}}),B(e,n||t.type,e,t),V(t)||t.codemirrorIgnore}function z(e){var t=e._handlers&&e._handlers.cursorActivity;if(t)for(var n=e.curOp.cursorActivityHandlers||(e.curOp.cursorActivityHandlers=[]),r=0;r<t.length;++r)-1==m(n,t[r])&&n.push(t[r])}function Q(e,t){return j(e,t).length>0}function O(e){e.prototype.on=function(e,t){ls(this,e,t)},e.prototype.off=function(e,t){U(this,e,t)}}function H(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function F(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function V(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function P(e){H(e),F(e)}function Y(e){return e.target||e.srcElement}function G(e){var t=e.which;return null==t&&(1&e.button?t=1:2&e.button?t=3:4&e.button&&(t=2)),Oa&&e.ctrlKey&&1==t&&(t=3),t}function W(e){if(null==qa){var t=r(\"span\",\"​\");n(e,r(\"span\",[t,document.createTextNode(\"x\")])),0!=e.firstChild.offsetHeight&&(qa=t.offsetWidth<=1&&t.offsetHeight>2&&!(Na&&8>Ia))}var o=qa?r(\"span\",\"​\"):r(\"span\",\" \",null,\"display: inline-block; width: 1px; margin-right: -1px\");return o.setAttribute(\"cm-text\",\"\"),o}function X(e){if(null!=Ja)return Ja;var r=n(e,document.createTextNode(\"AخA\")),o=Pa(r,0,1).getBoundingClientRect(),i=Pa(r,1,2).getBoundingClientRect();return t(e),o&&o.left!=o.right?Ja=i.right-o.right<3:!1}function _(e){if(null!=hs)return hs;var t=n(e,r(\"span\",\"x\")),o=t.getBoundingClientRect(),i=Pa(t,0,1).getBoundingClientRect();return hs=Math.abs(o.left-i.left)>1}function q(e,t){arguments.length>2&&(t.dependencies=Array.prototype.slice.call(arguments,2)),gs[e]=t}function J(e,t){fs[e]=t}function K(e){if(\"string\"==typeof e&&fs.hasOwnProperty(e))e=fs[e];else if(e&&\"string\"==typeof e.name&&fs.hasOwnProperty(e.name)){var t=fs[e.name];\"string\"==typeof t&&(t={name:t}),e=x(t,e),e.name=t.name}else{if(\"string\"==typeof e&&/^[\\w\\-]+\\/[\\w\\-]+\\+xml$/.test(e))return K(\"application/xml\");if(\"string\"==typeof e&&/^[\\w\\-]+\\/[\\w\\-]+\\+json$/.test(e))return K(\"application/json\")}return\"string\"==typeof e?{name:e}:e||{name:\"null\"}}function Z(e,t){t=K(t);var n=gs[t.name];if(!n)return Z(e,\"text/plain\");var r=n(e,t);if(ms.hasOwnProperty(t.name)){var o=ms[t.name];for(var i in o)o.hasOwnProperty(i)&&(r.hasOwnProperty(i)&&(r[\"_\"+i]=r[i]),r[i]=o[i])}if(r.name=t.name,t.helperType&&(r.helperType=t.helperType),t.modeProps)for(var a in t.modeProps)r[a]=t.modeProps[a];return r}function $(e,t){var n=ms.hasOwnProperty(e)?ms[e]:ms[e]={};g(t,n)}function ee(e,t){if(t===!0)return t;if(e.copyState)return e.copyState(t);var n={};for(var r in t){var o=t[r];o instanceof Array&&(o=o.concat([])),n[r]=o}return n}function te(e,t){for(var n;e.innerMode&&(n=e.innerMode(t),n&&n.mode!=e);)t=n.state,e=n.mode;return n||{mode:e,state:t}}function ne(e,t,n){return e.startState?e.startState(t,n):!0}function re(e,t){if(t-=e.first,0>t||t>=e.size)throw new Error(\"There is no line \"+(t+e.first)+\" in the document.\");for(var n=e;!n.lines;)for(var r=0;;++r){var o=n.children[r],i=o.chunkSize();if(i>t){n=o;break}t-=i}return n.lines[t]}function oe(e,t,n){var r=[],o=t.line;return e.iter(t.line,n.line+1,function(e){var i=e.text;o==n.line&&(i=i.slice(0,n.ch)),o==t.line&&(i=i.slice(t.ch)),r.push(i),++o}),r}function ie(e,t,n){var r=[];return e.iter(t,n,function(e){r.push(e.text)}),r}function ae(e,t){var n=t-e.height;if(n)for(var r=e;r;r=r.parent)r.height+=n}function se(e){if(null==e.parent)return null;for(var t=e.parent,n=m(t.lines,e),r=t.parent;r;t=r,r=r.parent)for(var o=0;r.children[o]!=t;++o)n+=r.children[o].chunkSize();return n+t.first}function le(e,t){var n=e.first;e:do{for(var r=0;r<e.children.length;++r){var o=e.children[r],i=o.height;if(i>t){e=o;continue e}t-=i,n+=o.chunkSize()}return n}while(!e.lines);for(var a=0;a<e.lines.length;++a){var s=e.lines[a],l=s.height;if(l>t)break;t-=l}return n+a}function ce(e,t){return t>=e.first&&t<e.first+e.size}function ue(e,t){return String(e.lineNumberFormatter(t+e.firstLineNumber))}function de(e,t,n){return void 0===n&&(n=null),this instanceof de?(this.line=e,this.ch=t,void(this.sticky=n)):new de(e,t,n)}function pe(e,t){return e.line-t.line||e.ch-t.ch}function he(e,t){return e.sticky==t.sticky&&0==pe(e,t)}function ge(e){return de(e.line,e.ch)}function fe(e,t){return pe(e,t)<0?t:e}function me(e,t){return pe(e,t)<0?e:t}function Ae(e,t){return Math.max(e.first,Math.min(t,e.first+e.size-1))}function Me(e,t){if(t.line<e.first)return de(e.first,0);var n=e.first+e.size-1;return t.line>n?de(n,re(e,n).text.length):we(t,re(e,t.line).text.length)}function we(e,t){var n=e.ch;return null==n||n>t?de(e.line,t):0>n?de(e.line,0):e}function ve(e,t){for(var n=[],r=0;r<t.length;r++)n[r]=Me(e,t[r]);return n}function be(e,t,n,r){var o=[e.state.modeGen],i={};De(e,t.text,e.doc.mode,n,function(e,t){return o.push(e,t)},i,r);for(var a=n.state,s=function(r){n.baseTokens=o;var s=e.state.overlays[r],l=1,c=0;n.state=!0,De(e,t.text,s.mode,n,function(e,t){for(var n=l;e>c;){var r=o[l];r>e&&o.splice(l,1,e,o[l+1],r),l+=2,c=Math.min(e,r)}if(t)if(s.opaque)o.splice(n,l-n,e,\"overlay \"+t),l=n+2;else for(;l>n;n+=2){var i=o[n+1];o[n+1]=(i?i+\" \":\"\")+\"overlay \"+t}},i),n.state=a,n.baseTokens=null,n.baseTokenPos=1},l=0;l<e.state.overlays.length;++l)s(l);return{styles:o,classes:i.bgClass||i.textClass?i:null}}function ye(e,t,n){if(!t.styles||t.styles[0]!=e.state.modeGen){var r=xe(e,se(t)),o=t.text.length>e.options.maxHighlightLength&&ee(e.doc.mode,r.state),i=be(e,t,r);o&&(r.state=o),t.stateAfter=r.save(!o),t.styles=i.styles,i.classes?t.styleClasses=i.classes:t.styleClasses&&(t.styleClasses=null),n===e.doc.highlightFrontier&&(e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier))}return t.styles}function xe(e,t,n){var r=e.doc,o=e.display;if(!r.mode.startState)return new ws(r,!0,t);var i=Se(e,t,n),a=i>r.first&&re(r,i-1).stateAfter,s=a?ws.fromSaved(r,a,i):new ws(r,ne(r.mode),i);return r.iter(i,t,function(n){Te(e,n.text,s);var r=s.line;n.stateAfter=r==t-1||r%5==0||r>=o.viewFrom&&r<o.viewTo?s.save():null,s.nextLine()}),n&&(r.modeFrontier=s.line),s}function Te(e,t,n,r){var o=e.doc.mode,i=new As(t,e.options.tabSize,n);for(i.start=i.pos=r||0,\"\"==t&&Ce(o,n.state);!i.eol();)Ne(o,i,n.state),i.start=i.pos}function Ce(e,t){if(e.blankLine)return e.blankLine(t);if(e.innerMode){var n=te(e,t);return n.mode.blankLine?n.mode.blankLine(n.state):void 0}}function Ne(e,t,n,r){for(var o=0;10>o;o++){r&&(r[0]=te(e,n).mode);var i=e.token(t,n);if(t.pos>t.start)return i}throw new Error(\"Mode \"+e.name+\" failed to advance stream.\")}function Ie(e,t,n,r){var o,i=e.doc,a=i.mode;t=Me(i,t);var s,l=re(i,t.line),c=xe(e,t.line,n),u=new As(l.text,e.options.tabSize,c);for(r&&(s=[]);(r||u.pos<t.ch)&&!u.eol();)u.start=u.pos,o=Ne(a,u,c.state),r&&s.push(new vs(u,o,ee(i.mode,c.state)));return r?s:new vs(u,o,c.state)}function Ee(e,t){if(e)for(;;){var n=e.match(/(?:^|\\s+)line-(background-)?(\\S+)/);if(!n)break;e=e.slice(0,n.index)+e.slice(n.index+n[0].length);var r=n[1]?\"bgClass\":\"textClass\";null==t[r]?t[r]=n[2]:new RegExp(\"(?:^|\\\\s)\"+n[2]+\"(?:$|\\\\s)\").test(t[r])||(t[r]+=\" \"+n[2])}return e}function De(e,t,n,r,o,i,a){var s=n.flattenSpans;null==s&&(s=e.options.flattenSpans);var l,c=0,u=null,d=new As(t,e.options.tabSize,r),p=e.options.addModeClass&&[null];for(\"\"==t&&Ee(Ce(n,r.state),i);!d.eol();){if(d.pos>e.options.maxHighlightLength?(s=!1,a&&Te(e,t,r,d.pos),d.pos=t.length,l=null):l=Ee(Ne(n,d,r.state,p),i),p){var h=p[0].name;h&&(l=\"m-\"+(l?h+\" \"+l:h))}if(!s||u!=l){for(;c<d.start;)c=Math.min(d.start,c+5e3),o(c,u);u=l}d.start=d.pos}for(;c<d.pos;){var g=Math.min(d.pos,c+5e3);o(g,u),c=g}}function Se(e,t,n){for(var r,o,i=e.doc,a=n?-1:t-(e.doc.mode.innerMode?1e3:100),s=t;s>a;--s){if(s<=i.first)return i.first;var l=re(i,s-1),c=l.stateAfter;if(c&&(!n||s+(c instanceof Ms?c.lookAhead:0)<=i.modeFrontier))return s;var u=f(l.text,null,e.options.tabSize);(null==o||r>u)&&(o=s-1,r=u)}return o}function Le(e,t){if(e.modeFrontier=Math.min(e.modeFrontier,t),!(e.highlightFrontier<t-10)){for(var n=e.first,r=t-1;r>n;r--){var o=re(e,r).stateAfter;if(o&&(!(o instanceof Ms)||r+o.lookAhead<t)){n=r+1;break}}e.highlightFrontier=Math.min(e.highlightFrontier,n)}}function ke(){bs=!0}function je(){ys=!0}function Ue(e,t,n){this.marker=e,this.from=t,this.to=n}function Be(e,t){if(e)for(var n=0;n<e.length;++n){var r=e[n];if(r.marker==t)return r}}function Re(e,t){for(var n,r=0;r<e.length;++r)e[r]!=t&&(n||(n=[])).push(e[r]);return n}function ze(e,t,n){var r=n&&window.WeakSet&&(n.markedSpans||(n.markedSpans=new WeakSet));r&&e.markedSpans&&r.has(e.markedSpans)?e.markedSpans.push(t):(e.markedSpans=e.markedSpans?e.markedSpans.concat([t]):[t],r&&r.add(e.markedSpans)),t.marker.attachLine(e)}function Qe(e,t,n){var r;if(e)for(var o=0;o<e.length;++o){var i=e[o],a=i.marker,s=null==i.from||(a.inclusiveLeft?i.from<=t:i.from<t);if(s||i.from==t&&\"bookmark\"==a.type&&(!n||!i.marker.insertLeft)){var l=null==i.to||(a.inclusiveRight?i.to>=t:i.to>t);(r||(r=[])).push(new Ue(a,i.from,l?null:i.to))}}return r}function Oe(e,t,n){var r;if(e)for(var o=0;o<e.length;++o){var i=e[o],a=i.marker,s=null==i.to||(a.inclusiveRight?i.to>=t:i.to>t);if(s||i.from==t&&\"bookmark\"==a.type&&(!n||i.marker.insertLeft)){var l=null==i.from||(a.inclusiveLeft?i.from<=t:i.from<t);(r||(r=[])).push(new Ue(a,l?null:i.from-t,null==i.to?null:i.to-t))}}return r}function He(e,t){if(t.full)return null;var n=ce(e,t.from.line)&&re(e,t.from.line).markedSpans,r=ce(e,t.to.line)&&re(e,t.to.line).markedSpans;if(!n&&!r)return null;var o=t.from.ch,i=t.to.ch,a=0==pe(t.from,t.to),s=Qe(n,o,a),l=Oe(r,i,a),c=1==t.text.length,u=w(t.text).length+(c?o:0);if(s)for(var d=0;d<s.length;++d){var p=s[d];if(null==p.to){var h=Be(l,p.marker);h?c&&(p.to=null==h.to?null:h.to+u):p.to=o}}if(l)for(var g=0;g<l.length;++g){var f=l[g];if(null!=f.to&&(f.to+=u),null==f.from){var m=Be(s,f.marker);m||(f.from=u,c&&(s||(s=[])).push(f))}else f.from+=u,c&&(s||(s=[])).push(f)}s&&(s=Fe(s)),l&&l!=s&&(l=Fe(l));var A=[s];if(!c){var M,v=t.text.length-2;if(v>0&&s)for(var b=0;b<s.length;++b)null==s[b].to&&(M||(M=[])).push(new Ue(s[b].marker,null,null));for(var y=0;v>y;++y)A.push(M);A.push(l)}return A}function Fe(e){for(var t=0;t<e.length;++t){var n=e[t];null!=n.from&&n.from==n.to&&n.marker.clearWhenEmpty!==!1&&e.splice(t--,1)}return e.length?e:null}function Ve(e,t,n){var r=null;if(e.iter(t.line,n.line+1,function(e){if(e.markedSpans)for(var t=0;t<e.markedSpans.length;++t){var n=e.markedSpans[t].marker;!n.readOnly||r&&-1!=m(r,n)||(r||(r=[])).push(n)}}),!r)return null;for(var o=[{from:t,to:n}],i=0;i<r.length;++i)for(var a=r[i],s=a.find(0),l=0;l<o.length;++l){var c=o[l];if(!(pe(c.to,s.from)<0||pe(c.from,s.to)>0)){var u=[l,1],d=pe(c.from,s.from),p=pe(c.to,s.to);(0>d||!a.inclusiveLeft&&!d)&&u.push({from:c.from,to:s.from}),(p>0||!a.inclusiveRight&&!p)&&u.push({from:s.to,to:c.to}),o.splice.apply(o,u),l+=u.length-3}}return o}function Pe(e){var t=e.markedSpans;if(t){for(var n=0;n<t.length;++n)t[n].marker.detachLine(e);e.markedSpans=null}}function Ye(e,t){if(t){for(var n=0;n<t.length;++n)t[n].marker.attachLine(e);e.markedSpans=t}}function Ge(e){return e.inclusiveLeft?-1:0}function We(e){return e.inclusiveRight?1:0}function Xe(e,t){var n=e.lines.length-t.lines.length;if(0!=n)return n;var r=e.find(),o=t.find(),i=pe(r.from,o.from)||Ge(e)-Ge(t);if(i)return-i;var a=pe(r.to,o.to)||We(e)-We(t);return a?a:t.id-e.id}function _e(e,t){var n,r=ys&&e.markedSpans;if(r)for(var o=void 0,i=0;i<r.length;++i)o=r[i],o.marker.collapsed&&null==(t?o.from:o.to)&&(!n||Xe(n,o.marker)<0)&&(n=o.marker);return n}function qe(e){return _e(e,!0)}function Je(e){return _e(e,!1)}function Ke(e,t){var n,r=ys&&e.markedSpans;if(r)for(var o=0;o<r.length;++o){var i=r[o];i.marker.collapsed&&(null==i.from||i.from<t)&&(null==i.to||i.to>t)&&(!n||Xe(n,i.marker)<0)&&(n=i.marker)}return n}function Ze(e,t,n,r,o){var i=re(e,t),a=ys&&i.markedSpans;if(a)for(var s=0;s<a.length;++s){var l=a[s];if(l.marker.collapsed){var c=l.marker.find(0),u=pe(c.from,n)||Ge(l.marker)-Ge(o),d=pe(c.to,r)||We(l.marker)-We(o);if(!(u>=0&&0>=d||0>=u&&d>=0)&&(0>=u&&(l.marker.inclusiveRight&&o.inclusiveLeft?pe(c.to,n)>=0:pe(c.to,n)>0)||u>=0&&(l.marker.inclusiveRight&&o.inclusiveLeft?pe(c.from,r)<=0:pe(c.from,r)<0)))return!0}}}function $e(e){for(var t;t=qe(e);)e=t.find(-1,!0).line;return e}function et(e){for(var t;t=Je(e);)e=t.find(1,!0).line;return e}function tt(e){for(var t,n;t=Je(e);)e=t.find(1,!0).line,(n||(n=[])).push(e);return n}function nt(e,t){var n=re(e,t),r=$e(n);return n==r?t:se(r)}function rt(e,t){if(t>e.lastLine())return t;var n,r=re(e,t);if(!ot(e,r))return t;for(;n=Je(r);)r=n.find(1,!0).line;return se(r)+1}function ot(e,t){var n=ys&&t.markedSpans;if(n)for(var r=void 0,o=0;o<n.length;++o)if(r=n[o],r.marker.collapsed){if(null==r.from)return!0;if(!r.marker.widgetNode&&0==r.from&&r.marker.inclusiveLeft&&it(e,t,r))return!0}}function it(e,t,n){if(null==n.to){var r=n.marker.find(1,!0);return it(e,r.line,Be(r.line.markedSpans,n.marker))}if(n.marker.inclusiveRight&&n.to==t.text.length)return!0;for(var o=void 0,i=0;i<t.markedSpans.length;++i)if(o=t.markedSpans[i],o.marker.collapsed&&!o.marker.widgetNode&&o.from==n.to&&(null==o.to||o.to!=n.from)&&(o.marker.inclusiveLeft||n.marker.inclusiveRight)&&it(e,t,o))return!0}function at(e){e=$e(e);for(var t=0,n=e.parent,r=0;r<n.lines.length;++r){var o=n.lines[r];if(o==e)break;t+=o.height}for(var i=n.parent;i;n=i,i=n.parent)for(var a=0;a<i.children.length;++a){var s=i.children[a];if(s==n)break;t+=s.height}return t}function st(e){if(0==e.height)return 0;for(var t,n=e.text.length,r=e;t=qe(r);){var o=t.find(0,!0);r=o.from.line,n+=o.from.ch-o.to.ch}for(r=e;t=Je(r);){var i=t.find(0,!0);n-=r.text.length-i.from.ch,r=i.to.line,n+=r.text.length-i.to.ch}return n}function lt(e){var t=e.display,n=e.doc;t.maxLine=re(n,n.first),t.maxLineLength=st(t.maxLine),t.maxLineChanged=!0,n.iter(function(e){var n=st(e);n>t.maxLineLength&&(t.maxLineLength=n,t.maxLine=e)})}function ct(e,t,n,r){e.text=t,e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null),null!=e.order&&(e.order=null),Pe(e),Ye(e,n);var o=r?r(e):1;o!=e.height&&ae(e,o)}function ut(e){e.parent=null,Pe(e)}function dt(e,t){if(!e||/^\\s*$/.test(e))return null;var n=t.addModeClass?Ns:Cs;return n[e]||(n[e]=e.replace(/\\S+/g,\"cm-$&\"))}function pt(e,t){var n=o(\"span\",null,null,Ea?\"padding-right: .1px\":null),r={pre:o(\"pre\",[n],\"CodeMirror-line\"),content:n,col:0,pos:0,cm:e,trailingSpace:!1,splitSpaces:e.getOption(\"lineWrapping\")\n};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var a=i?t.rest[i-1]:t.line,s=void 0;r.pos=0,r.addToken=gt,X(e.display.measure)&&(s=k(a,e.doc.direction))&&(r.addToken=mt(r.addToken,s)),r.map=[];var c=t!=e.display.externalMeasured&&se(a);Mt(a,r,ye(e,a,c)),a.styleClasses&&(a.styleClasses.bgClass&&(r.bgClass=l(a.styleClasses.bgClass,r.bgClass||\"\")),a.styleClasses.textClass&&(r.textClass=l(a.styleClasses.textClass,r.textClass||\"\"))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(W(e.display.measure))),0==i?(t.measure.map=r.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(r.map),(t.measure.caches||(t.measure.caches=[])).push({}))}if(Ea){var u=r.content.lastChild;(/\\bcm-tab\\b/.test(u.className)||u.querySelector&&u.querySelector(\".cm-tab\"))&&(r.content.className=\"cm-tab-wrap-hack\")}return B(e,\"renderLine\",e,t.line,r.pre),r.pre.className&&(r.textClass=l(r.pre.className,r.textClass||\"\")),r}function ht(e){var t=r(\"span\",\"•\",\"cm-invalidchar\");return t.title=\"\\\\u\"+e.charCodeAt(0).toString(16),t.setAttribute(\"aria-label\",t.title),t}function gt(e,t,n,o,i,a,s){if(t){var l,c=e.splitSpaces?ft(t,e.trailingSpace):t,u=e.cm.state.specialChars,d=!1;if(u.test(t)){l=document.createDocumentFragment();for(var p=0;;){u.lastIndex=p;var h=u.exec(t),g=h?h.index-p:t.length-p;if(g){var f=document.createTextNode(c.slice(p,p+g));Na&&9>Ia?l.appendChild(r(\"span\",[f])):l.appendChild(f),e.map.push(e.pos,e.pos+g,f),e.col+=g,e.pos+=g}if(!h)break;p+=g+1;var m=void 0;if(\"\t\"==h[0]){var A=e.cm.options.tabSize,w=A-e.col%A;m=l.appendChild(r(\"span\",M(w),\"cm-tab\")),m.setAttribute(\"role\",\"presentation\"),m.setAttribute(\"cm-text\",\"\t\"),e.col+=w}else\"\\r\"==h[0]||\"\\n\"==h[0]?(m=l.appendChild(r(\"span\",\"\\r\"==h[0]?\"␍\":\"␤\",\"cm-invalidchar\")),m.setAttribute(\"cm-text\",h[0]),e.col+=1):(m=e.cm.options.specialCharPlaceholder(h[0]),m.setAttribute(\"cm-text\",h[0]),Na&&9>Ia?l.appendChild(r(\"span\",[m])):l.appendChild(m),e.col+=1);e.map.push(e.pos,e.pos+1,m),e.pos++}}else e.col+=t.length,l=document.createTextNode(c),e.map.push(e.pos,e.pos+t.length,l),Na&&9>Ia&&(d=!0),e.pos+=t.length;if(e.trailingSpace=32==c.charCodeAt(t.length-1),n||o||i||d||a||s){var v=n||\"\";o&&(v+=o),i&&(v+=i);var b=r(\"span\",[l],v,a);if(s)for(var y in s)s.hasOwnProperty(y)&&\"style\"!=y&&\"class\"!=y&&b.setAttribute(y,s[y]);return e.content.appendChild(b)}e.content.appendChild(l)}}function ft(e,t){if(e.length>1&&!/  /.test(e))return e;for(var n=t,r=\"\",o=0;o<e.length;o++){var i=e.charAt(o);\" \"!=i||!n||o!=e.length-1&&32!=e.charCodeAt(o+1)||(i=\" \"),r+=i,n=\" \"==i}return r}function mt(e,t){return function(n,r,o,i,a,s,l){o=o?o+\" cm-force-border\":\"cm-force-border\";for(var c=n.pos,u=c+r.length;;){for(var d=void 0,p=0;p<t.length&&(d=t[p],!(d.to>c&&d.from<=c));p++);if(d.to>=u)return e(n,r,o,i,a,s,l);e(n,r.slice(0,d.to-c),o,i,null,s,l),i=null,r=r.slice(d.to-c),c=d.to}}}function At(e,t,n,r){var o=!r&&n.widgetNode;o&&e.map.push(e.pos,e.pos+t,o),!r&&e.cm.display.input.needsContentAttribute&&(o||(o=e.content.appendChild(document.createElement(\"span\"))),o.setAttribute(\"cm-marker\",n.id)),o&&(e.cm.display.input.setUneditable(o),e.content.appendChild(o)),e.pos+=t,e.trailingSpace=!1}function Mt(e,t,n){var r=e.markedSpans,o=e.text,i=0;if(r)for(var a,s,l,c,u,d,p,h=o.length,g=0,f=1,m=\"\",A=0;;){if(A==g){l=c=u=s=\"\",p=null,d=null,A=1/0;for(var M=[],w=void 0,v=0;v<r.length;++v){var b=r[v],y=b.marker;if(\"bookmark\"==y.type&&b.from==g&&y.widgetNode)M.push(y);else if(b.from<=g&&(null==b.to||b.to>g||y.collapsed&&b.to==g&&b.from==g)){if(null!=b.to&&b.to!=g&&A>b.to&&(A=b.to,c=\"\"),y.className&&(l+=\" \"+y.className),y.css&&(s=(s?s+\";\":\"\")+y.css),y.startStyle&&b.from==g&&(u+=\" \"+y.startStyle),y.endStyle&&b.to==A&&(w||(w=[])).push(y.endStyle,b.to),y.title&&((p||(p={})).title=y.title),y.attributes)for(var x in y.attributes)(p||(p={}))[x]=y.attributes[x];y.collapsed&&(!d||Xe(d.marker,y)<0)&&(d=b)}else b.from>g&&A>b.from&&(A=b.from)}if(w)for(var T=0;T<w.length;T+=2)w[T+1]==A&&(c+=\" \"+w[T]);if(!d||d.from==g)for(var C=0;C<M.length;++C)At(t,0,M[C]);if(d&&(d.from||0)==g){if(At(t,(null==d.to?h+1:d.to)-g,d.marker,null==d.from),null==d.to)return;d.to==g&&(d=!1)}}if(g>=h)break;for(var N=Math.min(h,A);;){if(m){var I=g+m.length;if(!d){var E=I>N?m.slice(0,N-g):m;t.addToken(t,E,a?a+l:l,u,g+E.length==A?c:\"\",s,p)}if(I>=N){m=m.slice(N-g),g=N;break}g=I,u=\"\"}m=o.slice(i,i=n[f++]),a=dt(n[f++],t.cm.options)}}else for(var D=1;D<n.length;D+=2)t.addToken(t,o.slice(i,i=n[D]),dt(n[D+1],t.cm.options))}function wt(e,t,n){this.line=t,this.rest=tt(t),this.size=this.rest?se(w(this.rest))-n+1:1,this.node=this.text=null,this.hidden=ot(e,t)}function vt(e,t,n){for(var r,o=[],i=t;n>i;i=r){var a=new wt(e.doc,re(e.doc,i),i);r=i+a.size,o.push(a)}return o}function bt(e){Is?Is.ops.push(e):e.ownsGroup=Is={ops:[e],delayedCallbacks:[]}}function yt(e){var t=e.delayedCallbacks,n=0;do{for(;n<t.length;n++)t[n].call(null);for(var r=0;r<e.ops.length;r++){var o=e.ops[r];if(o.cursorActivityHandlers)for(;o.cursorActivityCalled<o.cursorActivityHandlers.length;)o.cursorActivityHandlers[o.cursorActivityCalled++].call(null,o.cm)}}while(n<t.length)}function xt(e,t){var n=e.ownsGroup;if(n)try{yt(n)}finally{Is=null,t(n)}}function Tt(e,t){var n=j(e,t);if(n.length){var r,o=Array.prototype.slice.call(arguments,2);Is?r=Is.delayedCallbacks:Es?r=Es:(r=Es=[],setTimeout(Ct,0));for(var i=function(e){r.push(function(){return n[e].apply(null,o)})},a=0;a<n.length;++a)i(a)}}function Ct(){var e=Es;Es=null;for(var t=0;t<e.length;++t)e[t]()}function Nt(e,t,n,r){for(var o=0;o<t.changes.length;o++){var i=t.changes[o];\"text\"==i?St(e,t):\"gutter\"==i?kt(e,t,n,r):\"class\"==i?Lt(e,t):\"widget\"==i&&jt(e,t,r)}t.changes=null}function It(e){return e.node==e.text&&(e.node=r(\"div\",null,null,\"position: relative\"),e.text.parentNode&&e.text.parentNode.replaceChild(e.node,e.text),e.node.appendChild(e.text),Na&&8>Ia&&(e.node.style.zIndex=2)),e.node}function Et(e,t){var n=t.bgClass?t.bgClass+\" \"+(t.line.bgClass||\"\"):t.line.bgClass;if(n&&(n+=\" CodeMirror-linebackground\"),t.background)n?t.background.className=n:(t.background.parentNode.removeChild(t.background),t.background=null);else if(n){var o=It(t);t.background=o.insertBefore(r(\"div\",null,n),o.firstChild),e.display.input.setUneditable(t.background)}}function Dt(e,t){var n=e.display.externalMeasured;return n&&n.line==t.line?(e.display.externalMeasured=null,t.measure=n.measure,n.built):pt(e,t)}function St(e,t){var n=t.text.className,r=Dt(e,t);t.text==t.node&&(t.node=r.pre),t.text.parentNode.replaceChild(r.pre,t.text),t.text=r.pre,r.bgClass!=t.bgClass||r.textClass!=t.textClass?(t.bgClass=r.bgClass,t.textClass=r.textClass,Lt(e,t)):n&&(t.text.className=n)}function Lt(e,t){Et(e,t),t.line.wrapClass?It(t).className=t.line.wrapClass:t.node!=t.text&&(t.node.className=\"\");var n=t.textClass?t.textClass+\" \"+(t.line.textClass||\"\"):t.line.textClass;t.text.className=n||\"\"}function kt(e,t,n,o){if(t.gutter&&(t.node.removeChild(t.gutter),t.gutter=null),t.gutterBackground&&(t.node.removeChild(t.gutterBackground),t.gutterBackground=null),t.line.gutterClass){var i=It(t);t.gutterBackground=r(\"div\",null,\"CodeMirror-gutter-background \"+t.line.gutterClass,\"left: \"+(e.options.fixedGutter?o.fixedPos:-o.gutterTotalWidth)+\"px; width: \"+o.gutterTotalWidth+\"px\"),e.display.input.setUneditable(t.gutterBackground),i.insertBefore(t.gutterBackground,t.text)}var a=t.line.gutterMarkers;if(e.options.lineNumbers||a){var s=It(t),l=t.gutter=r(\"div\",null,\"CodeMirror-gutter-wrapper\",\"left: \"+(e.options.fixedGutter?o.fixedPos:-o.gutterTotalWidth)+\"px\");if(l.setAttribute(\"aria-hidden\",\"true\"),e.display.input.setUneditable(l),s.insertBefore(l,t.text),t.line.gutterClass&&(l.className+=\" \"+t.line.gutterClass),!e.options.lineNumbers||a&&a[\"CodeMirror-linenumbers\"]||(t.lineNumber=l.appendChild(r(\"div\",ue(e.options,n),\"CodeMirror-linenumber CodeMirror-gutter-elt\",\"left: \"+o.gutterLeft[\"CodeMirror-linenumbers\"]+\"px; width: \"+e.display.lineNumInnerWidth+\"px\"))),a)for(var c=0;c<e.display.gutterSpecs.length;++c){var u=e.display.gutterSpecs[c].className,d=a.hasOwnProperty(u)&&a[u];d&&l.appendChild(r(\"div\",[d],\"CodeMirror-gutter-elt\",\"left: \"+o.gutterLeft[u]+\"px; width: \"+o.gutterWidth[u]+\"px\"))}}}function jt(t,n,r){n.alignable&&(n.alignable=null);for(var o=e(\"CodeMirror-linewidget\"),i=n.node.firstChild,a=void 0;i;i=a)a=i.nextSibling,o.test(i.className)&&n.node.removeChild(i);Bt(t,n,r)}function Ut(e,t,n,r){var o=Dt(e,t);return t.text=t.node=o.pre,o.bgClass&&(t.bgClass=o.bgClass),o.textClass&&(t.textClass=o.textClass),Lt(e,t),kt(e,t,n,r),Bt(e,t,r),t.node}function Bt(e,t,n){if(Rt(e,t.line,t,n,!0),t.rest)for(var r=0;r<t.rest.length;r++)Rt(e,t.rest[r],t,n,!1)}function Rt(e,t,n,o,i){if(t.widgets)for(var a=It(n),s=0,l=t.widgets;s<l.length;++s){var c=l[s],u=r(\"div\",[c.node],\"CodeMirror-linewidget\"+(c.className?\" \"+c.className:\"\"));c.handleMouseEvents||u.setAttribute(\"cm-ignore-events\",\"true\"),zt(c,u,n,o),e.display.input.setUneditable(u),i&&c.above?a.insertBefore(u,n.gutter||n.text):a.appendChild(u),Tt(c,\"redraw\")}}function zt(e,t,n,r){if(e.noHScroll){(n.alignable||(n.alignable=[])).push(t);var o=r.wrapperWidth;t.style.left=r.fixedPos+\"px\",e.coverGutter||(o-=r.gutterTotalWidth,t.style.paddingLeft=r.gutterTotalWidth+\"px\"),t.style.width=o+\"px\"}e.coverGutter&&(t.style.zIndex=5,t.style.position=\"relative\",e.noHScroll||(t.style.marginLeft=-r.gutterTotalWidth+\"px\"))}function Qt(e){if(null!=e.height)return e.height;var t=e.doc.cm;if(!t)return 0;if(!i(document.body,e.node)){var o=\"position: relative;\";e.coverGutter&&(o+=\"margin-left: -\"+t.display.gutters.offsetWidth+\"px;\"),e.noHScroll&&(o+=\"width: \"+t.display.wrapper.clientWidth+\"px;\"),n(t.display.measure,r(\"div\",[e.node],null,o))}return e.height=e.node.parentNode.offsetHeight}function Ot(e,t){for(var n=Y(t);n!=e.wrapper;n=n.parentNode)if(!n||1==n.nodeType&&\"true\"==n.getAttribute(\"cm-ignore-events\")||n.parentNode==e.sizer&&n!=e.mover)return!0}function Ht(e){return e.lineSpace.offsetTop}function Ft(e){return e.mover.offsetHeight-e.lineSpace.offsetHeight}function Vt(e){if(e.cachedPaddingH)return e.cachedPaddingH;var t=n(e.measure,r(\"pre\",\"x\",\"CodeMirror-line-like\")),o=window.getComputedStyle?window.getComputedStyle(t):t.currentStyle,i={left:parseInt(o.paddingLeft),right:parseInt(o.paddingRight)};return isNaN(i.left)||isNaN(i.right)||(e.cachedPaddingH=i),i}function Pt(e){return Ka-e.display.nativeBarWidth}function Yt(e){return e.display.scroller.clientWidth-Pt(e)-e.display.barWidth}function Gt(e){return e.display.scroller.clientHeight-Pt(e)-e.display.barHeight}function Wt(e,t,n){var r=e.options.lineWrapping,o=r&&Yt(e);if(!t.measure.heights||r&&t.measure.width!=o){var i=t.measure.heights=[];if(r){t.measure.width=o;for(var a=t.text.firstChild.getClientRects(),s=0;s<a.length-1;s++){var l=a[s],c=a[s+1];Math.abs(l.bottom-c.bottom)>2&&i.push((l.bottom+c.top)/2-n.top)}}i.push(n.bottom-n.top)}}function Xt(e,t,n){if(e.line==t)return{map:e.measure.map,cache:e.measure.cache};if(e.rest){for(var r=0;r<e.rest.length;r++)if(e.rest[r]==t)return{map:e.measure.maps[r],cache:e.measure.caches[r]};for(var o=0;o<e.rest.length;o++)if(se(e.rest[o])>n)return{map:e.measure.maps[o],cache:e.measure.caches[o],before:!0}}}function _t(e,t){t=$e(t);var r=se(t),o=e.display.externalMeasured=new wt(e.doc,t,r);o.lineN=r;var i=o.built=pt(e,o);return o.text=i.pre,n(e.display.lineMeasure,i.pre),o}function qt(e,t,n,r){return Zt(e,Kt(e,t),n,r)}function Jt(e,t){if(t>=e.display.viewFrom&&t<e.display.viewTo)return e.display.view[Sn(e,t)];var n=e.display.externalMeasured;return n&&t>=n.lineN&&t<n.lineN+n.size?n:void 0}function Kt(e,t){var n=se(t),r=Jt(e,n);r&&!r.text?r=null:r&&r.changes&&(Nt(e,r,n,Cn(e)),e.curOp.forceUpdate=!0),r||(r=_t(e,t));var o=Xt(r,t,n);return{line:t,view:r,rect:null,map:o.map,cache:o.cache,before:o.before,hasHeights:!1}}function Zt(e,t,n,r,o){t.before&&(n=-1);var i,a=n+(r||\"\");return t.cache.hasOwnProperty(a)?i=t.cache[a]:(t.rect||(t.rect=t.view.text.getBoundingClientRect()),t.hasHeights||(Wt(e,t.view,t.rect),t.hasHeights=!0),i=tn(e,t,n,r),i.bogus||(t.cache[a]=i)),{left:i.left,right:i.right,top:o?i.rtop:i.top,bottom:o?i.rbottom:i.bottom}}function $t(e,t,n){for(var r,o,i,a,s,l,c=0;c<e.length;c+=3)if(s=e[c],l=e[c+1],s>t?(o=0,i=1,a=\"left\"):l>t?(o=t-s,i=o+1):(c==e.length-3||t==l&&e[c+3]>t)&&(i=l-s,o=i-1,t>=l&&(a=\"right\")),null!=o){if(r=e[c+2],s==l&&n==(r.insertLeft?\"left\":\"right\")&&(a=n),\"left\"==n&&0==o)for(;c&&e[c-2]==e[c-3]&&e[c-1].insertLeft;)r=e[(c-=3)+2],a=\"left\";if(\"right\"==n&&o==l-s)for(;c<e.length-3&&e[c+3]==e[c+4]&&!e[c+5].insertLeft;)r=e[(c+=3)+2],a=\"right\";break}return{node:r,start:o,end:i,collapse:a,coverStart:s,coverEnd:l}}function en(e,t){var n=Ds;if(\"left\"==t)for(var r=0;r<e.length&&(n=e[r]).left==n.right;r++);else for(var o=e.length-1;o>=0&&(n=e[o]).left==n.right;o--);return n}function tn(e,t,n,r){var o,i=$t(t.map,n,r),a=i.node,s=i.start,l=i.end,c=i.collapse;if(3==a.nodeType){for(var u=0;4>u;u++){for(;s&&I(t.line.text.charAt(i.coverStart+s));)--s;for(;i.coverStart+l<i.coverEnd&&I(t.line.text.charAt(i.coverStart+l));)++l;if(o=Na&&9>Ia&&0==s&&l==i.coverEnd-i.coverStart?a.parentNode.getBoundingClientRect():en(Pa(a,s,l).getClientRects(),r),o.left||o.right||0==s)break;l=s,s-=1,c=\"right\"}Na&&11>Ia&&(o=nn(e.display.measure,o))}else{s>0&&(c=r=\"right\");var d;o=e.options.lineWrapping&&(d=a.getClientRects()).length>1?d[\"right\"==r?d.length-1:0]:a.getBoundingClientRect()}if(Na&&9>Ia&&!s&&(!o||!o.left&&!o.right)){var p=a.parentNode.getClientRects()[0];o=p?{left:p.left,right:p.left+Tn(e.display),top:p.top,bottom:p.bottom}:Ds}for(var h=o.top-t.rect.top,g=o.bottom-t.rect.top,f=(h+g)/2,m=t.view.measure.heights,A=0;A<m.length-1&&!(f<m[A]);A++);var M=A?m[A-1]:0,w=m[A],v={left:(\"right\"==c?o.right:o.left)-t.rect.left,right:(\"left\"==c?o.left:o.right)-t.rect.left,top:M,bottom:w};return o.left||o.right||(v.bogus=!0),e.options.singleCursorHeightPerLine||(v.rtop=h,v.rbottom=g),v}function nn(e,t){if(!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI||!_(e))return t;var n=screen.logicalXDPI/screen.deviceXDPI,r=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*n,right:t.right*n,top:t.top*r,bottom:t.bottom*r}}function rn(e){if(e.measure&&(e.measure.cache={},e.measure.heights=null,e.rest))for(var t=0;t<e.rest.length;t++)e.measure.caches[t]={}}function on(e){e.display.externalMeasure=null,t(e.display.lineMeasure);for(var n=0;n<e.display.view.length;n++)rn(e.display.view[n])}function an(e){on(e),e.display.cachedCharWidth=e.display.cachedTextHeight=e.display.cachedPaddingH=null,e.options.lineWrapping||(e.display.maxLineChanged=!0),e.display.lineNumChars=null}function sn(e){return Sa&&za?-(e.body.getBoundingClientRect().left-parseInt(getComputedStyle(e.body).marginLeft)):e.defaultView.pageXOffset||(e.documentElement||e.body).scrollLeft}function ln(e){return Sa&&za?-(e.body.getBoundingClientRect().top-parseInt(getComputedStyle(e.body).marginTop)):e.defaultView.pageYOffset||(e.documentElement||e.body).scrollTop}function cn(e){var t=$e(e),n=t.widgets,r=0;if(n)for(var o=0;o<n.length;++o)n[o].above&&(r+=Qt(n[o]));return r}function un(e,t,n,r,o){if(!o){var i=cn(t);n.top+=i,n.bottom+=i}if(\"line\"==r)return n;r||(r=\"local\");var a=at(t);if(\"local\"==r?a+=Ht(e.display):a-=e.display.viewOffset,\"page\"==r||\"window\"==r){var s=e.display.lineSpace.getBoundingClientRect();a+=s.top+(\"window\"==r?0:ln(c(e)));var l=s.left+(\"window\"==r?0:sn(c(e)));n.left+=l,n.right+=l}return n.top+=a,n.bottom+=a,n}function dn(e,t,n){if(\"div\"==n)return t;var r=t.left,o=t.top;if(\"page\"==n)r-=sn(c(e)),o-=ln(c(e));else if(\"local\"==n||!n){var i=e.display.sizer.getBoundingClientRect();r+=i.left,o+=i.top}var a=e.display.lineSpace.getBoundingClientRect();return{left:r-a.left,top:o-a.top}}function pn(e,t,n,r,o){return r||(r=re(e.doc,t.line)),un(e,r,qt(e,r,t.ch,o),n)}function hn(e,t,n,r,o,i){function a(t,a){var s=Zt(e,o,t,a?\"right\":\"left\",i);return a?s.left=s.right:s.right=s.left,un(e,r,s,n)}function s(e,t,n){var r=l[t],o=1==r.level;return a(n?e-1:e,o!=n)}r=r||re(e.doc,t.line),o||(o=Kt(e,r));var l=k(r,e.doc.direction),c=t.ch,u=t.sticky;if(c>=r.text.length?(c=r.text.length,u=\"before\"):0>=c&&(c=0,u=\"after\"),!l)return a(\"before\"==u?c-1:c,\"before\"==u);var d=L(l,c,u),p=is,h=s(c,d,\"before\"==u);return null!=p&&(h.other=s(c,p,\"before\"!=u)),h}function gn(e,t){var n=0;t=Me(e.doc,t),e.options.lineWrapping||(n=Tn(e.display)*t.ch);var r=re(e.doc,t.line),o=at(r)+Ht(e.display);return{left:n,right:n,top:o,bottom:o+r.height}}function fn(e,t,n,r,o){var i=de(e,t,n);return i.xRel=o,r&&(i.outside=r),i}function mn(e,t,n){var r=e.doc;if(n+=e.display.viewOffset,0>n)return fn(r.first,0,null,-1,-1);var o=le(r,n),i=r.first+r.size-1;if(o>i)return fn(r.first+r.size-1,re(r,i).text.length,null,1,1);0>t&&(t=0);for(var a=re(r,o);;){var s=vn(e,a,o,t,n),l=Ke(a,s.ch+(s.xRel>0||s.outside>0?1:0));if(!l)return s;var c=l.find(1);if(c.line==o)return c;a=re(r,o=c.line)}}function An(e,t,n,r){r-=cn(t);var o=t.text.length,i=D(function(t){return Zt(e,n,t-1).bottom<=r},o,0);return o=D(function(t){return Zt(e,n,t).top>r},i,o),{begin:i,end:o}}function Mn(e,t,n,r){n||(n=Kt(e,t));var o=un(e,t,Zt(e,n,r),\"line\").top;return An(e,t,n,o)}function wn(e,t,n,r){return e.bottom<=n?!1:e.top>n?!0:(r?e.left:e.right)>t}function vn(e,t,n,r,o){o-=at(t);var i=Kt(e,t),a=cn(t),s=0,l=t.text.length,c=!0,u=k(t,e.doc.direction);if(u){var d=(e.options.lineWrapping?yn:bn)(e,t,n,i,u,r,o);c=1!=d.level,s=c?d.from:d.to-1,l=c?d.to:d.from-1}var p,h,g=null,f=null,m=D(function(t){var n=Zt(e,i,t);return n.top+=a,n.bottom+=a,wn(n,r,o,!1)?(n.top<=o&&n.left<=r&&(g=t,f=n),!0):!1},s,l),A=!1;if(f){var M=r-f.left<f.right-r,w=M==c;m=g+(w?0:1),h=w?\"after\":\"before\",p=M?f.left:f.right}else{c||m!=l&&m!=s||m++,h=0==m?\"after\":m==t.text.length?\"before\":Zt(e,i,m-(c?1:0)).bottom+a<=o==c?\"after\":\"before\";var v=hn(e,de(n,m,h),\"line\",t,i);p=v.left,A=o<v.top?-1:o>=v.bottom?1:0}return m=E(t.text,m,1),fn(n,m,h,A,r-p)}function bn(e,t,n,r,o,i,a){var s=D(function(s){var l=o[s],c=1!=l.level;return wn(hn(e,de(n,c?l.to:l.from,c?\"before\":\"after\"),\"line\",t,r),i,a,!0)},0,o.length-1),l=o[s];if(s>0){var c=1!=l.level,u=hn(e,de(n,c?l.from:l.to,c?\"after\":\"before\"),\"line\",t,r);wn(u,i,a,!0)&&u.top>a&&(l=o[s-1])}return l}function yn(e,t,n,r,o,i,a){var s=An(e,t,r,a),l=s.begin,c=s.end;/\\s/.test(t.text.charAt(c-1))&&c--;for(var u=null,d=null,p=0;p<o.length;p++){var h=o[p];if(!(h.from>=c||h.to<=l)){var g=1!=h.level,f=Zt(e,r,g?Math.min(c,h.to)-1:Math.max(l,h.from)).right,m=i>f?i-f+1e9:f-i;(!u||d>m)&&(u=h,d=m)}}return u||(u=o[o.length-1]),u.from<l&&(u={from:l,to:u.to,level:u.level}),u.to>c&&(u={from:u.from,to:c,level:u.level}),u}function xn(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Ts){Ts=r(\"pre\",null,\"CodeMirror-line-like\");for(var o=0;49>o;++o)Ts.appendChild(document.createTextNode(\"x\")),Ts.appendChild(r(\"br\"));Ts.appendChild(document.createTextNode(\"x\"))}n(e.measure,Ts);var i=Ts.offsetHeight/50;return i>3&&(e.cachedTextHeight=i),t(e.measure),i||1}function Tn(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=r(\"span\",\"xxxxxxxxxx\"),o=r(\"pre\",[t],\"CodeMirror-line-like\");n(e.measure,o);var i=t.getBoundingClientRect(),a=(i.right-i.left)/10;return a>2&&(e.cachedCharWidth=a),a||10}function Cn(e){for(var t=e.display,n={},r={},o=t.gutters.clientLeft,i=t.gutters.firstChild,a=0;i;i=i.nextSibling,++a){var s=e.display.gutterSpecs[a].className;n[s]=i.offsetLeft+i.clientLeft+o,r[s]=i.clientWidth}return{fixedPos:Nn(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:n,gutterWidth:r,wrapperWidth:t.wrapper.clientWidth}}function Nn(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function In(e){var t=xn(e.display),n=e.options.lineWrapping,r=n&&Math.max(5,e.display.scroller.clientWidth/Tn(e.display)-3);return function(o){if(ot(e.doc,o))return 0;var i=0;if(o.widgets)for(var a=0;a<o.widgets.length;a++)o.widgets[a].height&&(i+=o.widgets[a].height);return n?i+(Math.ceil(o.text.length/r)||1)*t:i+t}}function En(e){var t=e.doc,n=In(e);t.iter(function(e){var t=n(e);t!=e.height&&ae(e,t)})}function Dn(e,t,n,r){var o=e.display;if(!n&&\"true\"==Y(t).getAttribute(\"cm-not-content\"))return null;var i,a,s=o.lineSpace.getBoundingClientRect();try{i=t.clientX-s.left,a=t.clientY-s.top}catch(l){return null}var c,u=mn(e,i,a);if(r&&u.xRel>0&&(c=re(e.doc,u.line).text).length==u.ch){var d=f(c,c.length,e.options.tabSize)-c.length;u=de(u.line,Math.max(0,Math.round((i-Vt(e.display).left)/Tn(e.display))-d))}return u}function Sn(e,t){if(t>=e.display.viewTo)return null;if(t-=e.display.viewFrom,0>t)return null;for(var n=e.display.view,r=0;r<n.length;r++)if(t-=n[r].size,0>t)return r}function Ln(e,t,n,r){null==t&&(t=e.doc.first),null==n&&(n=e.doc.first+e.doc.size),r||(r=0);var o=e.display;if(r&&n<o.viewTo&&(null==o.updateLineNumbers||o.updateLineNumbers>t)&&(o.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=o.viewTo)ys&&nt(e.doc,t)<o.viewTo&&jn(e);else if(n<=o.viewFrom)ys&&rt(e.doc,n+r)>o.viewFrom?jn(e):(o.viewFrom+=r,o.viewTo+=r);else if(t<=o.viewFrom&&n>=o.viewTo)jn(e);else if(t<=o.viewFrom){var i=Un(e,n,n+r,1);i?(o.view=o.view.slice(i.index),o.viewFrom=i.lineN,o.viewTo+=r):jn(e)}else if(n>=o.viewTo){var a=Un(e,t,t,-1);a?(o.view=o.view.slice(0,a.index),o.viewTo=a.lineN):jn(e)}else{var s=Un(e,t,t,-1),l=Un(e,n,n+r,1);s&&l?(o.view=o.view.slice(0,s.index).concat(vt(e,s.lineN,l.lineN)).concat(o.view.slice(l.index)),o.viewTo+=r):jn(e)}var c=o.externalMeasured;c&&(n<c.lineN?c.lineN+=r:t<c.lineN+c.size&&(o.externalMeasured=null))}function kn(e,t,n){e.curOp.viewChanged=!0;var r=e.display,o=e.display.externalMeasured;if(o&&t>=o.lineN&&t<o.lineN+o.size&&(r.externalMeasured=null),!(t<r.viewFrom||t>=r.viewTo)){var i=r.view[Sn(e,t)];if(null!=i.node){var a=i.changes||(i.changes=[]);-1==m(a,n)&&a.push(n)}}}function jn(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function Un(e,t,n,r){var o,i=Sn(e,t),a=e.display.view;if(!ys||n==e.doc.first+e.doc.size)return{index:i,lineN:n};for(var s=e.display.viewFrom,l=0;i>l;l++)s+=a[l].size;if(s!=t){if(r>0){if(i==a.length-1)return null;o=s+a[i].size-t,i++}else o=s-t;t+=o,n+=o}for(;nt(e.doc,n)!=n;){if(i==(0>r?0:a.length-1))return null;n+=r*a[i-(0>r?1:0)].size,i+=r}return{index:i,lineN:n}}function Bn(e,t,n){var r=e.display,o=r.view;0==o.length||t>=r.viewTo||n<=r.viewFrom?(r.view=vt(e,t,n),r.viewFrom=t):(r.viewFrom>t?r.view=vt(e,t,r.viewFrom).concat(r.view):r.viewFrom<t&&(r.view=r.view.slice(Sn(e,t))),r.viewFrom=t,r.viewTo<n?r.view=r.view.concat(vt(e,r.viewTo,n)):r.viewTo>n&&(r.view=r.view.slice(0,Sn(e,n)))),r.viewTo=n}function Rn(e){for(var t=e.display.view,n=0,r=0;r<t.length;r++){var o=t[r];o.hidden||o.node&&!o.changes||++n}return n}function zn(e){e.display.input.showSelection(e.display.input.prepareSelection())}function Qn(e,t){void 0===t&&(t=!0);var n=e.doc,r={},o=r.cursors=document.createDocumentFragment(),i=r.selection=document.createDocumentFragment(),a=e.options.$customCursor;a&&(t=!0);for(var s=0;s<n.sel.ranges.length;s++)if(t||s!=n.sel.primIndex){var l=n.sel.ranges[s];if(!(l.from().line>=e.display.viewTo||l.to().line<e.display.viewFrom)){var c=l.empty();if(a){var u=a(e,l);u&&On(e,u,o)}else(c||e.options.showCursorWhenSelecting)&&On(e,l.head,o);c||Fn(e,l,i)}}return r}function On(e,t,n){var o=hn(e,t,\"div\",null,null,!e.options.singleCursorHeightPerLine),i=n.appendChild(r(\"div\",\" \",\"CodeMirror-cursor\"));if(i.style.left=o.left+\"px\",i.style.top=o.top+\"px\",i.style.height=Math.max(0,o.bottom-o.top)*e.options.cursorHeight+\"px\",/\\bcm-fat-cursor\\b/.test(e.getWrapperElement().className)){var a=pn(e,t,\"div\",null,null),s=a.right-a.left;i.style.width=(s>0?s:e.defaultCharWidth())+\"px\"}if(o.other){var l=n.appendChild(r(\"div\",\" \",\"CodeMirror-cursor CodeMirror-secondarycursor\"));l.style.display=\"\",l.style.left=o.other.left+\"px\",l.style.top=o.other.top+\"px\",l.style.height=.85*(o.other.bottom-o.other.top)+\"px\"}}function Hn(e,t){return e.top-t.top||e.left-t.left}function Fn(e,t,n){function o(e,t,n,o){0>t&&(t=0),t=Math.round(t),o=Math.round(o),l.appendChild(r(\"div\",null,\"CodeMirror-selected\",\"position: absolute; left: \"+e+\"px;\\n                             top: \"+t+\"px; width: \"+(null==n?d-e:n)+\"px;\\n                             height: \"+(o-t)+\"px\"))}function i(t,n,r){function i(n,r){return pn(e,de(t,n),\"div\",h,r)}function a(t,n,r){var o=Mn(e,h,null,t),a=\"ltr\"==n==(\"after\"==r)?\"left\":\"right\",s=\"after\"==r?o.begin:o.end-(/\\s/.test(h.text.charAt(o.end-1))?2:1);return i(s,a)[a]}var l,c,h=re(s,t),g=h.text.length,f=k(h,s.direction);return S(f,n||0,null==r?g:r,function(e,t,s,h){var m=\"ltr\"==s,A=i(e,m?\"left\":\"right\"),M=i(t-1,m?\"right\":\"left\"),w=null==n&&0==e,v=null==r&&t==g,b=0==h,y=!f||h==f.length-1;if(M.top-A.top<=3){var x=(p?w:v)&&b,T=(p?v:w)&&y,C=x?u:(m?A:M).left,N=T?d:(m?M:A).right;o(C,A.top,N-C,A.bottom)}else{var I,E,D,S;m?(I=p&&w&&b?u:A.left,E=p?d:a(e,s,\"before\"),D=p?u:a(t,s,\"after\"),S=p&&v&&y?d:M.right):(I=p?a(e,s,\"before\"):u,E=!p&&w&&b?d:A.right,D=!p&&v&&y?u:M.left,S=p?a(t,s,\"after\"):d),o(I,A.top,E-I,A.bottom),A.bottom<M.top&&o(u,A.bottom,null,M.top),o(D,M.top,S-D,M.bottom)}(!l||Hn(A,l)<0)&&(l=A),Hn(M,l)<0&&(l=M),(!c||Hn(A,c)<0)&&(c=A),Hn(M,c)<0&&(c=M)}),{start:l,end:c}}var a=e.display,s=e.doc,l=document.createDocumentFragment(),c=Vt(e.display),u=c.left,d=Math.max(a.sizerWidth,Yt(e)-a.sizer.offsetLeft)-c.right,p=\"ltr\"==s.direction,h=t.from(),g=t.to();if(h.line==g.line)i(h.line,h.ch,g.ch);else{var f=re(s,h.line),m=re(s,g.line),A=$e(f)==$e(m),M=i(h.line,h.ch,A?f.text.length+1:null).end,w=i(g.line,A?0:null,g.ch).start;A&&(M.top<w.top-2?(o(M.right,M.top,null,M.bottom),o(u,w.top,w.left,w.bottom)):o(M.right,M.top,w.left-M.right,M.bottom)),M.bottom<w.top&&o(u,M.bottom,null,w.top)}n.appendChild(l)}function Vn(e){if(e.state.focused){var t=e.display;clearInterval(t.blinker);var n=!0;t.cursorDiv.style.visibility=\"\",e.options.cursorBlinkRate>0?t.blinker=setInterval(function(){e.hasFocus()||Wn(e),t.cursorDiv.style.visibility=(n=!n)?\"\":\"hidden\"},e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility=\"hidden\")}}function Pn(e){e.hasFocus()||(e.display.input.focus(),e.state.focused||Gn(e))}function Yn(e){e.state.delayingBlurEvent=!0,setTimeout(function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,e.state.focused&&Wn(e))},100)}function Gn(e,t){e.state.delayingBlurEvent&&!e.state.draggingText&&(e.state.delayingBlurEvent=!1),\"nocursor\"!=e.options.readOnly&&(e.state.focused||(B(e,\"focus\",e,t),e.state.focused=!0,s(e.display.wrapper,\"CodeMirror-focused\"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),Ea&&setTimeout(function(){return e.display.input.reset(!0)},20)),e.display.input.receivedFocus()),Vn(e))}function Wn(e,t){e.state.delayingBlurEvent||(e.state.focused&&(B(e,\"blur\",e,t),e.state.focused=!1,Wa(e.display.wrapper,\"CodeMirror-focused\")),clearInterval(e.display.blinker),setTimeout(function(){e.state.focused||(e.display.shift=!1)},150))}function Xn(e){for(var t=e.display,n=t.lineDiv.offsetTop,r=Math.max(0,t.scroller.getBoundingClientRect().top),o=t.lineDiv.getBoundingClientRect().top,i=0,a=0;a<t.view.length;a++){var s=t.view[a],l=e.options.lineWrapping,c=void 0,u=0;if(!s.hidden){if(o+=s.line.height,Na&&8>Ia){var d=s.node.offsetTop+s.node.offsetHeight;c=d-n,n=d}else{var p=s.node.getBoundingClientRect();c=p.bottom-p.top,!l&&s.text.firstChild&&(u=s.text.firstChild.getBoundingClientRect().right-p.left-1)}var h=s.line.height-c;if((h>.005||-.005>h)&&(r>o&&(i-=h),ae(s.line,c),_n(s.line),s.rest))for(var g=0;g<s.rest.length;g++)_n(s.rest[g]);if(u>e.display.sizerWidth){var f=Math.ceil(u/Tn(e.display));f>e.display.maxLineLength&&(e.display.maxLineLength=f,e.display.maxLine=s.line,e.display.maxLineChanged=!0)}}}Math.abs(i)>2&&(t.scroller.scrollTop+=i)}function _n(e){if(e.widgets)for(var t=0;t<e.widgets.length;++t){var n=e.widgets[t],r=n.node.parentNode;r&&(n.height=r.offsetHeight)}}function qn(e,t,n){var r=n&&null!=n.top?Math.max(0,n.top):e.scroller.scrollTop;r=Math.floor(r-Ht(e));var o=n&&null!=n.bottom?n.bottom:r+e.wrapper.clientHeight,i=le(t,r),a=le(t,o);if(n&&n.ensure){var s=n.ensure.from.line,l=n.ensure.to.line;i>s?(i=s,a=le(t,at(re(t,s))+e.wrapper.clientHeight)):Math.min(l,t.lastLine())>=a&&(i=le(t,at(re(t,l))-e.wrapper.clientHeight),a=l)}return{from:i,to:Math.max(a,i+1)}}function Jn(e,t){if(!R(e,\"scrollCursorIntoView\")){var n=e.display,o=n.sizer.getBoundingClientRect(),i=null,a=n.wrapper.ownerDocument;if(t.top+o.top<0?i=!0:t.bottom+o.top>(a.defaultView.innerHeight||a.documentElement.clientHeight)&&(i=!1),null!=i&&!Ba){var s=r(\"div\",\"​\",null,\"position: absolute;\\n                         top: \"+(t.top-n.viewOffset-Ht(e.display))+\"px;\\n                         height: \"+(t.bottom-t.top+Pt(e)+n.barHeight)+\"px;\\n                         left: \"+t.left+\"px; width: \"+Math.max(2,t.right-t.left)+\"px;\");e.display.lineSpace.appendChild(s),s.scrollIntoView(i),e.display.lineSpace.removeChild(s)}}}function Kn(e,t,n,r){null==r&&(r=0);var o;e.options.lineWrapping||t!=n||(n=\"before\"==t.sticky?de(t.line,t.ch+1,\"before\"):t,t=t.ch?de(t.line,\"before\"==t.sticky?t.ch-1:t.ch,\"after\"):t);for(var i=0;5>i;i++){var a=!1,s=hn(e,t),l=n&&n!=t?hn(e,n):s;o={left:Math.min(s.left,l.left),top:Math.min(s.top,l.top)-r,right:Math.max(s.left,l.left),bottom:Math.max(s.bottom,l.bottom)+r};var c=$n(e,o),u=e.doc.scrollTop,d=e.doc.scrollLeft;if(null!=c.scrollTop&&(ar(e,c.scrollTop),Math.abs(e.doc.scrollTop-u)>1&&(a=!0)),null!=c.scrollLeft&&(lr(e,c.scrollLeft),Math.abs(e.doc.scrollLeft-d)>1&&(a=!0)),!a)break}return o}function Zn(e,t){var n=$n(e,t);null!=n.scrollTop&&ar(e,n.scrollTop),null!=n.scrollLeft&&lr(e,n.scrollLeft)}function $n(e,t){var n=e.display,r=xn(e.display);t.top<0&&(t.top=0);var o=e.curOp&&null!=e.curOp.scrollTop?e.curOp.scrollTop:n.scroller.scrollTop,i=Gt(e),a={};t.bottom-t.top>i&&(t.bottom=t.top+i);var s=e.doc.height+Ft(n),l=t.top<r,c=t.bottom>s-r;if(t.top<o)a.scrollTop=l?0:t.top;else if(t.bottom>o+i){var u=Math.min(t.top,(c?s:t.bottom)-i);u!=o&&(a.scrollTop=u)}var d=e.options.fixedGutter?0:n.gutters.offsetWidth,p=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:n.scroller.scrollLeft-d,h=Yt(e)-n.gutters.offsetWidth,g=t.right-t.left>h;return g&&(t.right=t.left+h),t.left<10?a.scrollLeft=0:t.left<p?a.scrollLeft=Math.max(0,t.left+d-(g?0:10)):t.right>h+p-3&&(a.scrollLeft=t.right+(g?0:10)-h),a}function er(e,t){null!=t&&(or(e),e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc.scrollTop:e.curOp.scrollTop)+t)}function tr(e){or(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function nr(e,t,n){(null!=t||null!=n)&&or(e),null!=t&&(e.curOp.scrollLeft=t),null!=n&&(e.curOp.scrollTop=n)}function rr(e,t){or(e),e.curOp.scrollToPos=t}function or(e){var t=e.curOp.scrollToPos;if(t){e.curOp.scrollToPos=null;var n=gn(e,t.from),r=gn(e,t.to);ir(e,n,r,t.margin)}}function ir(e,t,n,r){var o=$n(e,{left:Math.min(t.left,n.left),top:Math.min(t.top,n.top)-r,right:Math.max(t.right,n.right),bottom:Math.max(t.bottom,n.bottom)+r});nr(e,o.scrollLeft,o.scrollTop)}function ar(e,t){Math.abs(e.doc.scrollTop-t)<2||(ya||kr(e,{top:t}),sr(e,t,!0),ya&&kr(e),Cr(e,100))}function sr(e,t,n){t=Math.max(0,Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t)),(e.display.scroller.scrollTop!=t||n)&&(e.doc.scrollTop=t,e.display.scrollbars.setScrollTop(t),e.display.scroller.scrollTop!=t&&(e.display.scroller.scrollTop=t))}function lr(e,t,n,r){t=Math.max(0,Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth)),(!(n?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)||r)&&(e.doc.scrollLeft=t,Rr(e),e.display.scroller.scrollLeft!=t&&(e.display.scroller.scrollLeft=t),e.display.scrollbars.setScrollLeft(t))}function cr(e){var t=e.display,n=t.gutters.offsetWidth,r=Math.round(e.doc.height+Ft(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+Pt(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:n}}function ur(e,t){t||(t=cr(e));\nvar n=e.display.barWidth,r=e.display.barHeight;dr(e,t);for(var o=0;4>o&&n!=e.display.barWidth||r!=e.display.barHeight;o++)n!=e.display.barWidth&&e.options.lineWrapping&&Xn(e),dr(e,cr(e)),n=e.display.barWidth,r=e.display.barHeight}function dr(e,t){var n=e.display,r=n.scrollbars.update(t);n.sizer.style.paddingRight=(n.barWidth=r.right)+\"px\",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+\"px\",n.heightForcer.style.borderBottom=r.bottom+\"px solid transparent\",r.right&&r.bottom?(n.scrollbarFiller.style.display=\"block\",n.scrollbarFiller.style.height=r.bottom+\"px\",n.scrollbarFiller.style.width=r.right+\"px\"):n.scrollbarFiller.style.display=\"\",r.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(n.gutterFiller.style.display=\"block\",n.gutterFiller.style.height=r.bottom+\"px\",n.gutterFiller.style.width=t.gutterWidth+\"px\"):n.gutterFiller.style.display=\"\"}function pr(e){e.display.scrollbars&&(e.display.scrollbars.clear(),e.display.scrollbars.addClass&&Wa(e.display.wrapper,e.display.scrollbars.addClass)),e.display.scrollbars=new ks[e.options.scrollbarStyle](function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller),ls(t,\"mousedown\",function(){e.state.focused&&setTimeout(function(){return e.display.input.focus()},0)}),t.setAttribute(\"cm-not-content\",\"true\")},function(t,n){\"horizontal\"==n?lr(e,t):ar(e,t)},e),e.display.scrollbars.addClass&&s(e.display.wrapper,e.display.scrollbars.addClass)}function hr(e){e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++js,markArrays:null},bt(e.curOp)}function gr(e){var t=e.curOp;t&&xt(t,function(e){for(var t=0;t<e.ops.length;t++)e.ops[t].cm.curOp=null;fr(e)})}function fr(e){for(var t=e.ops,n=0;n<t.length;n++)mr(t[n]);for(var r=0;r<t.length;r++)Ar(t[r]);for(var o=0;o<t.length;o++)Mr(t[o]);for(var i=0;i<t.length;i++)wr(t[i]);for(var a=0;a<t.length;a++)vr(t[a])}function mr(e){var t=e.cm,n=t.display;Ir(t),e.updateMaxLine&&lt(t),e.mustUpdate=e.viewChanged||e.forceUpdate||null!=e.scrollTop||e.scrollToPos&&(e.scrollToPos.from.line<n.viewFrom||e.scrollToPos.to.line>=n.viewTo)||n.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new Us(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function Ar(e){e.updatedDisplay=e.mustUpdate&&Sr(e.cm,e.update)}function Mr(e){var t=e.cm,n=t.display;e.updatedDisplay&&Xn(t),e.barMeasure=cr(t),n.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=qt(t,n.maxLine,n.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+e.adjustWidthTo+Pt(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+e.adjustWidthTo-Yt(t))),(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=n.input.prepareSelection())}function wr(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+\"px\",e.maxScrollLeft<t.doc.scrollLeft&&lr(t,Math.min(t.display.scroller.scrollLeft,e.maxScrollLeft),!0),t.display.maxLineChanged=!1);var n=e.focus&&e.focus==a(u(t));e.preparedSelection&&t.display.input.showSelection(e.preparedSelection,n),(e.updatedDisplay||e.startHeight!=t.doc.height)&&ur(t,e.barMeasure),e.updatedDisplay&&Br(t,e.barMeasure),e.selectionChanged&&Vn(t),t.state.focused&&e.updateInput&&t.display.input.reset(e.typing),n&&Pn(e.cm)}function vr(e){var t=e.cm,n=t.display,r=t.doc;if(e.updatedDisplay&&Lr(t,e.update),null==n.wheelStartX||null==e.scrollTop&&null==e.scrollLeft&&!e.scrollToPos||(n.wheelStartX=n.wheelStartY=null),null!=e.scrollTop&&sr(t,e.scrollTop,e.forceScroll),null!=e.scrollLeft&&lr(t,e.scrollLeft,!0,!0),e.scrollToPos){var o=Kn(t,Me(r,e.scrollToPos.from),Me(r,e.scrollToPos.to),e.scrollToPos.margin);Jn(t,o)}var i=e.maybeHiddenMarkers,a=e.maybeUnhiddenMarkers;if(i)for(var s=0;s<i.length;++s)i[s].lines.length||B(i[s],\"hide\");if(a)for(var l=0;l<a.length;++l)a[l].lines.length&&B(a[l],\"unhide\");n.wrapper.offsetHeight&&(r.scrollTop=t.display.scroller.scrollTop),e.changeObjs&&B(t,\"changes\",t,e.changeObjs),e.update&&e.update.finish()}function br(e,t){if(e.curOp)return t();hr(e);try{return t()}finally{gr(e)}}function yr(e,t){return function(){if(e.curOp)return t.apply(e,arguments);hr(e);try{return t.apply(e,arguments)}finally{gr(e)}}}function xr(e){return function(){if(this.curOp)return e.apply(this,arguments);hr(this);try{return e.apply(this,arguments)}finally{gr(this)}}}function Tr(e){return function(){var t=this.cm;if(!t||t.curOp)return e.apply(this,arguments);hr(t);try{return e.apply(this,arguments)}finally{gr(t)}}}function Cr(e,t){e.doc.highlightFrontier<e.display.viewTo&&e.state.highlight.set(t,h(Nr,e))}function Nr(e){var t=e.doc;if(!(t.highlightFrontier>=e.display.viewTo)){var n=+new Date+e.options.workTime,r=xe(e,t.highlightFrontier),o=[];t.iter(r.line,Math.min(t.first+t.size,e.display.viewTo+500),function(i){if(r.line>=e.display.viewFrom){var a=i.styles,s=i.text.length>e.options.maxHighlightLength?ee(t.mode,r.state):null,l=be(e,i,r,!0);s&&(r.state=s),i.styles=l.styles;var c=i.styleClasses,u=l.classes;u?i.styleClasses=u:c&&(i.styleClasses=null);for(var d=!a||a.length!=i.styles.length||c!=u&&(!c||!u||c.bgClass!=u.bgClass||c.textClass!=u.textClass),p=0;!d&&p<a.length;++p)d=a[p]!=i.styles[p];d&&o.push(r.line),i.stateAfter=r.save(),r.nextLine()}else i.text.length<=e.options.maxHighlightLength&&Te(e,i.text,r),i.stateAfter=r.line%5==0?r.save():null,r.nextLine();return+new Date>n?(Cr(e,e.options.workDelay),!0):void 0}),t.highlightFrontier=r.line,t.modeFrontier=Math.max(t.modeFrontier,r.line),o.length&&br(e,function(){for(var t=0;t<o.length;t++)kn(e,o[t],\"text\")})}}function Ir(e){var t=e.display;!t.scrollbarsClipped&&t.scroller.offsetWidth&&(t.nativeBarWidth=t.scroller.offsetWidth-t.scroller.clientWidth,t.heightForcer.style.height=Pt(e)+\"px\",t.sizer.style.marginBottom=-t.nativeBarWidth+\"px\",t.sizer.style.borderRightWidth=Pt(e)+\"px\",t.scrollbarsClipped=!0)}function Er(e){if(e.hasFocus())return null;var t=a(u(e));if(!t||!i(e.display.lineDiv,t))return null;var n={activeElt:t};if(window.getSelection){var r=p(e).getSelection();r.anchorNode&&r.extend&&i(e.display.lineDiv,r.anchorNode)&&(n.anchorNode=r.anchorNode,n.anchorOffset=r.anchorOffset,n.focusNode=r.focusNode,n.focusOffset=r.focusOffset)}return n}function Dr(e){if(e&&e.activeElt&&e.activeElt!=a(d(e.activeElt))&&(e.activeElt.focus(),!/^(INPUT|TEXTAREA)$/.test(e.activeElt.nodeName)&&e.anchorNode&&i(document.body,e.anchorNode)&&i(document.body,e.focusNode))){var t=e.activeElt.ownerDocument,n=t.defaultView.getSelection(),r=t.createRange();r.setEnd(e.anchorNode,e.anchorOffset),r.collapse(!1),n.removeAllRanges(),n.addRange(r),n.extend(e.focusNode,e.focusOffset)}}function Sr(e,n){var r=e.display,o=e.doc;if(n.editorIsHidden)return jn(e),!1;if(!n.force&&n.visible.from>=r.viewFrom&&n.visible.to<=r.viewTo&&(null==r.updateLineNumbers||r.updateLineNumbers>=r.viewTo)&&r.renderedView==r.view&&0==Rn(e))return!1;zr(e)&&(jn(e),n.dims=Cn(e));var i=o.first+o.size,a=Math.max(n.visible.from-e.options.viewportMargin,o.first),s=Math.min(i,n.visible.to+e.options.viewportMargin);r.viewFrom<a&&a-r.viewFrom<20&&(a=Math.max(o.first,r.viewFrom)),r.viewTo>s&&r.viewTo-s<20&&(s=Math.min(i,r.viewTo)),ys&&(a=nt(e.doc,a),s=rt(e.doc,s));var l=a!=r.viewFrom||s!=r.viewTo||r.lastWrapHeight!=n.wrapperHeight||r.lastWrapWidth!=n.wrapperWidth;Bn(e,a,s),r.viewOffset=at(re(e.doc,r.viewFrom)),e.display.mover.style.top=r.viewOffset+\"px\";var c=Rn(e);if(!l&&0==c&&!n.force&&r.renderedView==r.view&&(null==r.updateLineNumbers||r.updateLineNumbers>=r.viewTo))return!1;var u=Er(e);return c>4&&(r.lineDiv.style.display=\"none\"),jr(e,r.updateLineNumbers,n.dims),c>4&&(r.lineDiv.style.display=\"\"),r.renderedView=r.view,Dr(u),t(r.cursorDiv),t(r.selectionDiv),r.gutters.style.height=r.sizer.style.minHeight=0,l&&(r.lastWrapHeight=n.wrapperHeight,r.lastWrapWidth=n.wrapperWidth,Cr(e,400)),r.updateLineNumbers=null,!0}function Lr(e,t){for(var n=t.viewport,r=!0;;r=!1){if(r&&e.options.lineWrapping&&t.oldDisplayWidth!=Yt(e))r&&(t.visible=qn(e.display,e.doc,n));else if(n&&null!=n.top&&(n={top:Math.min(e.doc.height+Ft(e.display)-Gt(e),n.top)}),t.visible=qn(e.display,e.doc,n),t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)break;if(!Sr(e,t))break;Xn(e);var o=cr(e);zn(e),ur(e,o),Br(e,o),t.force=!1}t.signal(e,\"update\",e),(e.display.viewFrom!=e.display.reportedViewFrom||e.display.viewTo!=e.display.reportedViewTo)&&(t.signal(e,\"viewportChange\",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function kr(e,t){var n=new Us(e,t);if(Sr(e,n)){Xn(e),Lr(e,n);var r=cr(e);zn(e),ur(e,r),Br(e,r),n.finish()}}function jr(e,n,r){function o(t){var n=t.nextSibling;return Ea&&Oa&&e.display.currentWheelTarget==t?t.style.display=\"none\":t.parentNode.removeChild(t),n}for(var i=e.display,a=e.options.lineNumbers,s=i.lineDiv,l=s.firstChild,c=i.view,u=i.viewFrom,d=0;d<c.length;d++){var p=c[d];if(p.hidden);else if(p.node&&p.node.parentNode==s){for(;l!=p.node;)l=o(l);var h=a&&null!=n&&u>=n&&p.lineNumber;p.changes&&(m(p.changes,\"gutter\")>-1&&(h=!1),Nt(e,p,u,r)),h&&(t(p.lineNumber),p.lineNumber.appendChild(document.createTextNode(ue(e.options,u)))),l=p.node.nextSibling}else{var g=Ut(e,p,u,r);s.insertBefore(g,l)}u+=p.size}for(;l;)l=o(l)}function Ur(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+\"px\",Tt(e,\"gutterChanged\",e)}function Br(e,t){e.display.sizer.style.minHeight=t.docHeight+\"px\",e.display.heightForcer.style.top=t.docHeight+\"px\",e.display.gutters.style.height=t.docHeight+e.display.barHeight+Pt(e)+\"px\"}function Rr(e){var t=e.display,n=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var r=Nn(t)-t.scroller.scrollLeft+e.doc.scrollLeft,o=t.gutters.offsetWidth,i=r+\"px\",a=0;a<n.length;a++)if(!n[a].hidden){e.options.fixedGutter&&(n[a].gutter&&(n[a].gutter.style.left=i),n[a].gutterBackground&&(n[a].gutterBackground.style.left=i));var s=n[a].alignable;if(s)for(var l=0;l<s.length;l++)s[l].style.left=i}e.options.fixedGutter&&(t.gutters.style.left=r+o+\"px\")}}function zr(e){if(!e.options.lineNumbers)return!1;var t=e.doc,n=ue(e.options,t.first+t.size-1),o=e.display;if(n.length!=o.lineNumChars){var i=o.measure.appendChild(r(\"div\",[r(\"div\",n)],\"CodeMirror-linenumber CodeMirror-gutter-elt\")),a=i.firstChild.offsetWidth,s=i.offsetWidth-a;return o.lineGutter.style.width=\"\",o.lineNumInnerWidth=Math.max(a,o.lineGutter.offsetWidth-s)+1,o.lineNumWidth=o.lineNumInnerWidth+s,o.lineNumChars=o.lineNumInnerWidth?n.length:-1,o.lineGutter.style.width=o.lineNumWidth+\"px\",Ur(e.display),!0}return!1}function Qr(e,t){for(var n=[],r=!1,o=0;o<e.length;o++){var i=e[o],a=null;if(\"string\"!=typeof i&&(a=i.style,i=i.className),\"CodeMirror-linenumbers\"==i){if(!t)continue;r=!0}n.push({className:i,style:a})}return t&&!r&&n.push({className:\"CodeMirror-linenumbers\",style:null}),n}function Or(e){var n=e.gutters,o=e.gutterSpecs;t(n),e.lineGutter=null;for(var i=0;i<o.length;++i){var a=o[i],s=a.className,l=a.style,c=n.appendChild(r(\"div\",null,\"CodeMirror-gutter \"+s));l&&(c.style.cssText=l),\"CodeMirror-linenumbers\"==s&&(e.lineGutter=c,c.style.width=(e.lineNumWidth||1)+\"px\")}n.style.display=o.length?\"\":\"none\",Ur(e)}function Hr(e){Or(e.display),Ln(e),Rr(e)}function Fr(e,t,n,i){var a=this;this.input=n,a.scrollbarFiller=r(\"div\",null,\"CodeMirror-scrollbar-filler\"),a.scrollbarFiller.setAttribute(\"cm-not-content\",\"true\"),a.gutterFiller=r(\"div\",null,\"CodeMirror-gutter-filler\"),a.gutterFiller.setAttribute(\"cm-not-content\",\"true\"),a.lineDiv=o(\"div\",null,\"CodeMirror-code\"),a.selectionDiv=r(\"div\",null,null,\"position: relative; z-index: 1\"),a.cursorDiv=r(\"div\",null,\"CodeMirror-cursors\"),a.measure=r(\"div\",null,\"CodeMirror-measure\"),a.lineMeasure=r(\"div\",null,\"CodeMirror-measure\"),a.lineSpace=o(\"div\",[a.measure,a.lineMeasure,a.selectionDiv,a.cursorDiv,a.lineDiv],null,\"position: relative; outline: none\");var s=o(\"div\",[a.lineSpace],\"CodeMirror-lines\");a.mover=r(\"div\",[s],null,\"position: relative\"),a.sizer=r(\"div\",[a.mover],\"CodeMirror-sizer\"),a.sizerWidth=null,a.heightForcer=r(\"div\",null,null,\"position: absolute; height: \"+Ka+\"px; width: 1px;\"),a.gutters=r(\"div\",null,\"CodeMirror-gutters\"),a.lineGutter=null,a.scroller=r(\"div\",[a.sizer,a.heightForcer,a.gutters],\"CodeMirror-scroll\"),a.scroller.setAttribute(\"tabIndex\",\"-1\"),a.wrapper=r(\"div\",[a.scrollbarFiller,a.gutterFiller,a.scroller],\"CodeMirror\"),Sa&&La>=105&&(a.wrapper.style.clipPath=\"inset(0px)\"),a.wrapper.setAttribute(\"translate\",\"no\"),Na&&8>Ia&&(a.gutters.style.zIndex=-1,a.scroller.style.paddingRight=0),Ea||ya&&Qa||(a.scroller.draggable=!0),e&&(e.appendChild?e.appendChild(a.wrapper):e(a.wrapper)),a.viewFrom=a.viewTo=t.first,a.reportedViewFrom=a.reportedViewTo=t.first,a.view=[],a.renderedView=null,a.externalMeasured=null,a.viewOffset=0,a.lastWrapHeight=a.lastWrapWidth=0,a.updateLineNumbers=null,a.nativeBarWidth=a.barHeight=a.barWidth=0,a.scrollbarsClipped=!1,a.lineNumWidth=a.lineNumInnerWidth=a.lineNumChars=null,a.alignWidgets=!1,a.cachedCharWidth=a.cachedTextHeight=a.cachedPaddingH=null,a.maxLine=null,a.maxLineLength=0,a.maxLineChanged=!1,a.wheelDX=a.wheelDY=a.wheelStartX=a.wheelStartY=null,a.shift=!1,a.selForContextMenu=null,a.activeTouch=null,a.gutterSpecs=Qr(i.gutters,i.lineNumbers),Or(a),n.init(a)}function Vr(e){var t=e.wheelDeltaX,n=e.wheelDeltaY;return null==t&&e.detail&&e.axis==e.HORIZONTAL_AXIS&&(t=e.detail),null==n&&e.detail&&e.axis==e.VERTICAL_AXIS?n=e.detail:null==n&&(n=e.wheelDelta),{x:t,y:n}}function Pr(e){var t=Vr(e);return t.x*=Rs,t.y*=Rs,t}function Yr(e,t){Sa&&102==La&&(null==e.display.chromeScrollHack?e.display.sizer.style.pointerEvents=\"none\":clearTimeout(e.display.chromeScrollHack),e.display.chromeScrollHack=setTimeout(function(){e.display.chromeScrollHack=null,e.display.sizer.style.pointerEvents=\"\"},100));var n=Vr(t),r=n.x,o=n.y,i=Rs;0===t.deltaMode&&(r=t.deltaX,o=t.deltaY,i=1);var a=e.display,s=a.scroller,l=s.scrollWidth>s.clientWidth,c=s.scrollHeight>s.clientHeight;if(r&&l||o&&c){if(o&&Oa&&Ea)e:for(var u=t.target,d=a.view;u!=s;u=u.parentNode)for(var p=0;p<d.length;p++)if(d[p].node==u){e.display.currentWheelTarget=u;break e}if(r&&!ya&&!ka&&null!=i)return o&&c&&ar(e,Math.max(0,s.scrollTop+o*i)),lr(e,Math.max(0,s.scrollLeft+r*i)),(!o||o&&c)&&H(t),void(a.wheelStartX=null);if(o&&null!=i){var h=o*i,g=e.doc.scrollTop,f=g+a.wrapper.clientHeight;0>h?g=Math.max(0,g+h-50):f=Math.min(e.doc.height,f+h+50),kr(e,{top:g,bottom:f})}20>Bs&&0!==t.deltaMode&&(null==a.wheelStartX?(a.wheelStartX=s.scrollLeft,a.wheelStartY=s.scrollTop,a.wheelDX=r,a.wheelDY=o,setTimeout(function(){if(null!=a.wheelStartX){var e=s.scrollLeft-a.wheelStartX,t=s.scrollTop-a.wheelStartY,n=t&&a.wheelDY&&t/a.wheelDY||e&&a.wheelDX&&e/a.wheelDX;a.wheelStartX=a.wheelStartY=null,n&&(Rs=(Rs*Bs+n)/(Bs+1),++Bs)}},200)):(a.wheelDX+=r,a.wheelDY+=o))}}function Gr(e,t,n){var r=e&&e.options.selectionsMayTouch,o=t[n];t.sort(function(e,t){return pe(e.from(),t.from())}),n=m(t,o);for(var i=1;i<t.length;i++){var a=t[i],s=t[i-1],l=pe(s.to(),a.from());if(r&&!a.empty()?l>0:l>=0){var c=me(s.from(),a.from()),u=fe(s.to(),a.to()),d=s.empty()?a.from()==a.head:s.from()==s.head;n>=i&&--n,t.splice(--i,2,new Qs(d?u:c,d?c:u))}}return new zs(t,n)}function Wr(e,t){return new zs([new Qs(e,t||e)],0)}function Xr(e){return e.text?de(e.from.line+e.text.length-1,w(e.text).length+(1==e.text.length?e.from.ch:0)):e.to}function _r(e,t){if(pe(e,t.from)<0)return e;if(pe(e,t.to)<=0)return Xr(t);var n=e.line+t.text.length-(t.to.line-t.from.line)-1,r=e.ch;return e.line==t.to.line&&(r+=Xr(t).ch-t.to.ch),de(n,r)}function qr(e,t){for(var n=[],r=0;r<e.sel.ranges.length;r++){var o=e.sel.ranges[r];n.push(new Qs(_r(o.anchor,t),_r(o.head,t)))}return Gr(e.cm,n,e.sel.primIndex)}function Jr(e,t,n){return e.line==t.line?de(n.line,e.ch-t.ch+n.ch):de(n.line+(e.line-t.line),e.ch)}function Kr(e,t,n){for(var r=[],o=de(e.first,0),i=o,a=0;a<t.length;a++){var s=t[a],l=Jr(s.from,o,i),c=Jr(Xr(s),o,i);if(o=s.to,i=c,\"around\"==n){var u=e.sel.ranges[a],d=pe(u.head,u.anchor)<0;r[a]=new Qs(d?c:l,d?l:c)}else r[a]=new Qs(l,l)}return new zs(r,e.sel.primIndex)}function Zr(e){e.doc.mode=Z(e.options,e.doc.modeOption),$r(e)}function $r(e){e.doc.iter(function(e){e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null)}),e.doc.modeFrontier=e.doc.highlightFrontier=e.doc.first,Cr(e,100),e.state.modeGen++,e.curOp&&Ln(e)}function eo(e,t){return 0==t.from.ch&&0==t.to.ch&&\"\"==w(t.text)&&(!e.cm||e.cm.options.wholeLineUpdateBefore)}function to(e,t,n,r){function o(e){return n?n[e]:null}function i(e,n,o){ct(e,n,o,r),Tt(e,\"change\",e,t)}function a(e,t){for(var n=[],i=e;t>i;++i)n.push(new xs(c[i],o(i),r));return n}var s=t.from,l=t.to,c=t.text,u=re(e,s.line),d=re(e,l.line),p=w(c),h=o(c.length-1),g=l.line-s.line;if(t.full)e.insert(0,a(0,c.length)),e.remove(c.length,e.size-c.length);else if(eo(e,t)){var f=a(0,c.length-1);i(d,d.text,h),g&&e.remove(s.line,g),f.length&&e.insert(s.line,f)}else if(u==d)if(1==c.length)i(u,u.text.slice(0,s.ch)+p+u.text.slice(l.ch),h);else{var m=a(1,c.length-1);m.push(new xs(p+u.text.slice(l.ch),h,r)),i(u,u.text.slice(0,s.ch)+c[0],o(0)),e.insert(s.line+1,m)}else if(1==c.length)i(u,u.text.slice(0,s.ch)+c[0]+d.text.slice(l.ch),o(0)),e.remove(s.line+1,g);else{i(u,u.text.slice(0,s.ch)+c[0],o(0)),i(d,p+d.text.slice(l.ch),h);var A=a(1,c.length-1);g>1&&e.remove(s.line+1,g-1),e.insert(s.line+1,A)}Tt(e,\"change\",e,t)}function no(e,t,n){function r(e,o,i){if(e.linked)for(var a=0;a<e.linked.length;++a){var s=e.linked[a];if(s.doc!=o){var l=i&&s.sharedHist;(!n||l)&&(t(s.doc,l),r(s.doc,e,l))}}}r(e,null,!0)}function ro(e,t){if(t.cm)throw new Error(\"This document is already in use.\");e.doc=t,t.cm=e,En(e),Zr(e),oo(e),e.options.direction=t.direction,e.options.lineWrapping||lt(e),e.options.mode=t.modeOption,Ln(e)}function oo(e){(\"rtl\"==e.doc.direction?s:Wa)(e.display.lineDiv,\"CodeMirror-rtl\")}function io(e){br(e,function(){oo(e),Ln(e)})}function ao(e){this.done=[],this.undone=[],this.undoDepth=e?e.undoDepth:1/0,this.lastModTime=this.lastSelTime=0,this.lastOp=this.lastSelOp=null,this.lastOrigin=this.lastSelOrigin=null,this.generation=this.maxGeneration=e?e.maxGeneration:1}function so(e,t){var n={from:ge(t.from),to:Xr(t),text:oe(e,t.from,t.to)};return fo(e,n,t.from.line,t.to.line+1),no(e,function(e){return fo(e,n,t.from.line,t.to.line+1)},!0),n}function lo(e){for(;e.length;){var t=w(e);if(!t.ranges)break;e.pop()}}function co(e,t){return t?(lo(e.done),w(e.done)):e.done.length&&!w(e.done).ranges?w(e.done):e.done.length>1&&!e.done[e.done.length-2].ranges?(e.done.pop(),w(e.done)):void 0}function uo(e,t,n,r){var o=e.history;o.undone.length=0;var i,a,s=+new Date;if((o.lastOp==r||o.lastOrigin==t.origin&&t.origin&&(\"+\"==t.origin.charAt(0)&&o.lastModTime>s-(e.cm?e.cm.options.historyEventDelay:500)||\"*\"==t.origin.charAt(0)))&&(i=co(o,o.lastOp==r)))a=w(i.changes),0==pe(t.from,t.to)&&0==pe(t.from,a.to)?a.to=Xr(t):i.changes.push(so(e,t));else{var l=w(o.done);for(l&&l.ranges||go(e.sel,o.done),i={changes:[so(e,t)],generation:o.generation},o.done.push(i);o.done.length>o.undoDepth;)o.done.shift(),o.done[0].ranges||o.done.shift()}o.done.push(n),o.generation=++o.maxGeneration,o.lastModTime=o.lastSelTime=s,o.lastOp=o.lastSelOp=r,o.lastOrigin=o.lastSelOrigin=t.origin,a||B(e,\"historyAdded\")}function po(e,t,n,r){var o=t.charAt(0);return\"*\"==o||\"+\"==o&&n.ranges.length==r.ranges.length&&n.somethingSelected()==r.somethingSelected()&&new Date-e.history.lastSelTime<=(e.cm?e.cm.options.historyEventDelay:500)}function ho(e,t,n,r){var o=e.history,i=r&&r.origin;n==o.lastSelOp||i&&o.lastSelOrigin==i&&(o.lastModTime==o.lastSelTime&&o.lastOrigin==i||po(e,i,w(o.done),t))?o.done[o.done.length-1]=t:go(t,o.done),o.lastSelTime=+new Date,o.lastSelOrigin=i,o.lastSelOp=n,r&&r.clearRedo!==!1&&lo(o.undone)}function go(e,t){var n=w(t);n&&n.ranges&&n.equals(e)||t.push(e)}function fo(e,t,n,r){var o=t[\"spans_\"+e.id],i=0;e.iter(Math.max(e.first,n),Math.min(e.first+e.size,r),function(n){n.markedSpans&&((o||(o=t[\"spans_\"+e.id]={}))[i]=n.markedSpans),++i})}function mo(e){if(!e)return null;for(var t,n=0;n<e.length;++n)e[n].marker.explicitlyCleared?t||(t=e.slice(0,n)):t&&t.push(e[n]);return t?t.length?t:null:e}function Ao(e,t){var n=t[\"spans_\"+e.id];if(!n)return null;for(var r=[],o=0;o<t.text.length;++o)r.push(mo(n[o]));return r}function Mo(e,t){var n=Ao(e,t),r=He(e,t);if(!n)return r;if(!r)return n;for(var o=0;o<n.length;++o){var i=n[o],a=r[o];if(i&&a)e:for(var s=0;s<a.length;++s){for(var l=a[s],c=0;c<i.length;++c)if(i[c].marker==l.marker)continue e;i.push(l)}else a&&(n[o]=a)}return n}function wo(e,t,n){for(var r=[],o=0;o<e.length;++o){var i=e[o];if(i.ranges)r.push(n?zs.prototype.deepCopy.call(i):i);else{var a=i.changes,s=[];r.push({changes:s});for(var l=0;l<a.length;++l){var c=a[l],u=void 0;if(s.push({from:c.from,to:c.to,text:c.text}),t)for(var d in c)(u=d.match(/^spans_(\\d+)$/))&&m(t,Number(u[1]))>-1&&(w(s)[d]=c[d],delete c[d])}}}return r}function vo(e,t,n,r){if(r){var o=e.anchor;if(n){var i=pe(t,o)<0;i!=pe(n,o)<0?(o=t,t=n):i!=pe(t,n)<0&&(t=n)}return new Qs(o,t)}return new Qs(n||t,t)}function bo(e,t,n,r,o){null==o&&(o=e.cm&&(e.cm.display.shift||e.extend)),Io(e,new zs([vo(e.sel.primary(),t,n,o)],0),r)}function yo(e,t,n){for(var r=[],o=e.cm&&(e.cm.display.shift||e.extend),i=0;i<e.sel.ranges.length;i++)r[i]=vo(e.sel.ranges[i],t[i],null,o);var a=Gr(e.cm,r,e.sel.primIndex);Io(e,a,n)}function xo(e,t,n,r){var o=e.sel.ranges.slice(0);o[t]=n,Io(e,Gr(e.cm,o,e.sel.primIndex),r)}function To(e,t,n,r){Io(e,Wr(t,n),r)}function Co(e,t,n){var r={ranges:t.ranges,update:function(t){this.ranges=[];for(var n=0;n<t.length;n++)this.ranges[n]=new Qs(Me(e,t[n].anchor),Me(e,t[n].head))},origin:n&&n.origin};return B(e,\"beforeSelectionChange\",e,r),e.cm&&B(e.cm,\"beforeSelectionChange\",e.cm,r),r.ranges!=t.ranges?Gr(e.cm,r.ranges,r.ranges.length-1):t}function No(e,t,n){var r=e.history.done,o=w(r);o&&o.ranges?(r[r.length-1]=t,Eo(e,t,n)):Io(e,t,n)}function Io(e,t,n){Eo(e,t,n),ho(e,e.sel,e.cm?e.cm.curOp.id:NaN,n)}function Eo(e,t,n){(Q(e,\"beforeSelectionChange\")||e.cm&&Q(e.cm,\"beforeSelectionChange\"))&&(t=Co(e,t,n));var r=n&&n.bias||(pe(t.primary().head,e.sel.primary().head)<0?-1:1);Do(e,Lo(e,t,r,!0)),n&&n.scroll===!1||!e.cm||\"nocursor\"==e.cm.getOption(\"readOnly\")||tr(e.cm)}function Do(e,t){t.equals(e.sel)||(e.sel=t,e.cm&&(e.cm.curOp.updateInput=1,e.cm.curOp.selectionChanged=!0,z(e.cm)),Tt(e,\"cursorActivity\",e))}function So(e){Do(e,Lo(e,e.sel,null,!1))}function Lo(e,t,n,r){for(var o,i=0;i<t.ranges.length;i++){var a=t.ranges[i],s=t.ranges.length==e.sel.ranges.length&&e.sel.ranges[i],l=jo(e,a.anchor,s&&s.anchor,n,r),c=a.head==a.anchor?l:jo(e,a.head,s&&s.head,n,r);(o||l!=a.anchor||c!=a.head)&&(o||(o=t.ranges.slice(0,i)),o[i]=new Qs(l,c))}return o?Gr(e.cm,o,t.primIndex):t}function ko(e,t,n,r,o){var i=re(e,t.line);if(i.markedSpans)for(var a=0;a<i.markedSpans.length;++a){var s=i.markedSpans[a],l=s.marker,c=\"selectLeft\"in l?!l.selectLeft:l.inclusiveLeft,u=\"selectRight\"in l?!l.selectRight:l.inclusiveRight;if((null==s.from||(c?s.from<=t.ch:s.from<t.ch))&&(null==s.to||(u?s.to>=t.ch:s.to>t.ch))){if(o&&(B(l,\"beforeCursorEnter\"),l.explicitlyCleared)){if(i.markedSpans){--a;continue}break}if(!l.atomic)continue;if(n){var d=l.find(0>r?1:-1),p=void 0;if((0>r?u:c)&&(d=Uo(e,d,-r,d&&d.line==t.line?i:null)),d&&d.line==t.line&&(p=pe(d,n))&&(0>r?0>p:p>0))return ko(e,d,t,r,o)}var h=l.find(0>r?-1:1);return(0>r?c:u)&&(h=Uo(e,h,r,h.line==t.line?i:null)),h?ko(e,h,t,r,o):null}}return t}function jo(e,t,n,r,o){var i=r||1,a=ko(e,t,n,i,o)||!o&&ko(e,t,n,i,!0)||ko(e,t,n,-i,o)||!o&&ko(e,t,n,-i,!0);return a?a:(e.cantEdit=!0,de(e.first,0))}function Uo(e,t,n,r){return 0>n&&0==t.ch?t.line>e.first?Me(e,de(t.line-1)):null:n>0&&t.ch==(r||re(e,t.line)).text.length?t.line<e.first+e.size-1?de(t.line+1,0):null:new de(t.line,t.ch+n)}function Bo(e){e.setSelection(de(e.firstLine(),0),de(e.lastLine()),$a)}function Ro(e,t,n){var r={canceled:!1,from:t.from,to:t.to,text:t.text,origin:t.origin,cancel:function(){return r.canceled=!0}};return n&&(r.update=function(t,n,o,i){t&&(r.from=Me(e,t)),n&&(r.to=Me(e,n)),o&&(r.text=o),void 0!==i&&(r.origin=i)}),B(e,\"beforeChange\",e,r),e.cm&&B(e.cm,\"beforeChange\",e.cm,r),r.canceled?(e.cm&&(e.cm.curOp.updateInput=2),null):{from:r.from,to:r.to,text:r.text,origin:r.origin}}function zo(e,t,n){if(e.cm){if(!e.cm.curOp)return yr(e.cm,zo)(e,t,n);if(e.cm.state.suppressEdits)return}if(!(Q(e,\"beforeChange\")||e.cm&&Q(e.cm,\"beforeChange\"))||(t=Ro(e,t,!0))){var r=bs&&!n&&Ve(e,t.from,t.to);if(r)for(var o=r.length-1;o>=0;--o)Qo(e,{from:r[o].from,to:r[o].to,text:o?[\"\"]:t.text,origin:t.origin});else Qo(e,t)}}function Qo(e,t){if(1!=t.text.length||\"\"!=t.text[0]||0!=pe(t.from,t.to)){var n=qr(e,t);uo(e,t,n,e.cm?e.cm.curOp.id:NaN),Fo(e,t,n,He(e,t));var r=[];no(e,function(e,n){n||-1!=m(r,e.history)||(Wo(e.history,t),r.push(e.history)),Fo(e,t,null,He(e,t))})}}function Oo(e,t,n){var r=e.cm&&e.cm.state.suppressEdits;if(!r||n){for(var o,i=e.history,a=e.sel,s=\"undo\"==t?i.done:i.undone,l=\"undo\"==t?i.undone:i.done,c=0;c<s.length&&(o=s[c],n?!o.ranges||o.equals(e.sel):o.ranges);c++);if(c!=s.length){for(i.lastOrigin=i.lastSelOrigin=null;;){if(o=s.pop(),!o.ranges){if(r)return void s.push(o);break}if(go(o,l),n&&!o.equals(e.sel))return void Io(e,o,{clearRedo:!1});a=o}var u=[];go(a,l),l.push({changes:u,generation:i.generation}),i.generation=o.generation||++i.maxGeneration;for(var d=Q(e,\"beforeChange\")||e.cm&&Q(e.cm,\"beforeChange\"),p=function(n){var r=o.changes[n];if(r.origin=t,d&&!Ro(e,r,!1))return s.length=0,{};u.push(so(e,r));var i=n?qr(e,r):w(s);Fo(e,r,i,Mo(e,r)),!n&&e.cm&&e.cm.scrollIntoView({from:r.from,to:Xr(r)});var a=[];no(e,function(e,t){t||-1!=m(a,e.history)||(Wo(e.history,r),a.push(e.history)),Fo(e,r,null,Mo(e,r))})},h=o.changes.length-1;h>=0;--h){var g=p(h);if(g)return g.v}}}}function Ho(e,t){if(0!=t&&(e.first+=t,e.sel=new zs(v(e.sel.ranges,function(e){return new Qs(de(e.anchor.line+t,e.anchor.ch),de(e.head.line+t,e.head.ch))}),e.sel.primIndex),e.cm)){Ln(e.cm,e.first,e.first-t,t);for(var n=e.cm.display,r=n.viewFrom;r<n.viewTo;r++)kn(e.cm,r,\"gutter\")}}function Fo(e,t,n,r){if(e.cm&&!e.cm.curOp)return yr(e.cm,Fo)(e,t,n,r);if(t.to.line<e.first)return void Ho(e,t.text.length-1-(t.to.line-t.from.line));if(!(t.from.line>e.lastLine())){if(t.from.line<e.first){var o=t.text.length-1-(e.first-t.from.line);Ho(e,o),t={from:de(e.first,0),to:de(t.to.line+o,t.to.ch),text:[w(t.text)],origin:t.origin}}var i=e.lastLine();t.to.line>i&&(t={from:t.from,to:de(i,re(e,i).text.length),text:[t.text[0]],origin:t.origin}),t.removed=oe(e,t.from,t.to),n||(n=qr(e,t)),e.cm?Vo(e.cm,t,r):to(e,t,r),Eo(e,n,$a),e.cantEdit&&jo(e,de(e.firstLine(),0))&&(e.cantEdit=!1)}}function Vo(e,t,n){var r=e.doc,o=e.display,i=t.from,a=t.to,s=!1,l=i.line;e.options.lineWrapping||(l=se($e(re(r,i.line))),r.iter(l,a.line+1,function(e){return e==o.maxLine?(s=!0,!0):void 0})),r.sel.contains(t.from,t.to)>-1&&z(e),to(r,t,n,In(e)),e.options.lineWrapping||(r.iter(l,i.line+t.text.length,function(e){var t=st(e);t>o.maxLineLength&&(o.maxLine=e,o.maxLineLength=t,o.maxLineChanged=!0,s=!1)}),s&&(e.curOp.updateMaxLine=!0)),Le(r,i.line),Cr(e,400);var c=t.text.length-(a.line-i.line)-1;t.full?Ln(e):i.line!=a.line||1!=t.text.length||eo(e.doc,t)?Ln(e,i.line,a.line+1,c):kn(e,i.line,\"text\");var u=Q(e,\"changes\"),d=Q(e,\"change\");if(d||u){var p={from:i,to:a,text:t.text,removed:t.removed,origin:t.origin};d&&Tt(e,\"change\",e,p),u&&(e.curOp.changeObjs||(e.curOp.changeObjs=[])).push(p)}e.display.selForContextMenu=null}function Po(e,t,n,r,o){var i;r||(r=n),pe(r,n)<0&&(i=[r,n],n=i[0],r=i[1]),\"string\"==typeof t&&(t=e.splitLines(t)),zo(e,{from:n,to:r,text:t,origin:o})}function Yo(e,t,n,r){n<e.line?e.line+=r:t<e.line&&(e.line=t,e.ch=0)}function Go(e,t,n,r){for(var o=0;o<e.length;++o){var i=e[o],a=!0;if(i.ranges){i.copied||(i=e[o]=i.deepCopy(),i.copied=!0);for(var s=0;s<i.ranges.length;s++)Yo(i.ranges[s].anchor,t,n,r),Yo(i.ranges[s].head,t,n,r)}else{for(var l=0;l<i.changes.length;++l){var c=i.changes[l];if(n<c.from.line)c.from=de(c.from.line+r,c.from.ch),c.to=de(c.to.line+r,c.to.ch);else if(t<=c.to.line){a=!1;break}}a||(e.splice(0,o+1),o=0)}}}function Wo(e,t){var n=t.from.line,r=t.to.line,o=t.text.length-(r-n)-1;Go(e.done,n,r,o),Go(e.undone,n,r,o)}function Xo(e,t,n,r){var o=t,i=t;return\"number\"==typeof t?i=re(e,Ae(e,t)):o=se(t),null==o?null:(r(i,o)&&e.cm&&kn(e.cm,o,n),i)}function _o(e){this.lines=e,this.parent=null;for(var t=0,n=0;n<e.length;++n)e[n].parent=this,t+=e[n].height;this.height=t}function qo(e){this.children=e;for(var t=0,n=0,r=0;r<e.length;++r){var o=e[r];t+=o.chunkSize(),n+=o.height,o.parent=this}this.size=t,this.height=n,this.parent=null}function Jo(e,t,n){at(t)<(e.curOp&&e.curOp.scrollTop||e.doc.scrollTop)&&er(e,n)}function Ko(e,t,n,r){var o=new Os(e,n,r),i=e.cm;return i&&o.noHScroll&&(i.display.alignWidgets=!0),Xo(e,t,\"widget\",function(t){var n=t.widgets||(t.widgets=[]);if(null==o.insertAt?n.push(o):n.splice(Math.min(n.length,Math.max(0,o.insertAt)),0,o),o.line=t,i&&!ot(e,t)){var r=at(t)<e.scrollTop;ae(t,t.height+Qt(o)),r&&er(i,o.height),i.curOp.forceUpdate=!0}return!0}),i&&Tt(i,\"lineWidgetAdded\",i,o,\"number\"==typeof t?t:se(t)),o}function Zo(e,t,n,r,i){if(r&&r.shared)return $o(e,t,n,r,i);if(e.cm&&!e.cm.curOp)return yr(e.cm,Zo)(e,t,n,r,i);var a=new Fs(e,i),s=pe(t,n);if(r&&g(r,a,!1),s>0||0==s&&a.clearWhenEmpty!==!1)return a;if(a.replacedWith&&(a.collapsed=!0,a.widgetNode=o(\"span\",[a.replacedWith],\"CodeMirror-widget\"),r.handleMouseEvents||a.widgetNode.setAttribute(\"cm-ignore-events\",\"true\"),r.insertLeft&&(a.widgetNode.insertLeft=!0)),a.collapsed){if(Ze(e,t.line,t,n,a)||t.line!=n.line&&Ze(e,n.line,t,n,a))throw new Error(\"Inserting collapsed marker partially overlapping an existing one\");je()}a.addToHistory&&uo(e,{from:t,to:n,origin:\"markText\"},e.sel,NaN);var l,c=t.line,u=e.cm;if(e.iter(c,n.line+1,function(r){u&&a.collapsed&&!u.options.lineWrapping&&$e(r)==u.display.maxLine&&(l=!0),a.collapsed&&c!=t.line&&ae(r,0),ze(r,new Ue(a,c==t.line?t.ch:null,c==n.line?n.ch:null),e.cm&&e.cm.curOp),++c}),a.collapsed&&e.iter(t.line,n.line+1,function(t){ot(e,t)&&ae(t,0)}),a.clearOnEnter&&ls(a,\"beforeCursorEnter\",function(){return a.clear()}),a.readOnly&&(ke(),(e.history.done.length||e.history.undone.length)&&e.clearHistory()),a.collapsed&&(a.id=++Hs,a.atomic=!0),u){if(l&&(u.curOp.updateMaxLine=!0),a.collapsed)Ln(u,t.line,n.line+1);else if(a.className||a.startStyle||a.endStyle||a.css||a.attributes||a.title)for(var d=t.line;d<=n.line;d++)kn(u,d,\"text\");a.atomic&&So(u.doc),Tt(u,\"markerAdded\",u,a)}return a}function $o(e,t,n,r,o){r=g(r),r.shared=!1;var i=[Zo(e,t,n,r,o)],a=i[0],s=r.widgetNode;return no(e,function(e){s&&(r.widgetNode=s.cloneNode(!0)),i.push(Zo(e,Me(e,t),Me(e,n),r,o));for(var l=0;l<e.linked.length;++l)if(e.linked[l].isParent)return;a=w(i)}),new Vs(i,a)}function ei(e){return e.findMarks(de(e.first,0),e.clipPos(de(e.lastLine())),function(e){return e.parent})}function ti(e,t){for(var n=0;n<t.length;n++){var r=t[n],o=r.find(),i=e.clipPos(o.from),a=e.clipPos(o.to);if(pe(i,a)){var s=Zo(e,i,a,r.primary,r.primary.type);r.markers.push(s),s.parent=r}}}function ni(e){for(var t=function(t){var n=e[t],r=[n.primary.doc];no(n.primary.doc,function(e){return r.push(e)});for(var o=0;o<n.markers.length;o++){var i=n.markers[o];-1==m(r,i.doc)&&(i.parent=null,n.markers.splice(o--,1))}},n=0;n<e.length;n++)t(n)}function ri(e){var t=this;if(ai(t),!R(t,e)&&!Ot(t.display,e)){H(e),Na&&(Gs=+new Date);var n=Dn(t,e,!0),r=e.dataTransfer.files;if(n&&!t.isReadOnly())if(r&&r.length&&window.FileReader&&window.File)for(var o=r.length,i=Array(o),a=0,s=function(){++a==o&&yr(t,function(){n=Me(t.doc,n);var e={from:n,to:n,text:t.doc.splitLines(i.filter(function(e){return null!=e}).join(t.doc.lineSeparator())),origin:\"paste\"};zo(t.doc,e),No(t.doc,Wr(Me(t.doc,n),Me(t.doc,Xr(e))))})()},l=function(e,n){if(t.options.allowDropFileTypes&&-1==m(t.options.allowDropFileTypes,e.type))return void s();\nvar r=new FileReader;r.onerror=function(){return s()},r.onload=function(){var e=r.result;return/[\\x00-\\x08\\x0e-\\x1f]{2}/.test(e)?void s():(i[n]=e,void s())},r.readAsText(e)},c=0;c<r.length;c++)l(r[c],c);else{if(t.state.draggingText&&t.doc.sel.contains(n)>-1)return t.state.draggingText(e),void setTimeout(function(){return t.display.input.focus()},20);try{var u=e.dataTransfer.getData(\"Text\");if(u){var d;if(t.state.draggingText&&!t.state.draggingText.copy&&(d=t.listSelections()),Eo(t.doc,Wr(n,n)),d)for(var p=0;p<d.length;++p)Po(t.doc,\"\",d[p].anchor,d[p].head,\"drag\");t.replaceSelection(u,\"around\",\"paste\"),t.display.input.focus()}}catch(h){}}}}function oi(e,t){if(Na&&(!e.state.draggingText||+new Date-Gs<100))return void P(t);if(!R(e,t)&&!Ot(e.display,t)&&(t.dataTransfer.setData(\"Text\",e.getSelection()),t.dataTransfer.effectAllowed=\"copyMove\",t.dataTransfer.setDragImage&&!ja)){var n=r(\"img\",null,null,\"position: fixed; left: 0; top: 0;\");n.src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\",ka&&(n.width=n.height=1,e.display.wrapper.appendChild(n),n._top=n.offsetTop),t.dataTransfer.setDragImage(n,0,0),ka&&n.parentNode.removeChild(n)}}function ii(e,t){var o=Dn(e,t);if(o){var i=document.createDocumentFragment();On(e,o,i),e.display.dragCursor||(e.display.dragCursor=r(\"div\",null,\"CodeMirror-cursors CodeMirror-dragcursors\"),e.display.lineSpace.insertBefore(e.display.dragCursor,e.display.cursorDiv)),n(e.display.dragCursor,i)}}function ai(e){e.display.dragCursor&&(e.display.lineSpace.removeChild(e.display.dragCursor),e.display.dragCursor=null)}function si(e){if(document.getElementsByClassName){for(var t=document.getElementsByClassName(\"CodeMirror\"),n=[],r=0;r<t.length;r++){var o=t[r].CodeMirror;o&&n.push(o)}n.length&&n[0].operation(function(){for(var t=0;t<n.length;t++)e(n[t])})}}function li(){Ws||(ci(),Ws=!0)}function ci(){var e;ls(window,\"resize\",function(){null==e&&(e=setTimeout(function(){e=null,si(ui)},100))}),ls(window,\"blur\",function(){return si(Wn)})}function ui(e){var t=e.display;t.cachedCharWidth=t.cachedTextHeight=t.cachedPaddingH=null,t.scrollbarsClipped=!1,e.setSize()}function di(e){var t=e.split(/-(?!$)/);e=t[t.length-1];for(var n,r,o,i,a=0;a<t.length-1;a++){var s=t[a];if(/^(cmd|meta|m)$/i.test(s))i=!0;else if(/^a(lt)?$/i.test(s))n=!0;else if(/^(c|ctrl|control)$/i.test(s))r=!0;else{if(!/^s(hift)?$/i.test(s))throw new Error(\"Unrecognized modifier name: \"+s);o=!0}}return n&&(e=\"Alt-\"+e),r&&(e=\"Ctrl-\"+e),i&&(e=\"Cmd-\"+e),o&&(e=\"Shift-\"+e),e}function pi(e){var t={};for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];if(/^(name|fallthrough|(de|at)tach)$/.test(n))continue;if(\"...\"==r){delete e[n];continue}for(var o=v(n.split(\" \"),di),i=0;i<o.length;i++){var a=void 0,s=void 0;i==o.length-1?(s=o.join(\" \"),a=r):(s=o.slice(0,i+1).join(\" \"),a=\"...\");var l=t[s];if(l){if(l!=a)throw new Error(\"Inconsistent bindings for \"+s)}else t[s]=a}delete e[n]}for(var c in t)e[c]=t[c];return e}function hi(e,t,n,r){t=Ai(t);var o=t.call?t.call(e,r):t[e];if(o===!1)return\"nothing\";if(\"...\"===o)return\"multi\";if(null!=o&&n(o))return\"handled\";if(t.fallthrough){if(\"[object Array]\"!=Object.prototype.toString.call(t.fallthrough))return hi(e,t.fallthrough,n,r);for(var i=0;i<t.fallthrough.length;i++){var a=hi(e,t.fallthrough[i],n,r);if(a)return a}}}function gi(e){var t=\"string\"==typeof e?e:Xs[e.keyCode];return\"Ctrl\"==t||\"Alt\"==t||\"Shift\"==t||\"Mod\"==t}function fi(e,t,n){var r=e;return t.altKey&&\"Alt\"!=r&&(e=\"Alt-\"+e),(Ya?t.metaKey:t.ctrlKey)&&\"Ctrl\"!=r&&(e=\"Ctrl-\"+e),(Ya?t.ctrlKey:t.metaKey)&&\"Mod\"!=r&&(e=\"Cmd-\"+e),!n&&t.shiftKey&&\"Shift\"!=r&&(e=\"Shift-\"+e),e}function mi(e,t){if(ka&&34==e.keyCode&&e[\"char\"])return!1;var n=Xs[e.keyCode];return null==n||e.altGraphKey?!1:(3==e.keyCode&&e.code&&(n=e.code),fi(n,e,t))}function Ai(e){return\"string\"==typeof e?Ks[e]:e}function Mi(e,t){for(var n=e.doc.sel.ranges,r=[],o=0;o<n.length;o++){for(var i=t(n[o]);r.length&&pe(i.from,w(r).to)<=0;){var a=r.pop();if(pe(a.from,i.from)<0){i.from=a.from;break}}r.push(i)}br(e,function(){for(var t=r.length-1;t>=0;t--)Po(e.doc,\"\",r[t].from,r[t].to,\"+delete\");tr(e)})}function wi(e,t,n){var r=E(e.text,t+n,n);return 0>r||r>e.text.length?null:r}function vi(e,t,n){var r=wi(e,t.ch,n);return null==r?null:new de(t.line,r,0>n?\"after\":\"before\")}function bi(e,t,n,r,o){if(e){\"rtl\"==t.doc.direction&&(o=-o);var i=k(n,t.doc.direction);if(i){var a,s=0>o?w(i):i[0],l=0>o==(1==s.level),c=l?\"after\":\"before\";if(s.level>0||\"rtl\"==t.doc.direction){var u=Kt(t,n);a=0>o?n.text.length-1:0;var d=Zt(t,u,a).top;a=D(function(e){return Zt(t,u,e).top==d},0>o==(1==s.level)?s.from:s.to-1,a),\"before\"==c&&(a=wi(n,a,1))}else a=0>o?s.to:s.from;return new de(r,a,c)}}return new de(r,0>o?n.text.length:0,0>o?\"before\":\"after\")}function yi(e,t,n,r){var o=k(t,e.doc.direction);if(!o)return vi(t,n,r);n.ch>=t.text.length?(n.ch=t.text.length,n.sticky=\"before\"):n.ch<=0&&(n.ch=0,n.sticky=\"after\");var i=L(o,n.ch,n.sticky),a=o[i];if(\"ltr\"==e.doc.direction&&a.level%2==0&&(r>0?a.to>n.ch:a.from<n.ch))return vi(t,n,r);var s,l=function(e,n){return wi(t,e instanceof de?e.ch:e,n)},c=function(n){return e.options.lineWrapping?(s=s||Kt(e,t),Mn(e,t,s,n)):{begin:0,end:t.text.length}},u=c(\"before\"==n.sticky?l(n,-1):n.ch);if(\"rtl\"==e.doc.direction||1==a.level){var d=1==a.level==0>r,p=l(n,d?1:-1);if(null!=p&&(d?p<=a.to&&p<=u.end:p>=a.from&&p>=u.begin)){var h=d?\"before\":\"after\";return new de(n.line,p,h)}}var g=function(e,t,r){for(var i=function(e,t){return t?new de(n.line,l(e,1),\"before\"):new de(n.line,e,\"after\")};e>=0&&e<o.length;e+=t){var a=o[e],s=t>0==(1!=a.level),c=s?r.begin:l(r.end,-1);if(a.from<=c&&c<a.to)return i(c,s);if(c=s?a.from:l(a.to,-1),r.begin<=c&&c<r.end)return i(c,s)}},f=g(i+r,r,u);if(f)return f;var m=r>0?u.end:l(u.begin,-1);return null==m||r>0&&m==t.text.length||!(f=g(r>0?0:o.length-1,r,c(m)))?null:f}function xi(e,t){var n=re(e.doc,t),r=$e(n);return r!=n&&(t=se(r)),bi(!0,e,r,t,1)}function Ti(e,t){var n=re(e.doc,t),r=et(n);return r!=n&&(t=se(r)),bi(!0,e,n,t,-1)}function Ci(e,t){var n=xi(e,t.line),r=re(e.doc,n.line),o=k(r,e.doc.direction);if(!o||0==o[0].level){var i=Math.max(n.ch,r.text.search(/\\S/)),a=t.line==n.line&&t.ch<=i&&t.ch;return de(n.line,a?0:i,n.sticky)}return n}function Ni(e,t,n){if(\"string\"==typeof t&&(t=Zs[t],!t))return!1;e.display.input.ensurePolled();var r=e.display.shift,o=!1;try{e.isReadOnly()&&(e.state.suppressEdits=!0),n&&(e.display.shift=!1),o=t(e)!=Za}finally{e.display.shift=r,e.state.suppressEdits=!1}return o}function Ii(e,t,n){for(var r=0;r<e.state.keyMaps.length;r++){var o=hi(t,e.state.keyMaps[r],n,e);if(o)return o}return e.options.extraKeys&&hi(t,e.options.extraKeys,n,e)||hi(t,e.options.keyMap,n,e)}function Ei(e,t,n,r){var o=e.state.keySeq;if(o){if(gi(t))return\"handled\";if(/\\'$/.test(t)?e.state.keySeq=null:$s.set(50,function(){e.state.keySeq==o&&(e.state.keySeq=null,e.display.input.reset())}),Di(e,o+\" \"+t,n,r))return!0}return Di(e,t,n,r)}function Di(e,t,n,r){var o=Ii(e,t,r);return\"multi\"==o&&(e.state.keySeq=t),\"handled\"==o&&Tt(e,\"keyHandled\",e,t,n),(\"handled\"==o||\"multi\"==o)&&(H(n),Vn(e)),!!o}function Si(e,t){var n=mi(t,!0);return n?t.shiftKey&&!e.state.keySeq?Ei(e,\"Shift-\"+n,t,function(t){return Ni(e,t,!0)})||Ei(e,n,t,function(t){return(\"string\"==typeof t?/^go[A-Z]/.test(t):t.motion)?Ni(e,t):void 0}):Ei(e,n,t,function(t){return Ni(e,t)}):!1}function Li(e,t,n){return Ei(e,\"'\"+n+\"'\",t,function(t){return Ni(e,t,!0)})}function ki(e){var t=this;if(!(e.target&&e.target!=t.display.input.getField()||(t.curOp.focus=a(u(t)),R(t,e)))){Na&&11>Ia&&27==e.keyCode&&(e.returnValue=!1);var n=e.keyCode;t.display.shift=16==n||e.shiftKey;var r=Si(t,e);ka&&(el=r?n:null,!r&&88==n&&!ps&&(Oa?e.metaKey:e.ctrlKey)&&t.replaceSelection(\"\",null,\"cut\")),ya&&!Oa&&!r&&46==n&&e.shiftKey&&!e.ctrlKey&&document.execCommand&&document.execCommand(\"cut\"),18!=n||/\\bCodeMirror-crosshair\\b/.test(t.display.lineDiv.className)||ji(t)}}function ji(e){function t(e){18!=e.keyCode&&e.altKey||(Wa(n,\"CodeMirror-crosshair\"),U(document,\"keyup\",t),U(document,\"mouseover\",t))}var n=e.display.lineDiv;s(n,\"CodeMirror-crosshair\"),ls(document,\"keyup\",t),ls(document,\"mouseover\",t)}function Ui(e){16==e.keyCode&&(this.doc.sel.shift=!1),R(this,e)}function Bi(e){var t=this;if(!(e.target&&e.target!=t.display.input.getField()||Ot(t.display,e)||R(t,e)||e.ctrlKey&&!e.altKey||Oa&&e.metaKey)){var n=e.keyCode,r=e.charCode;if(ka&&n==el)return el=null,void H(e);if(!ka||e.which&&!(e.which<10)||!Si(t,e)){var o=String.fromCharCode(null==r?n:r);\"\\b\"!=o&&(Li(t,e,o)||t.display.input.onKeyPress(e))}}}function Ri(e,t){var n=+new Date;return ol&&ol.compare(n,e,t)?(rl=ol=null,\"triple\"):rl&&rl.compare(n,e,t)?(ol=new nl(n,e,t),rl=null,\"double\"):(rl=new nl(n,e,t),ol=null,\"single\")}function zi(e){var t=this,n=t.display;if(!(R(t,e)||n.activeTouch&&n.input.supportsTouch())){if(n.input.ensurePolled(),n.shift=e.shiftKey,Ot(n,e))return void(Ea||(n.scroller.draggable=!1,setTimeout(function(){return n.scroller.draggable=!0},100)));if(!Wi(t,e)){var r=Dn(t,e),o=G(e),i=r?Ri(r,o):\"single\";p(t).focus(),1==o&&t.state.selectingText&&t.state.selectingText(e),r&&Qi(t,o,r,i,e)||(1==o?r?Hi(t,r,i,e):Y(e)==n.scroller&&H(e):2==o?(r&&bo(t.doc,r),setTimeout(function(){return n.input.focus()},20)):3==o&&(Ga?t.display.input.onContextMenu(e):Yn(t)))}}}function Qi(e,t,n,r,o){var i=\"Click\";return\"double\"==r?i=\"Double\"+i:\"triple\"==r&&(i=\"Triple\"+i),i=(1==t?\"Left\":2==t?\"Middle\":\"Right\")+i,Ei(e,fi(i,o),o,function(t){if(\"string\"==typeof t&&(t=Zs[t]),!t)return!1;var r=!1;try{e.isReadOnly()&&(e.state.suppressEdits=!0),r=t(e,n)!=Za}finally{e.state.suppressEdits=!1}return r})}function Oi(e,t,n){var r=e.getOption(\"configureMouse\"),o=r?r(e,t,n):{};if(null==o.unit){var i=Ha?n.shiftKey&&n.metaKey:n.altKey;o.unit=i?\"rectangle\":\"single\"==t?\"char\":\"double\"==t?\"word\":\"line\"}return(null==o.extend||e.doc.extend)&&(o.extend=e.doc.extend||n.shiftKey),null==o.addNew&&(o.addNew=Oa?n.metaKey:n.ctrlKey),null==o.moveOnDrag&&(o.moveOnDrag=!(Oa?n.altKey:n.ctrlKey)),o}function Hi(e,t,n,r){Na?setTimeout(h(Pn,e),0):e.curOp.focus=a(u(e));var o,i=Oi(e,n,r),s=e.doc.sel;e.options.dragDrop&&cs&&!e.isReadOnly()&&\"single\"==n&&(o=s.contains(t))>-1&&(pe((o=s.ranges[o]).from(),t)<0||t.xRel>0)&&(pe(o.to(),t)>0||t.xRel<0)?Fi(e,r,t,i):Pi(e,r,t,i)}function Fi(e,t,n,r){var o=e.display,i=!1,a=yr(e,function(t){Ea&&(o.scroller.draggable=!1),e.state.draggingText=!1,e.state.delayingBlurEvent&&(e.hasFocus()?e.state.delayingBlurEvent=!1:Yn(e)),U(o.wrapper.ownerDocument,\"mouseup\",a),U(o.wrapper.ownerDocument,\"mousemove\",s),U(o.scroller,\"dragstart\",l),U(o.scroller,\"drop\",a),i||(H(t),r.addNew||bo(e.doc,n,null,null,r.extend),Ea&&!ja||Na&&9==Ia?setTimeout(function(){o.wrapper.ownerDocument.body.focus({preventScroll:!0}),o.input.focus()},20):o.input.focus())}),s=function(e){i=i||Math.abs(t.clientX-e.clientX)+Math.abs(t.clientY-e.clientY)>=10},l=function(){return i=!0};Ea&&(o.scroller.draggable=!0),e.state.draggingText=a,a.copy=!r.moveOnDrag,ls(o.wrapper.ownerDocument,\"mouseup\",a),ls(o.wrapper.ownerDocument,\"mousemove\",s),ls(o.scroller,\"dragstart\",l),ls(o.scroller,\"drop\",a),e.state.delayingBlurEvent=!0,setTimeout(function(){return o.input.focus()},20),o.scroller.dragDrop&&o.scroller.dragDrop()}function Vi(e,t,n){if(\"char\"==n)return new Qs(t,t);if(\"word\"==n)return e.findWordAt(t);if(\"line\"==n)return new Qs(de(t.line,0),Me(e.doc,de(t.line+1,0)));var r=n(e,t);return new Qs(r.from,r.to)}function Pi(e,t,n,r){function o(t){if(0!=pe(M,t))if(M=t,\"rectangle\"==r.unit){for(var o=[],i=e.options.tabSize,a=f(re(c,n.line).text,n.ch,i),s=f(re(c,t.line).text,t.ch,i),l=Math.min(a,s),u=Math.max(a,s),g=Math.min(n.line,t.line),m=Math.min(e.lastLine(),Math.max(n.line,t.line));m>=g;g++){var w=re(c,g).text,v=A(w,l,i);l==u?o.push(new Qs(de(g,v),de(g,v))):w.length>v&&o.push(new Qs(de(g,v),de(g,A(w,u,i))))}o.length||o.push(new Qs(n,n)),Io(c,Gr(e,h.ranges.slice(0,p).concat(o),p),{origin:\"*mouse\",scroll:!1}),e.scrollIntoView(t)}else{var b,y=d,x=Vi(e,t,r.unit),T=y.anchor;pe(x.anchor,T)>0?(b=x.head,T=me(y.from(),x.anchor)):(b=x.anchor,T=fe(y.to(),x.head));var C=h.ranges.slice(0);C[p]=Yi(e,new Qs(Me(c,T),b)),Io(c,Gr(e,C,p),es)}}function i(t){var n=++v,s=Dn(e,t,!0,\"rectangle\"==r.unit);if(s)if(0!=pe(s,M)){e.curOp.focus=a(u(e)),o(s);var d=qn(l,c);(s.line>=d.to||s.line<d.from)&&setTimeout(yr(e,function(){v==n&&i(t)}),150)}else{var p=t.clientY<w.top?-20:t.clientY>w.bottom?20:0;p&&setTimeout(yr(e,function(){v==n&&(l.scroller.scrollTop+=p,i(t))}),50)}}function s(t){e.state.selectingText=!1,v=1/0,t&&(H(t),l.input.focus()),U(l.wrapper.ownerDocument,\"mousemove\",b),U(l.wrapper.ownerDocument,\"mouseup\",y),c.history.lastSelOrigin=null}Na&&Yn(e);var l=e.display,c=e.doc;H(t);var d,p,h=c.sel,g=h.ranges;if(r.addNew&&!r.extend?(p=c.sel.contains(n),d=p>-1?g[p]:new Qs(n,n)):(d=c.sel.primary(),p=c.sel.primIndex),\"rectangle\"==r.unit)r.addNew||(d=new Qs(n,n)),n=Dn(e,t,!0,!0),p=-1;else{var m=Vi(e,n,r.unit);d=r.extend?vo(d,m.anchor,m.head,r.extend):m}r.addNew?-1==p?(p=g.length,Io(c,Gr(e,g.concat([d]),p),{scroll:!1,origin:\"*mouse\"})):g.length>1&&g[p].empty()&&\"char\"==r.unit&&!r.extend?(Io(c,Gr(e,g.slice(0,p).concat(g.slice(p+1)),0),{scroll:!1,origin:\"*mouse\"}),h=c.sel):xo(c,p,d,es):(p=0,Io(c,new zs([d],0),es),h=c.sel);var M=n,w=l.wrapper.getBoundingClientRect(),v=0,b=yr(e,function(e){0!==e.buttons&&G(e)?i(e):s(e)}),y=yr(e,s);e.state.selectingText=y,ls(l.wrapper.ownerDocument,\"mousemove\",b),ls(l.wrapper.ownerDocument,\"mouseup\",y)}function Yi(e,t){var n=t.anchor,r=t.head,o=re(e.doc,n.line);if(0==pe(n,r)&&n.sticky==r.sticky)return t;var i=k(o);if(!i)return t;var a=L(i,n.ch,n.sticky),s=i[a];if(s.from!=n.ch&&s.to!=n.ch)return t;var l=a+(s.from==n.ch==(1!=s.level)?0:1);if(0==l||l==i.length)return t;var c;if(r.line!=n.line)c=(r.line-n.line)*(\"ltr\"==e.doc.direction?1:-1)>0;else{var u=L(i,r.ch,r.sticky),d=u-a||(r.ch-n.ch)*(1==s.level?-1:1);c=u==l-1||u==l?0>d:d>0}var p=i[l+(c?-1:0)],h=c==(1==p.level),g=h?p.from:p.to,f=h?\"after\":\"before\";return n.ch==g&&n.sticky==f?t:new Qs(new de(n.line,g,f),r)}function Gi(e,t,n,r){var o,i;if(t.touches)o=t.touches[0].clientX,i=t.touches[0].clientY;else try{o=t.clientX,i=t.clientY}catch(a){return!1}if(o>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;r&&H(t);var s=e.display,l=s.lineDiv.getBoundingClientRect();if(i>l.bottom||!Q(e,n))return V(t);i-=l.top-s.viewOffset;for(var c=0;c<e.display.gutterSpecs.length;++c){var u=s.gutters.childNodes[c];if(u&&u.getBoundingClientRect().right>=o){var d=le(e.doc,i),p=e.display.gutterSpecs[c];return B(e,n,e,d,p.className,t),V(t)}}}function Wi(e,t){return Gi(e,t,\"gutterClick\",!0)}function Xi(e,t){Ot(e.display,t)||_i(e,t)||R(e,t,\"contextmenu\")||Ga||e.display.input.onContextMenu(t)}function _i(e,t){return Q(e,\"gutterContextMenu\")?Gi(e,t,\"gutterContextMenu\",!1):!1}function qi(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\\s*cm-s-\\S+/g,\"\")+e.options.theme.replace(/(^|\\s)\\s*/g,\" cm-s-\"),an(e)}function Ji(e){function t(t,r,o,i){e.defaults[t]=r,o&&(n[t]=i?function(e,t,n){n!=il&&o(e,t,n)}:o)}var n=e.optionHandlers;e.defineOption=t,e.Init=il,t(\"value\",\"\",function(e,t){return e.setValue(t)},!0),t(\"mode\",null,function(e,t){e.doc.modeOption=t,Zr(e)},!0),t(\"indentUnit\",2,Zr,!0),t(\"indentWithTabs\",!1),t(\"smartIndent\",!0),t(\"tabSize\",4,function(e){$r(e),an(e),Ln(e)},!0),t(\"lineSeparator\",null,function(e,t){if(e.doc.lineSep=t,t){var n=[],r=e.doc.first;e.doc.iter(function(e){for(var o=0;;){var i=e.text.indexOf(t,o);if(-1==i)break;o=i+t.length,n.push(de(r,i))}r++});for(var o=n.length-1;o>=0;o--)Po(e.doc,t,n[o],de(n[o].line,n[o].ch+t.length))}}),t(\"specialChars\",/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u061c\\u200b\\u200e\\u200f\\u2028\\u2029\\u202d\\u202e\\u2066\\u2067\\u2069\\ufeff\\ufff9-\\ufffc]/g,function(e,t,n){e.state.specialChars=new RegExp(t.source+(t.test(\"\t\")?\"\":\"|\t\"),\"g\"),n!=il&&e.refresh()}),t(\"specialCharPlaceholder\",ht,function(e){return e.refresh()},!0),t(\"electricChars\",!0),t(\"inputStyle\",Qa?\"contenteditable\":\"textarea\",function(){throw new Error(\"inputStyle can not (yet) be changed in a running editor\")},!0),t(\"spellcheck\",!1,function(e,t){return e.getInputField().spellcheck=t},!0),t(\"autocorrect\",!1,function(e,t){return e.getInputField().autocorrect=t},!0),t(\"autocapitalize\",!1,function(e,t){return e.getInputField().autocapitalize=t},!0),t(\"rtlMoveVisually\",!Fa),t(\"wholeLineUpdateBefore\",!0),t(\"theme\",\"default\",function(e){qi(e),Hr(e)},!0),t(\"keyMap\",\"default\",function(e,t,n){var r=Ai(t),o=n!=il&&Ai(n);o&&o.detach&&o.detach(e,r),r.attach&&r.attach(e,o||null)}),t(\"extraKeys\",null),t(\"configureMouse\",null),t(\"lineWrapping\",!1,Zi,!0),t(\"gutters\",[],function(e,t){e.display.gutterSpecs=Qr(t,e.options.lineNumbers),Hr(e)},!0),t(\"fixedGutter\",!0,function(e,t){e.display.gutters.style.left=t?Nn(e.display)+\"px\":\"0\",e.refresh()},!0),t(\"coverGutterNextToScrollbar\",!1,function(e){return ur(e)},!0),t(\"scrollbarStyle\",\"native\",function(e){pr(e),ur(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)},!0),t(\"lineNumbers\",!1,function(e,t){e.display.gutterSpecs=Qr(e.options.gutters,t),Hr(e)},!0),t(\"firstLineNumber\",1,Hr,!0),t(\"lineNumberFormatter\",function(e){return e},Hr,!0),t(\"showCursorWhenSelecting\",!1,zn,!0),t(\"resetSelectionOnContextMenu\",!0),t(\"lineWiseCopyCut\",!0),t(\"pasteLinesPerSelection\",!0),t(\"selectionsMayTouch\",!1),t(\"readOnly\",!1,function(e,t){\"nocursor\"==t&&(Wn(e),e.display.input.blur()),e.display.input.readOnlyChanged(t)}),t(\"screenReaderLabel\",null,function(e,t){t=\"\"===t?null:t,e.display.input.screenReaderLabelChanged(t)}),t(\"disableInput\",!1,function(e,t){t||e.display.input.reset()},!0),t(\"dragDrop\",!0,Ki),t(\"allowDropFileTypes\",null),t(\"cursorBlinkRate\",530),t(\"cursorScrollMargin\",0),t(\"cursorHeight\",1,zn,!0),t(\"singleCursorHeightPerLine\",!0,zn,!0),t(\"workTime\",100),t(\"workDelay\",100),t(\"flattenSpans\",!0,$r,!0),t(\"addModeClass\",!1,$r,!0),t(\"pollInterval\",100),t(\"undoDepth\",200,function(e,t){return e.doc.history.undoDepth=t}),t(\"historyEventDelay\",1250),t(\"viewportMargin\",10,function(e){return e.refresh()},!0),t(\"maxHighlightLength\",1e4,$r,!0),t(\"moveInputWithCursor\",!0,function(e,t){t||e.display.input.resetPosition()}),t(\"tabindex\",null,function(e,t){return e.display.input.getField().tabIndex=t||\"\"}),t(\"autofocus\",null),t(\"direction\",\"ltr\",function(e,t){return e.doc.setDirection(t)},!0),t(\"phrases\",null)}function Ki(e,t,n){var r=n&&n!=il;if(!t!=!r){var o=e.display.dragFunctions,i=t?ls:U;i(e.display.scroller,\"dragstart\",o.start),i(e.display.scroller,\"dragenter\",o.enter),i(e.display.scroller,\"dragover\",o.over),i(e.display.scroller,\"dragleave\",o.leave),i(e.display.scroller,\"drop\",o.drop)}}function Zi(e){e.options.lineWrapping?(s(e.display.wrapper,\"CodeMirror-wrap\"),e.display.sizer.style.minWidth=\"\",e.display.sizerWidth=null):(Wa(e.display.wrapper,\"CodeMirror-wrap\"),lt(e)),En(e),Ln(e),an(e),setTimeout(function(){return ur(e)},100)}function $i(e,t){var n=this;if(!(this instanceof $i))return new $i(e,t);this.options=t=t?g(t):{},g(al,t,!1);var r=t.value;\"string\"==typeof r?r=new Ys(r,t.mode,null,t.lineSeparator,t.direction):t.mode&&(r.modeOption=t.mode),this.doc=r;var o=new $i.inputStyles[t.inputStyle](this),i=this.display=new Fr(e,r,o,t);i.wrapper.CodeMirror=this,qi(this),t.lineWrapping&&(this.display.wrapper.className+=\" CodeMirror-wrap\"),pr(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new _a,keySeq:null,specialChars:null},t.autofocus&&!Qa&&i.input.focus(),Na&&11>Ia&&setTimeout(function(){return n.display.input.reset(!0)},20),ea(this),li(),hr(this),this.curOp.forceUpdate=!0,ro(this,r),t.autofocus&&!Qa||this.hasFocus()?setTimeout(function(){n.hasFocus()&&!n.state.focused&&Gn(n)},20):Wn(this);for(var a in sl)sl.hasOwnProperty(a)&&sl[a](this,t[a],il);zr(this),t.finishInit&&t.finishInit(this);for(var s=0;s<ll.length;++s)ll[s](this);gr(this),Ea&&t.lineWrapping&&\"optimizelegibility\"==getComputedStyle(i.lineDiv).textRendering&&(i.lineDiv.style.textRendering=\"auto\")}function ea(e){function t(){o.activeTouch&&(i=setTimeout(function(){return o.activeTouch=null},1e3),a=o.activeTouch,a.end=+new Date)}function n(e){if(1!=e.touches.length)return!1;var t=e.touches[0];return t.radiusX<=1&&t.radiusY<=1}function r(e,t){if(null==t.left)return!0;var n=t.left-e.left,r=t.top-e.top;return n*n+r*r>400}var o=e.display;ls(o.scroller,\"mousedown\",yr(e,zi)),Na&&11>Ia?ls(o.scroller,\"dblclick\",yr(e,function(t){if(!R(e,t)){var n=Dn(e,t);if(n&&!Wi(e,t)&&!Ot(e.display,t)){H(t);var r=e.findWordAt(n);bo(e.doc,r.anchor,r.head)}}})):ls(o.scroller,\"dblclick\",function(t){return R(e,t)||H(t)}),ls(o.scroller,\"contextmenu\",function(t){return Xi(e,t)}),ls(o.input.getField(),\"contextmenu\",function(t){o.scroller.contains(t.target)||Xi(e,t)});var i,a={end:0};ls(o.scroller,\"touchstart\",function(t){if(!R(e,t)&&!n(t)&&!Wi(e,t)){o.input.ensurePolled(),clearTimeout(i);var r=+new Date;o.activeTouch={start:r,moved:!1,prev:r-a.end<=300?a:null},1==t.touches.length&&(o.activeTouch.left=t.touches[0].pageX,o.activeTouch.top=t.touches[0].pageY)}}),ls(o.scroller,\"touchmove\",function(){o.activeTouch&&(o.activeTouch.moved=!0)}),ls(o.scroller,\"touchend\",function(n){var i=o.activeTouch;if(i&&!Ot(o,n)&&null!=i.left&&!i.moved&&new Date-i.start<300){var a,s=e.coordsChar(o.activeTouch,\"page\");a=!i.prev||r(i,i.prev)?new Qs(s,s):!i.prev.prev||r(i,i.prev.prev)?e.findWordAt(s):new Qs(de(s.line,0),Me(e.doc,de(s.line+1,0))),e.setSelection(a.anchor,a.head),e.focus(),H(n)}t()}),ls(o.scroller,\"touchcancel\",t),ls(o.scroller,\"scroll\",function(){o.scroller.clientHeight&&(ar(e,o.scroller.scrollTop),lr(e,o.scroller.scrollLeft,!0),B(e,\"scroll\",e))}),ls(o.scroller,\"mousewheel\",function(t){return Yr(e,t)}),ls(o.scroller,\"DOMMouseScroll\",function(t){return Yr(e,t)}),ls(o.wrapper,\"scroll\",function(){return o.wrapper.scrollTop=o.wrapper.scrollLeft=0}),o.dragFunctions={enter:function(t){R(e,t)||P(t)},over:function(t){R(e,t)||(ii(e,t),P(t))},start:function(t){return oi(e,t)},drop:yr(e,ri),leave:function(t){R(e,t)||ai(e)}};var s=o.input.getField();ls(s,\"keyup\",function(t){return Ui.call(e,t)}),ls(s,\"keydown\",yr(e,ki)),ls(s,\"keypress\",yr(e,Bi)),ls(s,\"focus\",function(t){return Gn(e,t)}),ls(s,\"blur\",function(t){return Wn(e,t)})}function ta(e,t,n,r){var o,i=e.doc;null==n&&(n=\"add\"),\"smart\"==n&&(i.mode.indent?o=xe(e,t).state:n=\"prev\");var a=e.options.tabSize,s=re(i,t),l=f(s.text,null,a);s.stateAfter&&(s.stateAfter=null);var c,u=s.text.match(/^\\s*/)[0];if(r||/\\S/.test(s.text)){if(\"smart\"==n&&(c=i.mode.indent(o,s.text.slice(u.length),s.text),c==Za||c>150)){if(!r)return;n=\"prev\"}}else c=0,n=\"not\";\"prev\"==n?c=t>i.first?f(re(i,t-1).text,null,a):0:\"add\"==n?c=l+e.options.indentUnit:\"subtract\"==n?c=l-e.options.indentUnit:\"number\"==typeof n&&(c=l+n),c=Math.max(0,c);var d=\"\",p=0;if(e.options.indentWithTabs)for(var h=Math.floor(c/a);h;--h)p+=a,d+=\"\t\";if(c>p&&(d+=M(c-p)),d!=u)return Po(i,d,de(t,0),de(t,u.length),\"+input\"),s.stateAfter=null,!0;for(var g=0;g<i.sel.ranges.length;g++){var m=i.sel.ranges[g];if(m.head.line==t&&m.head.ch<u.length){var A=de(t,u.length);xo(i,g,new Qs(A,A));break}}}function na(e){cl=e}function ra(e,t,n,r,o){var i=e.doc;e.display.shift=!1,r||(r=i.sel);var a=+new Date-200,s=\"paste\"==o||e.state.pasteIncoming>a,l=us(t),c=null;if(s&&r.ranges.length>1)if(cl&&cl.text.join(\"\\n\")==t){if(r.ranges.length%cl.text.length==0){c=[];for(var u=0;u<cl.text.length;u++)c.push(i.splitLines(cl.text[u]))}}else l.length==r.ranges.length&&e.options.pasteLinesPerSelection&&(c=v(l,function(e){return[e]}));for(var d=e.curOp.updateInput,p=r.ranges.length-1;p>=0;p--){var h=r.ranges[p],g=h.from(),f=h.to();h.empty()&&(n&&n>0?g=de(g.line,g.ch-n):e.state.overwrite&&!s?f=de(f.line,Math.min(re(i,f.line).text.length,f.ch+w(l).length)):s&&cl&&cl.lineWise&&cl.text.join(\"\\n\")==l.join(\"\\n\")&&(g=f=de(g.line,0)));var m={from:g,to:f,text:c?c[p%c.length]:l,origin:o||(s?\"paste\":e.state.cutIncoming>a?\"cut\":\"+input\")};zo(e.doc,m),Tt(e,\"inputRead\",e,m)}t&&!s&&ia(e,t),tr(e),e.curOp.updateInput<2&&(e.curOp.updateInput=d),e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=-1}function oa(e,t){var n=e.clipboardData&&e.clipboardData.getData(\"Text\");return n?(e.preventDefault(),t.isReadOnly()||t.options.disableInput||!t.hasFocus()||br(t,function(){return ra(t,n,0,null,\"paste\")}),!0):void 0}function ia(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var n=e.doc.sel,r=n.ranges.length-1;r>=0;r--){var o=n.ranges[r];if(!(o.head.ch>100||r&&n.ranges[r-1].head.line==o.head.line)){var i=e.getModeAt(o.head),a=!1;if(i.electricChars){for(var s=0;s<i.electricChars.length;s++)if(t.indexOf(i.electricChars.charAt(s))>-1){a=ta(e,o.head.line,\"smart\");break}}else i.electricInput&&i.electricInput.test(re(e.doc,o.head.line).text.slice(0,o.head.ch))&&(a=ta(e,o.head.line,\"smart\"));a&&Tt(e,\"electricInput\",e,o.head.line)}}}function aa(e){for(var t=[],n=[],r=0;r<e.doc.sel.ranges.length;r++){var o=e.doc.sel.ranges[r].head.line,i={anchor:de(o,0),head:de(o+1,0)};n.push(i),t.push(e.getRange(i.anchor,i.head))}return{text:t,ranges:n}}function sa(e,t,n,r){e.setAttribute(\"autocorrect\",n?\"on\":\"off\"),e.setAttribute(\"autocapitalize\",r?\"on\":\"off\"),e.setAttribute(\"spellcheck\",!!t)}function la(){var e=r(\"textarea\",null,null,\"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none\"),t=r(\"div\",[e],null,\"overflow: hidden; position: relative; width: 3px; height: 0px;\");return Ea?e.style.width=\"1000px\":e.setAttribute(\"wrap\",\"off\"),Ra&&(e.style.border=\"1px solid black\"),t}function ca(e){var t=e.optionHandlers,n=e.helpers={};e.prototype={constructor:e,focus:function(){p(this).focus(),this.display.input.focus()},setOption:function(e,n){var r=this.options,o=r[e];(r[e]!=n||\"mode\"==e)&&(r[e]=n,t.hasOwnProperty(e)&&yr(this,t[e])(this,n,o),B(this,\"optionChange\",this,e))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?\"push\":\"unshift\"](Ai(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,n=0;n<t.length;++n)if(t[n]==e||t[n].name==e)return t.splice(n,1),!0},addOverlay:xr(function(t,n){var r=t.token?t:e.getMode(this.options,t);if(r.startState)throw new Error(\"Overlays may not be stateful.\");b(this.state.overlays,{mode:r,modeSpec:t,opaque:n&&n.opaque,priority:n&&n.priority||0},function(e){return e.priority}),this.state.modeGen++,Ln(this)}),removeOverlay:xr(function(e){for(var t=this.state.overlays,n=0;n<t.length;++n){var r=t[n].modeSpec;if(r==e||\"string\"==typeof e&&r.name==e)return t.splice(n,1),this.state.modeGen++,void Ln(this)}}),indentLine:xr(function(e,t,n){\"string\"!=typeof t&&\"number\"!=typeof t&&(t=null==t?this.options.smartIndent?\"smart\":\"prev\":t?\"add\":\"subtract\"),ce(this.doc,e)&&ta(this,e,t,n)}),indentSelection:xr(function(e){for(var t=this.doc.sel.ranges,n=-1,r=0;r<t.length;r++){var o=t[r];if(o.empty())o.head.line>n&&(ta(this,o.head.line,e,!0),n=o.head.line,r==this.doc.sel.primIndex&&tr(this));else{var i=o.from(),a=o.to(),s=Math.max(n,i.line);n=Math.min(this.lastLine(),a.line-(a.ch?0:1))+1;for(var l=s;n>l;++l)ta(this,l,e);var c=this.doc.sel.ranges;0==i.ch&&t.length==c.length&&c[r].from().ch>0&&xo(this.doc,r,new Qs(i,c[r].to()),$a)}}}),getTokenAt:function(e,t){return Ie(this,e,t)},getLineTokens:function(e,t){return Ie(this,de(e),t,!0)},getTokenTypeAt:function(e){e=Me(this.doc,e);var t,n=ye(this,re(this.doc,e.line)),r=0,o=(n.length-1)/2,i=e.ch;if(0==i)t=n[2];else for(;;){var a=r+o>>1;if((a?n[2*a-1]:0)>=i)o=a;else{if(!(n[2*a+1]<i)){t=n[2*a+2];break}r=a+1}}var s=t?t.indexOf(\"overlay \"):-1;return 0>s?t:0==s?null:t.slice(0,s-1)},getModeAt:function(t){var n=this.doc.mode;return n.innerMode?e.innerMode(n,this.getTokenAt(t).state).mode:n},getHelper:function(e,t){return this.getHelpers(e,t)[0]},getHelpers:function(e,t){var r=[];if(!n.hasOwnProperty(t))return r;var o=n[t],i=this.getModeAt(e);if(\"string\"==typeof i[t])o[i[t]]&&r.push(o[i[t]]);else if(i[t])for(var a=0;a<i[t].length;a++){var s=o[i[t][a]];s&&r.push(s)}else i.helperType&&o[i.helperType]?r.push(o[i.helperType]):o[i.name]&&r.push(o[i.name]);for(var l=0;l<o._global.length;l++){var c=o._global[l];c.pred(i,this)&&-1==m(r,c.val)&&r.push(c.val)}return r},getStateAfter:function(e,t){var n=this.doc;return e=Ae(n,null==e?n.first+n.size-1:e),xe(this,e+1,t).state},cursorCoords:function(e,t){var n,r=this.doc.sel.primary();return n=null==e?r.head:\"object\"==typeof e?Me(this.doc,e):e?r.from():r.to(),hn(this,n,t||\"page\")},charCoords:function(e,t){return pn(this,Me(this.doc,e),t||\"page\")},coordsChar:function(e,t){return e=dn(this,e,t||\"page\"),mn(this,e.left,e.top)},lineAtHeight:function(e,t){return e=dn(this,{top:e,left:0},t||\"page\").top,le(this.doc,e+this.display.viewOffset)},heightAtLine:function(e,t,n){var r,o=!1;if(\"number\"==typeof e){var i=this.doc.first+this.doc.size-1;e<this.doc.first?e=this.doc.first:e>i&&(e=i,o=!0),r=re(this.doc,e)}else r=e;return un(this,r,{top:0,left:0},t||\"page\",n||o).top+(o?this.doc.height-at(r):0)},defaultTextHeight:function(){return xn(this.display)},defaultCharWidth:function(){return Tn(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,n,r,o){var i=this.display;e=hn(this,Me(this.doc,e));var a=e.bottom,s=e.left;if(t.style.position=\"absolute\",t.setAttribute(\"cm-ignore-events\",\"true\"),this.display.input.setUneditable(t),i.sizer.appendChild(t),\"over\"==r)a=e.top;else if(\"above\"==r||\"near\"==r){var l=Math.max(i.wrapper.clientHeight,this.doc.height),c=Math.max(i.sizer.clientWidth,i.lineSpace.clientWidth);(\"above\"==r||e.bottom+t.offsetHeight>l)&&e.top>t.offsetHeight?a=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=l&&(a=e.bottom),s+t.offsetWidth>c&&(s=c-t.offsetWidth)}t.style.top=a+\"px\",t.style.left=t.style.right=\"\",\"right\"==o?(s=i.sizer.clientWidth-t.offsetWidth,t.style.right=\"0px\"):(\"left\"==o?s=0:\"middle\"==o&&(s=(i.sizer.clientWidth-t.offsetWidth)/2),t.style.left=s+\"px\"),n&&Zn(this,{left:s,top:a,right:s+t.offsetWidth,bottom:a+t.offsetHeight})},triggerOnKeyDown:xr(ki),triggerOnKeyPress:xr(Bi),triggerOnKeyUp:Ui,triggerOnMouseDown:xr(zi),execCommand:function(e){return Zs.hasOwnProperty(e)?Zs[e].call(null,this):void 0},triggerElectric:xr(function(e){ia(this,e)}),findPosH:function(e,t,n,r){var o=1;0>t&&(o=-1,t=-t);for(var i=Me(this.doc,e),a=0;t>a&&(i=ua(this.doc,i,o,n,r),!i.hitSide);++a);return i},moveH:xr(function(e,t){var n=this;this.extendSelectionsBy(function(r){return n.display.shift||n.doc.extend||r.empty()?ua(n.doc,r.head,e,t,n.options.rtlMoveVisually):0>e?r.from():r.to()},ts)}),deleteH:xr(function(e,t){var n=this.doc.sel,r=this.doc;n.somethingSelected()?r.replaceSelection(\"\",null,\"+delete\"):Mi(this,function(n){var o=ua(r,n.head,e,t,!1);return 0>e?{from:o,to:n.head}:{from:n.head,to:o}})}),findPosV:function(e,t,n,r){var o=1,i=r;0>t&&(o=-1,t=-t);for(var a=Me(this.doc,e),s=0;t>s;++s){var l=hn(this,a,\"div\");if(null==i?i=l.left:l.left=i,a=da(this,l,o,n),a.hitSide)break}return a},moveV:xr(function(e,t){var n=this,r=this.doc,o=[],i=!this.display.shift&&!r.extend&&r.sel.somethingSelected();if(r.extendSelectionsBy(function(a){if(i)return 0>e?a.from():a.to();var s=hn(n,a.head,\"div\");null!=a.goalColumn&&(s.left=a.goalColumn),o.push(s.left);var l=da(n,s,e,t);return\"page\"==t&&a==r.sel.primary()&&er(n,pn(n,l,\"div\").top-s.top),l},ts),o.length)for(var a=0;a<r.sel.ranges.length;a++)r.sel.ranges[a].goalColumn=o[a]}),findWordAt:function(e){var t=this.doc,n=re(t,e.line).text,r=e.ch,o=e.ch;if(n){var i=this.getHelper(e,\"wordChars\");\"before\"!=e.sticky&&o!=n.length||!r?++o:--r;for(var a=n.charAt(r),s=C(a,i)?function(e){return C(e,i)}:/\\s/.test(a)?function(e){return/\\s/.test(e)}:function(e){return!/\\s/.test(e)&&!C(e)};r>0&&s(n.charAt(r-1));)--r;for(;o<n.length&&s(n.charAt(o));)++o}return new Qs(de(e.line,r),de(e.line,o))},toggleOverwrite:function(e){(null==e||e!=this.state.overwrite)&&((this.state.overwrite=!this.state.overwrite)?s(this.display.cursorDiv,\"CodeMirror-overwrite\"):Wa(this.display.cursorDiv,\"CodeMirror-overwrite\"),B(this,\"overwriteToggle\",this,this.state.overwrite));\n},hasFocus:function(){return this.display.input.getField()==a(u(this))},isReadOnly:function(){return!(!this.options.readOnly&&!this.doc.cantEdit)},scrollTo:xr(function(e,t){nr(this,e,t)}),getScrollInfo:function(){var e=this.display.scroller;return{left:e.scrollLeft,top:e.scrollTop,height:e.scrollHeight-Pt(this)-this.display.barHeight,width:e.scrollWidth-Pt(this)-this.display.barWidth,clientHeight:Gt(this),clientWidth:Yt(this)}},scrollIntoView:xr(function(e,t){null==e?(e={from:this.doc.sel.primary().head,to:null},null==t&&(t=this.options.cursorScrollMargin)):\"number\"==typeof e?e={from:de(e,0),to:null}:null==e.from&&(e={from:e,to:null}),e.to||(e.to=e.from),e.margin=t||0,null!=e.from.line?rr(this,e):ir(this,e.from,e.to,e.margin)}),setSize:xr(function(e,t){var n=this,r=function(e){return\"number\"==typeof e||/^\\d+$/.test(String(e))?e+\"px\":e};null!=e&&(this.display.wrapper.style.width=r(e)),null!=t&&(this.display.wrapper.style.height=r(t)),this.options.lineWrapping&&on(this);var o=this.display.viewFrom;this.doc.iter(o,this.display.viewTo,function(e){if(e.widgets)for(var t=0;t<e.widgets.length;t++)if(e.widgets[t].noHScroll){kn(n,o,\"widget\");break}++o}),this.curOp.forceUpdate=!0,B(this,\"refresh\",this)}),operation:function(e){return br(this,e)},startOperation:function(){return hr(this)},endOperation:function(){return gr(this)},refresh:xr(function(){var e=this.display.cachedTextHeight;Ln(this),this.curOp.forceUpdate=!0,an(this),nr(this,this.doc.scrollLeft,this.doc.scrollTop),Ur(this.display),(null==e||Math.abs(e-xn(this.display))>.5||this.options.lineWrapping)&&En(this),B(this,\"refresh\",this)}),swapDoc:xr(function(e){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),ro(this,e),an(this),this.display.input.reset(),nr(this,e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,Tt(this,\"swapDoc\",this,t),t}),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},O(e),e.registerHelper=function(t,r,o){n.hasOwnProperty(t)||(n[t]=e[t]={_global:[]}),n[t][r]=o},e.registerGlobalHelper=function(t,r,o,i){e.registerHelper(t,r,i),n[t]._global.push({pred:o,val:i})}}function ua(e,t,n,r,o){function i(){var n=t.line+u;return n<e.first||n>=e.first+e.size?!1:(t=new de(n,t.ch,t.sticky),c=re(e,n))}function a(a){var s;if(\"codepoint\"==r){var l=c.text.charCodeAt(t.ch+(n>0?0:-1));if(isNaN(l))s=null;else{var d=n>0?l>=55296&&56320>l:l>=56320&&57343>l;s=new de(t.line,Math.max(0,Math.min(c.text.length,t.ch+n*(d?2:1))),-n)}}else s=o?yi(e.cm,c,t,n):vi(c,t,n);if(null==s){if(a||!i())return!1;t=bi(o,e.cm,c,t.line,u)}else t=s;return!0}var s=t,l=n,c=re(e,t.line),u=o&&\"rtl\"==e.direction?-n:n;if(\"char\"==r||\"codepoint\"==r)a();else if(\"column\"==r)a(!0);else if(\"word\"==r||\"group\"==r)for(var d=null,p=\"group\"==r,h=e.cm&&e.cm.getHelper(t,\"wordChars\"),g=!0;!(0>n)||a(!g);g=!1){var f=c.text.charAt(t.ch)||\"\\n\",m=C(f,h)?\"w\":p&&\"\\n\"==f?\"n\":!p||/\\s/.test(f)?null:\"p\";if(!p||g||m||(m=\"s\"),d&&d!=m){0>n&&(n=1,a(),t.sticky=\"after\");break}if(m&&(d=m),n>0&&!a(!g))break}var A=jo(e,t,s,l,!0);return he(s,A)&&(A.hitSide=!0),A}function da(e,t,n,r){var o,i=e.doc,a=t.left;if(\"page\"==r){var s=Math.min(e.display.wrapper.clientHeight,p(e).innerHeight||i(e).documentElement.clientHeight),l=Math.max(s-.5*xn(e.display),3);o=(n>0?t.bottom:t.top)+n*l}else\"line\"==r&&(o=n>0?t.bottom+3:t.top-3);for(var c;c=mn(e,a,o),c.outside;){if(0>n?0>=o:o>=i.height){c.hitSide=!0;break}o+=5*n}return c}function pa(e,t){var n=Jt(e,t.line);if(!n||n.hidden)return null;var r=re(e.doc,t.line),o=Xt(n,r,t.line),i=k(r,e.doc.direction),a=\"left\";if(i){var s=L(i,t.ch);a=s%2?\"right\":\"left\"}var l=$t(o.map,t.ch,a);return l.offset=\"right\"==l.collapse?l.end:l.start,l}function ha(e){for(var t=e;t;t=t.parentNode)if(/CodeMirror-gutter-wrapper/.test(t.className))return!0;return!1}function ga(e,t){return t&&(e.bad=!0),e}function fa(e,t,n,r,o){function i(e){return function(t){return t.id==e}}function a(){u&&(c+=d,p&&(c+=d),u=p=!1)}function s(e){e&&(a(),c+=e)}function l(t){if(1==t.nodeType){var n=t.getAttribute(\"cm-text\");if(n)return void s(n);var c,h=t.getAttribute(\"cm-marker\");if(h){var g=e.findMarks(de(r,0),de(o+1,0),i(+h));return void(g.length&&(c=g[0].find(0))&&s(oe(e.doc,c.from,c.to).join(d)))}if(\"false\"==t.getAttribute(\"contenteditable\"))return;var f=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&0==t.textContent.length)return;f&&a();for(var m=0;m<t.childNodes.length;m++)l(t.childNodes[m]);/^(pre|p)$/i.test(t.nodeName)&&(p=!0),f&&(u=!0)}else 3==t.nodeType&&s(t.nodeValue.replace(/\\u200b/g,\"\").replace(/\\u00a0/g,\" \"))}for(var c=\"\",u=!1,d=e.doc.lineSeparator(),p=!1;l(t),t!=n;)t=t.nextSibling,p=!1;return c}function ma(e,t,n){var r;if(t==e.display.lineDiv){if(r=e.display.lineDiv.childNodes[n],!r)return ga(e.clipPos(de(e.display.viewTo-1)),!0);t=null,n=0}else for(r=t;;r=r.parentNode){if(!r||r==e.display.lineDiv)return null;if(r.parentNode&&r.parentNode==e.display.lineDiv)break}for(var o=0;o<e.display.view.length;o++){var i=e.display.view[o];if(i.node==r)return Aa(i,t,n)}}function Aa(e,t,n){function r(t,n,r){for(var o=-1;o<(d?d.length:0);o++)for(var i=0>o?u.map:d[o],a=0;a<i.length;a+=3){var s=i[a+2];if(s==t||s==n){var l=se(0>o?e.line:e.rest[o]),c=i[a]+r;return(0>r||s!=t)&&(c=i[a+(r?1:0)]),de(l,c)}}}var o=e.text.firstChild,a=!1;if(!t||!i(o,t))return ga(de(se(e.line),0),!0);if(t==o&&(a=!0,t=o.childNodes[n],n=0,!t)){var s=e.rest?w(e.rest):e.line;return ga(de(se(s),s.text.length),a)}var l=3==t.nodeType?t:null,c=t;for(l||1!=t.childNodes.length||3!=t.firstChild.nodeType||(l=t.firstChild,n&&(n=l.nodeValue.length));c.parentNode!=o;)c=c.parentNode;var u=e.measure,d=u.maps,p=r(l,c,n);if(p)return ga(p,a);for(var h=c.nextSibling,g=l?l.nodeValue.length-n:0;h;h=h.nextSibling){if(p=r(h,h.firstChild,0))return ga(de(p.line,p.ch-g),a);g+=h.textContent.length}for(var f=c.previousSibling,m=n;f;f=f.previousSibling){if(p=r(f,f.firstChild,-1))return ga(de(p.line,p.ch+m),a);m+=f.textContent.length}}function Ma(e,t){function n(){e.value=c.getValue()}if(t=t?g(t):{},t.value=e.value,!t.tabindex&&e.tabIndex&&(t.tabindex=e.tabIndex),!t.placeholder&&e.placeholder&&(t.placeholder=e.placeholder),null==t.autofocus){var r=a(d(e));t.autofocus=r==e||null!=e.getAttribute(\"autofocus\")&&r==document.body}var o;if(e.form&&(ls(e.form,\"submit\",n),!t.leaveSubmitMethodAlone)){var i=e.form;o=i.submit;try{var s=i.submit=function(){n(),i.submit=o,i.submit(),i.submit=s}}catch(l){}}t.finishInit=function(r){r.save=n,r.getTextArea=function(){return e},r.toTextArea=function(){r.toTextArea=isNaN,n(),e.parentNode.removeChild(r.getWrapperElement()),e.style.display=\"\",e.form&&(U(e.form,\"submit\",n),t.leaveSubmitMethodAlone||\"function\"!=typeof e.form.submit||(e.form.submit=o))}},e.style.display=\"none\";var c=$i(function(t){return e.parentNode.insertBefore(t,e.nextSibling)},t);return c}function wa(e){e.off=U,e.on=ls,e.wheelEventPixels=Pr,e.Doc=Ys,e.splitLines=us,e.countColumn=f,e.findColumn=A,e.isWordChar=T,e.Pass=Za,e.signal=B,e.Line=xs,e.changeEnd=Xr,e.scrollbarModel=ks,e.Pos=de,e.cmpPos=pe,e.modes=gs,e.mimeModes=fs,e.resolveMode=K,e.getMode=Z,e.modeExtensions=ms,e.extendMode=$,e.copyState=ee,e.startState=ne,e.innerMode=te,e.commands=Zs,e.keyMap=Ks,e.keyName=mi,e.isModifierKey=gi,e.lookupKey=hi,e.normalizeKeyMap=pi,e.StringStream=As,e.SharedTextMarker=Vs,e.TextMarker=Fs,e.LineWidget=Os,e.e_preventDefault=H,e.e_stopPropagation=F,e.e_stop=P,e.addClass=s,e.contains=i,e.rmClass=Wa,e.keyNames=Xs}var va=navigator.userAgent,ba=navigator.platform,ya=/gecko\\/\\d/i.test(va),xa=/MSIE \\d/.test(va),Ta=/Trident\\/(?:[7-9]|\\d{2,})\\..*rv:(\\d+)/.exec(va),Ca=/Edge\\/(\\d+)/.exec(va),Na=xa||Ta||Ca,Ia=Na&&(xa?document.documentMode||6:+(Ca||Ta)[1]),Ea=!Ca&&/WebKit\\//.test(va),Da=Ea&&/Qt\\/\\d+\\.\\d+/.test(va),Sa=!Ca&&/Chrome\\/(\\d+)/.exec(va),La=Sa&&+Sa[1],ka=/Opera\\//.test(va),ja=/Apple Computer/.test(navigator.vendor),Ua=/Mac OS X 1\\d\\D([8-9]|\\d\\d)\\D/.test(va),Ba=/PhantomJS/.test(va),Ra=ja&&(/Mobile\\/\\w+/.test(va)||navigator.maxTouchPoints>2),za=/Android/.test(va),Qa=Ra||za||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(va),Oa=Ra||/Mac/.test(ba),Ha=/\\bCrOS\\b/.test(va),Fa=/win/i.test(ba),Va=ka&&va.match(/Version\\/(\\d*\\.\\d*)/);Va&&(Va=Number(Va[1])),Va&&Va>=15&&(ka=!1,Ea=!0);var Pa,Ya=Oa&&(Da||ka&&(null==Va||12.11>Va)),Ga=ya||Na&&Ia>=9,Wa=function(t,n){var r=t.className,o=e(n).exec(r);if(o){var i=r.slice(o.index+o[0].length);t.className=r.slice(0,o.index)+(i?o[1]+i:\"\")}};Pa=document.createRange?function(e,t,n,r){var o=document.createRange();return o.setEnd(r||e,n),o.setStart(e,t),o}:function(e,t,n){var r=document.body.createTextRange();try{r.moveToElementText(e.parentNode)}catch(o){return r}return r.collapse(!0),r.moveEnd(\"character\",n),r.moveStart(\"character\",t),r};var Xa=function(e){e.select()};Ra?Xa=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:Na&&(Xa=function(e){try{e.select()}catch(t){}});var _a=function(){this.id=null,this.f=null,this.time=0,this.handler=h(this.onTimeout,this)};_a.prototype.onTimeout=function(e){e.id=0,e.time<=+new Date?e.f():setTimeout(e.handler,e.time-+new Date)},_a.prototype.set=function(e,t){this.f=t;var n=+new Date+e;(!this.id||n<this.time)&&(clearTimeout(this.id),this.id=setTimeout(this.handler,e),this.time=n)};var qa,Ja,Ka=50,Za={toString:function(){return\"CodeMirror.Pass\"}},$a={scroll:!1},es={origin:\"*mouse\"},ts={origin:\"+move\"},ns=[\"\"],rs=/[\\u00df\\u0587\\u0590-\\u05f4\\u0600-\\u06ff\\u3040-\\u309f\\u30a0-\\u30ff\\u3400-\\u4db5\\u4e00-\\u9fcc\\uac00-\\ud7af]/,os=/[\\u0300-\\u036f\\u0483-\\u0489\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u065e\\u0670\\u06d6-\\u06dc\\u06de-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07eb-\\u07f3\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0900-\\u0902\\u093c\\u0941-\\u0948\\u094d\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09bc\\u09be\\u09c1-\\u09c4\\u09cd\\u09d7\\u09e2\\u09e3\\u0a01\\u0a02\\u0a3c\\u0a41\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a70\\u0a71\\u0a75\\u0a81\\u0a82\\u0abc\\u0ac1-\\u0ac5\\u0ac7\\u0ac8\\u0acd\\u0ae2\\u0ae3\\u0b01\\u0b3c\\u0b3e\\u0b3f\\u0b41-\\u0b44\\u0b4d\\u0b56\\u0b57\\u0b62\\u0b63\\u0b82\\u0bbe\\u0bc0\\u0bcd\\u0bd7\\u0c3e-\\u0c40\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0cbc\\u0cbf\\u0cc2\\u0cc6\\u0ccc\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0d3e\\u0d41-\\u0d44\\u0d4d\\u0d57\\u0d62\\u0d63\\u0dca\\u0dcf\\u0dd2-\\u0dd4\\u0dd6\\u0ddf\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0eb1\\u0eb4-\\u0eb9\\u0ebb\\u0ebc\\u0ec8-\\u0ecd\\u0f18\\u0f19\\u0f35\\u0f37\\u0f39\\u0f71-\\u0f7e\\u0f80-\\u0f84\\u0f86\\u0f87\\u0f90-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102d-\\u1030\\u1032-\\u1037\\u1039\\u103a\\u103d\\u103e\\u1058\\u1059\\u105e-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108d\\u109d\\u135f\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b7-\\u17bd\\u17c6\\u17c9-\\u17d3\\u17dd\\u180b-\\u180d\\u18a9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193b\\u1a17\\u1a18\\u1a56\\u1a58-\\u1a5e\\u1a60\\u1a62\\u1a65-\\u1a6c\\u1a73-\\u1a7c\\u1a7f\\u1b00-\\u1b03\\u1b34\\u1b36-\\u1b3a\\u1b3c\\u1b42\\u1b6b-\\u1b73\\u1b80\\u1b81\\u1ba2-\\u1ba5\\u1ba8\\u1ba9\\u1c2c-\\u1c33\\u1c36\\u1c37\\u1cd0-\\u1cd2\\u1cd4-\\u1ce0\\u1ce2-\\u1ce8\\u1ced\\u1dc0-\\u1de6\\u1dfd-\\u1dff\\u200c\\u200d\\u20d0-\\u20f0\\u2cef-\\u2cf1\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\ua66f-\\ua672\\ua67c\\ua67d\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua825\\ua826\\ua8c4\\ua8e0-\\ua8f1\\ua926-\\ua92d\\ua947-\\ua951\\ua980-\\ua982\\ua9b3\\ua9b6-\\ua9b9\\ua9bc\\uaa29-\\uaa2e\\uaa31\\uaa32\\uaa35\\uaa36\\uaa43\\uaa4c\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uabe5\\uabe8\\uabed\\udc00-\\udfff\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe26\\uff9e\\uff9f]/,is=null,as=function(){function e(e){return 247>=e?n.charAt(e):e>=1424&&1524>=e?\"R\":e>=1536&&1785>=e?r.charAt(e-1536):e>=1774&&2220>=e?\"r\":e>=8192&&8203>=e?\"w\":8204==e?\"b\":\"L\"}function t(e,t,n){this.level=e,this.from=t,this.to=n}var n=\"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN\",r=\"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111\",o=/[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac]/,i=/[stwN]/,a=/[LRr]/,s=/[Lb1n]/,l=/[1n]/;return function(n,r){var c=\"ltr\"==r?\"L\":\"R\";if(0==n.length||\"ltr\"==r&&!o.test(n))return!1;for(var u=n.length,d=[],p=0;u>p;++p)d.push(e(n.charCodeAt(p)));for(var h=0,g=c;u>h;++h){var f=d[h];\"m\"==f?d[h]=g:g=f}for(var m=0,A=c;u>m;++m){var M=d[m];\"1\"==M&&\"r\"==A?d[m]=\"n\":a.test(M)&&(A=M,\"r\"==M&&(d[m]=\"R\"))}for(var v=1,b=d[0];u-1>v;++v){var y=d[v];\"+\"==y&&\"1\"==b&&\"1\"==d[v+1]?d[v]=\"1\":\",\"!=y||b!=d[v+1]||\"1\"!=b&&\"n\"!=b||(d[v]=b),b=y}for(var x=0;u>x;++x){var T=d[x];if(\",\"==T)d[x]=\"N\";else if(\"%\"==T){var C=void 0;for(C=x+1;u>C&&\"%\"==d[C];++C);for(var N=x&&\"!\"==d[x-1]||u>C&&\"1\"==d[C]?\"1\":\"N\",I=x;C>I;++I)d[I]=N;x=C-1}}for(var E=0,D=c;u>E;++E){var S=d[E];\"L\"==D&&\"1\"==S?d[E]=\"L\":a.test(S)&&(D=S)}for(var L=0;u>L;++L)if(i.test(d[L])){var k=void 0;for(k=L+1;u>k&&i.test(d[k]);++k);for(var j=\"L\"==(L?d[L-1]:c),U=\"L\"==(u>k?d[k]:c),B=j==U?j?\"L\":\"R\":c,R=L;k>R;++R)d[R]=B;L=k-1}for(var z,Q=[],O=0;u>O;)if(s.test(d[O])){var H=O;for(++O;u>O&&s.test(d[O]);++O);Q.push(new t(0,H,O))}else{var F=O,V=Q.length,P=\"rtl\"==r?1:0;for(++O;u>O&&\"L\"!=d[O];++O);for(var Y=F;O>Y;)if(l.test(d[Y])){Y>F&&(Q.splice(V,0,new t(1,F,Y)),V+=P);var G=Y;for(++Y;O>Y&&l.test(d[Y]);++Y);Q.splice(V,0,new t(2,G,Y)),V+=P,F=Y}else++Y;O>F&&Q.splice(V,0,new t(1,F,O))}return\"ltr\"==r&&(1==Q[0].level&&(z=n.match(/^\\s+/))&&(Q[0].from=z[0].length,Q.unshift(new t(0,0,z[0].length))),1==w(Q).level&&(z=n.match(/\\s+$/))&&(w(Q).to-=z[0].length,Q.push(new t(0,u-z[0].length,u)))),\"rtl\"==r?Q.reverse():Q}}(),ss=[],ls=function(e,t,n){if(e.addEventListener)e.addEventListener(t,n,!1);else if(e.attachEvent)e.attachEvent(\"on\"+t,n);else{var r=e._handlers||(e._handlers={});r[t]=(r[t]||ss).concat(n)}},cs=function(){if(Na&&9>Ia)return!1;var e=r(\"div\");return\"draggable\"in e||\"dragDrop\"in e}(),us=3!=\"\\n\\nb\".split(/\\n/).length?function(e){for(var t=0,n=[],r=e.length;r>=t;){var o=e.indexOf(\"\\n\",t);-1==o&&(o=e.length);var i=e.slice(t,\"\\r\"==e.charAt(o-1)?o-1:o),a=i.indexOf(\"\\r\");-1!=a?(n.push(i.slice(0,a)),t+=a+1):(n.push(i),t=o+1)}return n}:function(e){return e.split(/\\r\\n?|\\n/)},ds=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(t){return!1}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(n){}return t&&t.parentElement()==e?0!=t.compareEndPoints(\"StartToEnd\",t):!1},ps=function(){var e=r(\"div\");return\"oncopy\"in e?!0:(e.setAttribute(\"oncopy\",\"return;\"),\"function\"==typeof e.oncopy)}(),hs=null,gs={},fs={},ms={},As=function(e,t,n){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=n};As.prototype.eol=function(){return this.pos>=this.string.length},As.prototype.sol=function(){return this.pos==this.lineStart},As.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},As.prototype.next=function(){return this.pos<this.string.length?this.string.charAt(this.pos++):void 0},As.prototype.eat=function(e){var t,n=this.string.charAt(this.pos);return t=\"string\"==typeof e?n==e:n&&(e.test?e.test(n):e(n)),t?(++this.pos,n):void 0},As.prototype.eatWhile=function(e){for(var t=this.pos;this.eat(e););return this.pos>t},As.prototype.eatSpace=function(){for(var e=this.pos;/[\\s\\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},As.prototype.skipToEnd=function(){this.pos=this.string.length},As.prototype.skipTo=function(e){var t=this.string.indexOf(e,this.pos);return t>-1?(this.pos=t,!0):void 0},As.prototype.backUp=function(e){this.pos-=e},As.prototype.column=function(){return this.lastColumnPos<this.start&&(this.lastColumnValue=f(this.string,this.start,this.tabSize,this.lastColumnPos,this.lastColumnValue),this.lastColumnPos=this.start),this.lastColumnValue-(this.lineStart?f(this.string,this.lineStart,this.tabSize):0)},As.prototype.indentation=function(){return f(this.string,null,this.tabSize)-(this.lineStart?f(this.string,this.lineStart,this.tabSize):0)},As.prototype.match=function(e,t,n){if(\"string\"!=typeof e){var r=this.string.slice(this.pos).match(e);return r&&r.index>0?null:(r&&t!==!1&&(this.pos+=r[0].length),r)}var o=function(e){return n?e.toLowerCase():e},i=this.string.substr(this.pos,e.length);return o(i)==o(e)?(t!==!1&&(this.pos+=e.length),!0):void 0},As.prototype.current=function(){return this.string.slice(this.start,this.pos)},As.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}},As.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)},As.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};var Ms=function(e,t){this.state=e,this.lookAhead=t},ws=function(e,t,n,r){this.state=t,this.doc=e,this.line=n,this.maxLookAhead=r||0,this.baseTokens=null,this.baseTokenPos=1};ws.prototype.lookAhead=function(e){var t=this.doc.getLine(this.line+e);return null!=t&&e>this.maxLookAhead&&(this.maxLookAhead=e),t},ws.prototype.baseToken=function(e){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=e;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,\"\"),size:this.baseTokens[this.baseTokenPos]-e}},ws.prototype.nextLine=function(){this.line++,this.maxLookAhead>0&&this.maxLookAhead--},ws.fromSaved=function(e,t,n){return t instanceof Ms?new ws(e,ee(e.mode,t.state),n,t.lookAhead):new ws(e,ee(e.mode,t),n)},ws.prototype.save=function(e){var t=e!==!1?ee(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new Ms(t,this.maxLookAhead):t};var vs=function(e,t,n){this.start=e.start,this.end=e.pos,this.string=e.current(),this.type=t||null,this.state=n},bs=!1,ys=!1,xs=function(e,t,n){this.text=e,Ye(this,t),this.height=n?n(this):1};xs.prototype.lineNo=function(){return se(this)},O(xs);var Ts,Cs={},Ns={},Is=null,Es=null,Ds={left:0,right:0,top:0,bottom:0},Ss=function(e,t,n){this.cm=n;var o=this.vert=r(\"div\",[r(\"div\",null,null,\"min-width: 1px\")],\"CodeMirror-vscrollbar\"),i=this.horiz=r(\"div\",[r(\"div\",null,null,\"height: 100%; min-height: 1px\")],\"CodeMirror-hscrollbar\");o.tabIndex=i.tabIndex=-1,e(o),e(i),ls(o,\"scroll\",function(){o.clientHeight&&t(o.scrollTop,\"vertical\")}),ls(i,\"scroll\",function(){i.clientWidth&&t(i.scrollLeft,\"horizontal\")}),this.checkedZeroWidth=!1,Na&&8>Ia&&(this.horiz.style.minHeight=this.vert.style.minWidth=\"18px\")};Ss.prototype.update=function(e){var t=e.scrollWidth>e.clientWidth+1,n=e.scrollHeight>e.clientHeight+1,r=e.nativeBarWidth;if(n){this.vert.style.display=\"block\",this.vert.style.bottom=t?r+\"px\":\"0\";var o=e.viewHeight-(t?r:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+o)+\"px\"}else this.vert.scrollTop=0,this.vert.style.display=\"\",this.vert.firstChild.style.height=\"0\";if(t){this.horiz.style.display=\"block\",this.horiz.style.right=n?r+\"px\":\"0\",this.horiz.style.left=e.barLeft+\"px\";var i=e.viewWidth-e.barLeft-(n?r:0);this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+i)+\"px\"}else this.horiz.style.display=\"\",this.horiz.firstChild.style.width=\"0\";return!this.checkedZeroWidth&&e.clientHeight>0&&(0==r&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:n?r:0,bottom:t?r:0}},Ss.prototype.setScrollLeft=function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,\"horiz\")},Ss.prototype.setScrollTop=function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,\"vert\")},Ss.prototype.zeroWidthHack=function(){var e=Oa&&!Ua?\"12px\":\"18px\";this.horiz.style.height=this.vert.style.width=e,this.horiz.style.visibility=this.vert.style.visibility=\"hidden\",this.disableHoriz=new _a,this.disableVert=new _a},Ss.prototype.enableZeroWidthBar=function(e,t,n){function r(){var o=e.getBoundingClientRect(),i=\"vert\"==n?document.elementFromPoint(o.right-1,(o.top+o.bottom)/2):document.elementFromPoint((o.right+o.left)/2,o.bottom-1);i!=e?e.style.visibility=\"hidden\":t.set(1e3,r)}e.style.visibility=\"\",t.set(1e3,r)},Ss.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)};var Ls=function(){};Ls.prototype.update=function(){return{bottom:0,right:0}},Ls.prototype.setScrollLeft=function(){},Ls.prototype.setScrollTop=function(){},Ls.prototype.clear=function(){};var ks={\"native\":Ss,\"null\":Ls},js=0,Us=function(e,t,n){var r=e.display;this.viewport=t,this.visible=qn(r,e.doc,t),this.editorIsHidden=!r.wrapper.offsetWidth,this.wrapperHeight=r.wrapper.clientHeight,this.wrapperWidth=r.wrapper.clientWidth,this.oldDisplayWidth=Yt(e),this.force=n,this.dims=Cn(e),this.events=[]};Us.prototype.signal=function(e,t){Q(e,t)&&this.events.push(arguments)},Us.prototype.finish=function(){for(var e=0;e<this.events.length;e++)B.apply(null,this.events[e])};var Bs=0,Rs=null;Na?Rs=-.53:ya?Rs=15:Sa?Rs=-.7:ja&&(Rs=-1/3);var zs=function(e,t){this.ranges=e,this.primIndex=t};zs.prototype.primary=function(){return this.ranges[this.primIndex]},zs.prototype.equals=function(e){if(e==this)return!0;if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length)return!1;for(var t=0;t<this.ranges.length;t++){var n=this.ranges[t],r=e.ranges[t];if(!he(n.anchor,r.anchor)||!he(n.head,r.head))return!1}return!0},zs.prototype.deepCopy=function(){for(var e=[],t=0;t<this.ranges.length;t++)e[t]=new Qs(ge(this.ranges[t].anchor),ge(this.ranges[t].head));return new zs(e,this.primIndex)},zs.prototype.somethingSelected=function(){for(var e=0;e<this.ranges.length;e++)if(!this.ranges[e].empty())return!0;return!1},zs.prototype.contains=function(e,t){t||(t=e);for(var n=0;n<this.ranges.length;n++){var r=this.ranges[n];if(pe(t,r.from())>=0&&pe(e,r.to())<=0)return n}return-1};var Qs=function(e,t){this.anchor=e,this.head=t};Qs.prototype.from=function(){return me(this.anchor,this.head)},Qs.prototype.to=function(){return fe(this.anchor,this.head)},Qs.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch},_o.prototype={chunkSize:function(){return this.lines.length},removeInner:function(e,t){for(var n=e,r=e+t;r>n;++n){var o=this.lines[n];this.height-=o.height,ut(o),Tt(o,\"delete\")}this.lines.splice(e,t)},collapse:function(e){e.push.apply(e,this.lines)},insertInner:function(e,t,n){this.height+=n,this.lines=this.lines.slice(0,e).concat(t).concat(this.lines.slice(e));for(var r=0;r<t.length;++r)t[r].parent=this},iterN:function(e,t,n){for(var r=e+t;r>e;++e)if(n(this.lines[e]))return!0}},qo.prototype={chunkSize:function(){return this.size},removeInner:function(e,t){this.size-=t;for(var n=0;n<this.children.length;++n){var r=this.children[n],o=r.chunkSize();if(o>e){var i=Math.min(t,o-e),a=r.height;if(r.removeInner(e,i),this.height-=a-r.height,o==i&&(this.children.splice(n--,1),r.parent=null),0==(t-=i))break;e=0}else e-=o}if(this.size-t<25&&(this.children.length>1||!(this.children[0]instanceof _o))){var s=[];this.collapse(s),this.children=[new _o(s)],this.children[0].parent=this}},collapse:function(e){for(var t=0;t<this.children.length;++t)this.children[t].collapse(e)},insertInner:function(e,t,n){this.size+=t.length,this.height+=n;for(var r=0;r<this.children.length;++r){var o=this.children[r],i=o.chunkSize();if(i>=e){if(o.insertInner(e,t,n),o.lines&&o.lines.length>50){for(var a=o.lines.length%25+25,s=a;s<o.lines.length;){var l=new _o(o.lines.slice(s,s+=25));o.height-=l.height,this.children.splice(++r,0,l),l.parent=this}o.lines=o.lines.slice(0,a),this.maybeSpill()}break}e-=i}},maybeSpill:function(){if(!(this.children.length<=10)){var e=this;do{var t=e.children.splice(e.children.length-5,5),n=new qo(t);if(e.parent){e.size-=n.size,e.height-=n.height;var r=m(e.parent.children,e);e.parent.children.splice(r+1,0,n)}else{var o=new qo(e.children);o.parent=e,e.children=[o,n],e=o}n.parent=e.parent}while(e.children.length>10);e.parent.maybeSpill()}},iterN:function(e,t,n){for(var r=0;r<this.children.length;++r){var o=this.children[r],i=o.chunkSize();if(i>e){var a=Math.min(t,i-e);if(o.iterN(e,a,n))return!0;if(0==(t-=a))break;e=0}else e-=i}}};var Os=function(e,t,n){if(n)for(var r in n)n.hasOwnProperty(r)&&(this[r]=n[r]);this.doc=e,this.node=t};Os.prototype.clear=function(){var e=this.doc.cm,t=this.line.widgets,n=this.line,r=se(n);if(null!=r&&t){for(var o=0;o<t.length;++o)t[o]==this&&t.splice(o--,1);t.length||(n.widgets=null);var i=Qt(this);ae(n,Math.max(0,n.height-i)),e&&(br(e,function(){Jo(e,n,-i),kn(e,r,\"widget\")}),Tt(e,\"lineWidgetCleared\",e,this,r))}},Os.prototype.changed=function(){var e=this,t=this.height,n=this.doc.cm,r=this.line;this.height=null;var o=Qt(this)-t;o&&(ot(this.doc,r)||ae(r,r.height+o),n&&br(n,function(){n.curOp.forceUpdate=!0,Jo(n,r,o),Tt(n,\"lineWidgetChanged\",n,e,se(r))}))},O(Os);var Hs=0,Fs=function(e,t){this.lines=[],this.type=t,this.doc=e,this.id=++Hs};Fs.prototype.clear=function(){if(!this.explicitlyCleared){var e=this.doc.cm,t=e&&!e.curOp;if(t&&hr(e),Q(this,\"clear\")){var n=this.find();n&&Tt(this,\"clear\",n.from,n.to)}for(var r=null,o=null,i=0;i<this.lines.length;++i){var a=this.lines[i],s=Be(a.markedSpans,this);e&&!this.collapsed?kn(e,se(a),\"text\"):e&&(null!=s.to&&(o=se(a)),null!=s.from&&(r=se(a))),a.markedSpans=Re(a.markedSpans,s),null==s.from&&this.collapsed&&!ot(this.doc,a)&&e&&ae(a,xn(e.display))}if(e&&this.collapsed&&!e.options.lineWrapping)for(var l=0;l<this.lines.length;++l){var c=$e(this.lines[l]),u=st(c);u>e.display.maxLineLength&&(e.display.maxLine=c,e.display.maxLineLength=u,e.display.maxLineChanged=!0)}null!=r&&e&&this.collapsed&&Ln(e,r,o+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,e&&So(e.doc)),e&&Tt(e,\"markerCleared\",e,this,r,o),t&&gr(e),this.parent&&this.parent.clear()}},Fs.prototype.find=function(e,t){null==e&&\"bookmark\"==this.type&&(e=1);for(var n,r,o=0;o<this.lines.length;++o){var i=this.lines[o],a=Be(i.markedSpans,this);if(null!=a.from&&(n=de(t?i:se(i),a.from),-1==e))return n;if(null!=a.to&&(r=de(t?i:se(i),a.to),1==e))return r}return n&&{from:n,to:r}},Fs.prototype.changed=function(){var e=this,t=this.find(-1,!0),n=this,r=this.doc.cm;t&&r&&br(r,function(){var o=t.line,i=se(t.line),a=Jt(r,i);if(a&&(rn(a),r.curOp.selectionChanged=r.curOp.forceUpdate=!0),r.curOp.updateMaxLine=!0,!ot(n.doc,o)&&null!=n.height){var s=n.height;n.height=null;var l=Qt(n)-s;l&&ae(o,o.height+l)}Tt(r,\"markerChanged\",r,e)})},Fs.prototype.attachLine=function(e){if(!this.lines.length&&this.doc.cm){var t=this.doc.cm.curOp;t.maybeHiddenMarkers&&-1!=m(t.maybeHiddenMarkers,this)||(t.maybeUnhiddenMarkers||(t.maybeUnhiddenMarkers=[])).push(this)}this.lines.push(e)},Fs.prototype.detachLine=function(e){if(this.lines.splice(m(this.lines,e),1),!this.lines.length&&this.doc.cm){var t=this.doc.cm.curOp;(t.maybeHiddenMarkers||(t.maybeHiddenMarkers=[])).push(this)}},O(Fs);var Vs=function(e,t){this.markers=e,this.primary=t;for(var n=0;n<e.length;++n)e[n].parent=this};Vs.prototype.clear=function(){if(!this.explicitlyCleared){this.explicitlyCleared=!0;for(var e=0;e<this.markers.length;++e)this.markers[e].clear();Tt(this,\"clear\")}},Vs.prototype.find=function(e,t){return this.primary.find(e,t)},O(Vs);var Ps=0,Ys=function(e,t,n,r,o){if(!(this instanceof Ys))return new Ys(e,t,n,r,o);null==n&&(n=0),qo.call(this,[new _o([new xs(\"\",null)])]),this.first=n,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.cleanGeneration=1,this.modeFrontier=this.highlightFrontier=n;var i=de(n,0);this.sel=Wr(i),this.history=new ao(null),this.id=++Ps,this.modeOption=t,this.lineSep=r,this.direction=\"rtl\"==o?\"rtl\":\"ltr\",this.extend=!1,\"string\"==typeof e&&(e=this.splitLines(e)),to(this,{from:i,to:i,text:e}),Io(this,Wr(i),$a)};Ys.prototype=x(qo.prototype,{constructor:Ys,iter:function(e,t,n){n?this.iterN(e-this.first,t-e,n):this.iterN(this.first,this.first+this.size,e)},insert:function(e,t){for(var n=0,r=0;r<t.length;++r)n+=t[r].height;this.insertInner(e-this.first,t,n)},remove:function(e,t){this.removeInner(e-this.first,t)},getValue:function(e){var t=ie(this,this.first,this.first+this.size);return e===!1?t:t.join(e||this.lineSeparator())},setValue:Tr(function(e){var t=de(this.first,0),n=this.first+this.size-1;zo(this,{from:t,to:de(n,re(this,n).text.length),text:this.splitLines(e),origin:\"setValue\",full:!0},!0),this.cm&&nr(this.cm,0,0),Io(this,Wr(t),$a)}),replaceRange:function(e,t,n,r){t=Me(this,t),n=n?Me(this,n):t,Po(this,e,t,n,r)},getRange:function(e,t,n){var r=oe(this,Me(this,e),Me(this,t));return n===!1?r:\"\"===n?r.join(\"\"):r.join(n||this.lineSeparator())},getLine:function(e){var t=this.getLineHandle(e);return t&&t.text},getLineHandle:function(e){return ce(this,e)?re(this,e):void 0},getLineNumber:function(e){return se(e)},getLineHandleVisualStart:function(e){return\"number\"==typeof e&&(e=re(this,e)),$e(e)},lineCount:function(){return this.size},firstLine:function(){return this.first},lastLine:function(){return this.first+this.size-1},clipPos:function(e){return Me(this,e)},getCursor:function(e){var t,n=this.sel.primary();return t=null==e||\"head\"==e?n.head:\"anchor\"==e?n.anchor:\"end\"==e||\"to\"==e||e===!1?n.to():n.from()},listSelections:function(){return this.sel.ranges},somethingSelected:function(){return this.sel.somethingSelected()},setCursor:Tr(function(e,t,n){To(this,Me(this,\"number\"==typeof e?de(e,t||0):e),null,n)}),setSelection:Tr(function(e,t,n){To(this,Me(this,e),Me(this,t||e),n)}),extendSelection:Tr(function(e,t,n){bo(this,Me(this,e),t&&Me(this,t),n)}),extendSelections:Tr(function(e,t){yo(this,ve(this,e),t)}),extendSelectionsBy:Tr(function(e,t){var n=v(this.sel.ranges,e);yo(this,ve(this,n),t)}),setSelections:Tr(function(e,t,n){if(e.length){for(var r=[],o=0;o<e.length;o++)r[o]=new Qs(Me(this,e[o].anchor),Me(this,e[o].head||e[o].anchor));null==t&&(t=Math.min(e.length-1,this.sel.primIndex)),Io(this,Gr(this.cm,r,t),n)}}),addSelection:Tr(function(e,t,n){var r=this.sel.ranges.slice(0);r.push(new Qs(Me(this,e),Me(this,t||e))),Io(this,Gr(this.cm,r,r.length-1),n)}),getSelection:function(e){for(var t,n=this.sel.ranges,r=0;r<n.length;r++){var o=oe(this,n[r].from(),n[r].to());t=t?t.concat(o):o}return e===!1?t:t.join(e||this.lineSeparator())},getSelections:function(e){for(var t=[],n=this.sel.ranges,r=0;r<n.length;r++){var o=oe(this,n[r].from(),n[r].to());e!==!1&&(o=o.join(e||this.lineSeparator())),t[r]=o}return t},replaceSelection:function(e,t,n){for(var r=[],o=0;o<this.sel.ranges.length;o++)r[o]=e;this.replaceSelections(r,t,n||\"+input\")},replaceSelections:Tr(function(e,t,n){for(var r=[],o=this.sel,i=0;i<o.ranges.length;i++){var a=o.ranges[i];r[i]={from:a.from(),to:a.to(),text:this.splitLines(e[i]),origin:n}}for(var s=t&&\"end\"!=t&&Kr(this,r,t),l=r.length-1;l>=0;l--)zo(this,r[l]);s?No(this,s):this.cm&&tr(this.cm)}),undo:Tr(function(){Oo(this,\"undo\")}),redo:Tr(function(){Oo(this,\"redo\")}),undoSelection:Tr(function(){Oo(this,\"undo\",!0)}),redoSelection:Tr(function(){Oo(this,\"redo\",!0)}),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,n=0,r=0;r<e.done.length;r++)e.done[r].ranges||++t;for(var o=0;o<e.undone.length;o++)e.undone[o].ranges||++n;return{undo:t,redo:n}},clearHistory:function(){var e=this;this.history=new ao(this.history),no(this,function(t){return t.history=e.history},!0)},markClean:function(){this.cleanGeneration=this.changeGeneration(!0)},changeGeneration:function(e){return e&&(this.history.lastOp=this.history.lastSelOp=this.history.lastOrigin=null),this.history.generation},isClean:function(e){return this.history.generation==(e||this.cleanGeneration)},getHistory:function(){return{done:wo(this.history.done),undone:wo(this.history.undone)}},setHistory:function(e){\nvar t=this.history=new ao(this.history);t.done=wo(e.done.slice(0),null,!0),t.undone=wo(e.undone.slice(0),null,!0)},setGutterMarker:Tr(function(e,t,n){return Xo(this,e,\"gutter\",function(e){var r=e.gutterMarkers||(e.gutterMarkers={});return r[t]=n,!n&&N(r)&&(e.gutterMarkers=null),!0})}),clearGutter:Tr(function(e){var t=this;this.iter(function(n){n.gutterMarkers&&n.gutterMarkers[e]&&Xo(t,n,\"gutter\",function(){return n.gutterMarkers[e]=null,N(n.gutterMarkers)&&(n.gutterMarkers=null),!0})})}),lineInfo:function(e){var t;if(\"number\"==typeof e){if(!ce(this,e))return null;if(t=e,e=re(this,e),!e)return null}else if(t=se(e),null==t)return null;return{line:t,handle:e,text:e.text,gutterMarkers:e.gutterMarkers,textClass:e.textClass,bgClass:e.bgClass,wrapClass:e.wrapClass,widgets:e.widgets}},addLineClass:Tr(function(t,n,r){return Xo(this,t,\"gutter\"==n?\"gutter\":\"class\",function(t){var o=\"text\"==n?\"textClass\":\"background\"==n?\"bgClass\":\"gutter\"==n?\"gutterClass\":\"wrapClass\";if(t[o]){if(e(r).test(t[o]))return!1;t[o]+=\" \"+r}else t[o]=r;return!0})}),removeLineClass:Tr(function(t,n,r){return Xo(this,t,\"gutter\"==n?\"gutter\":\"class\",function(t){var o=\"text\"==n?\"textClass\":\"background\"==n?\"bgClass\":\"gutter\"==n?\"gutterClass\":\"wrapClass\",i=t[o];if(!i)return!1;if(null==r)t[o]=null;else{var a=i.match(e(r));if(!a)return!1;var s=a.index+a[0].length;t[o]=i.slice(0,a.index)+(a.index&&s!=i.length?\" \":\"\")+i.slice(s)||null}return!0})}),addLineWidget:Tr(function(e,t,n){return Ko(this,e,t,n)}),removeLineWidget:function(e){e.clear()},markText:function(e,t,n){return Zo(this,Me(this,e),Me(this,t),n,n&&n.type||\"range\")},setBookmark:function(e,t){var n={replacedWith:t&&(null==t.nodeType?t.widget:t),insertLeft:t&&t.insertLeft,clearWhenEmpty:!1,shared:t&&t.shared,handleMouseEvents:t&&t.handleMouseEvents};return e=Me(this,e),Zo(this,e,e,n,\"bookmark\")},findMarksAt:function(e){e=Me(this,e);var t=[],n=re(this,e.line).markedSpans;if(n)for(var r=0;r<n.length;++r){var o=n[r];(null==o.from||o.from<=e.ch)&&(null==o.to||o.to>=e.ch)&&t.push(o.marker.parent||o.marker)}return t},findMarks:function(e,t,n){e=Me(this,e),t=Me(this,t);var r=[],o=e.line;return this.iter(e.line,t.line+1,function(i){var a=i.markedSpans;if(a)for(var s=0;s<a.length;s++){var l=a[s];null!=l.to&&o==e.line&&e.ch>=l.to||null==l.from&&o!=e.line||null!=l.from&&o==t.line&&l.from>=t.ch||n&&!n(l.marker)||r.push(l.marker.parent||l.marker)}++o}),r},getAllMarks:function(){var e=[];return this.iter(function(t){var n=t.markedSpans;if(n)for(var r=0;r<n.length;++r)null!=n[r].from&&e.push(n[r].marker)}),e},posFromIndex:function(e){var t,n=this.first,r=this.lineSeparator().length;return this.iter(function(o){var i=o.text.length+r;return i>e?(t=e,!0):(e-=i,void++n)}),Me(this,de(n,t))},indexFromPos:function(e){e=Me(this,e);var t=e.ch;if(e.line<this.first||e.ch<0)return 0;var n=this.lineSeparator().length;return this.iter(this.first,e.line,function(e){t+=e.text.length+n}),t},copy:function(e){var t=new Ys(ie(this,this.first,this.first+this.size),this.modeOption,this.first,this.lineSep,this.direction);return t.scrollTop=this.scrollTop,t.scrollLeft=this.scrollLeft,t.sel=this.sel,t.extend=!1,e&&(t.history.undoDepth=this.history.undoDepth,t.setHistory(this.getHistory())),t},linkedDoc:function(e){e||(e={});var t=this.first,n=this.first+this.size;null!=e.from&&e.from>t&&(t=e.from),null!=e.to&&e.to<n&&(n=e.to);var r=new Ys(ie(this,t,n),e.mode||this.modeOption,t,this.lineSep,this.direction);return e.sharedHist&&(r.history=this.history),(this.linked||(this.linked=[])).push({doc:r,sharedHist:e.sharedHist}),r.linked=[{doc:this,isParent:!0,sharedHist:e.sharedHist}],ti(r,ei(this)),r},unlinkDoc:function(e){if(e instanceof $i&&(e=e.doc),this.linked)for(var t=0;t<this.linked.length;++t){var n=this.linked[t];if(n.doc==e){this.linked.splice(t,1),e.unlinkDoc(this),ni(ei(this));break}}if(e.history==this.history){var r=[e.id];no(e,function(e){return r.push(e.id)},!0),e.history=new ao(null),e.history.done=wo(this.history.done,r),e.history.undone=wo(this.history.undone,r)}},iterLinkedDocs:function(e){no(this,e)},getMode:function(){return this.mode},getEditor:function(){return this.cm},splitLines:function(e){return this.lineSep?e.split(this.lineSep):us(e)},lineSeparator:function(){return this.lineSep||\"\\n\"},setDirection:Tr(function(e){\"rtl\"!=e&&(e=\"ltr\"),e!=this.direction&&(this.direction=e,this.iter(function(e){return e.order=null}),this.cm&&io(this.cm))})}),Ys.prototype.eachLine=Ys.prototype.iter;for(var Gs=0,Ws=!1,Xs={3:\"Pause\",8:\"Backspace\",9:\"Tab\",13:\"Enter\",16:\"Shift\",17:\"Ctrl\",18:\"Alt\",19:\"Pause\",20:\"CapsLock\",27:\"Esc\",32:\"Space\",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"Left\",38:\"Up\",39:\"Right\",40:\"Down\",44:\"PrintScrn\",45:\"Insert\",46:\"Delete\",59:\";\",61:\"=\",91:\"Mod\",92:\"Mod\",93:\"Mod\",106:\"*\",107:\"=\",109:\"-\",110:\".\",111:\"/\",145:\"ScrollLock\",173:\"-\",186:\";\",187:\"=\",188:\",\",189:\"-\",190:\".\",191:\"/\",192:\"`\",219:\"[\",220:\"\\\\\",221:\"]\",222:\"'\",224:\"Mod\",63232:\"Up\",63233:\"Down\",63234:\"Left\",63235:\"Right\",63272:\"Delete\",63273:\"Home\",63275:\"End\",63276:\"PageUp\",63277:\"PageDown\",63302:\"Insert\"},_s=0;10>_s;_s++)Xs[_s+48]=Xs[_s+96]=String(_s);for(var qs=65;90>=qs;qs++)Xs[qs]=String.fromCharCode(qs);for(var Js=1;12>=Js;Js++)Xs[Js+111]=Xs[Js+63235]=\"F\"+Js;var Ks={};Ks.basic={Left:\"goCharLeft\",Right:\"goCharRight\",Up:\"goLineUp\",Down:\"goLineDown\",End:\"goLineEnd\",Home:\"goLineStartSmart\",PageUp:\"goPageUp\",PageDown:\"goPageDown\",Delete:\"delCharAfter\",Backspace:\"delCharBefore\",\"Shift-Backspace\":\"delCharBefore\",Tab:\"defaultTab\",\"Shift-Tab\":\"indentAuto\",Enter:\"newlineAndIndent\",Insert:\"toggleOverwrite\",Esc:\"singleSelection\"},Ks.pcDefault={\"Ctrl-A\":\"selectAll\",\"Ctrl-D\":\"deleteLine\",\"Ctrl-Z\":\"undo\",\"Shift-Ctrl-Z\":\"redo\",\"Ctrl-Y\":\"redo\",\"Ctrl-Home\":\"goDocStart\",\"Ctrl-End\":\"goDocEnd\",\"Ctrl-Up\":\"goLineUp\",\"Ctrl-Down\":\"goLineDown\",\"Ctrl-Left\":\"goGroupLeft\",\"Ctrl-Right\":\"goGroupRight\",\"Alt-Left\":\"goLineStart\",\"Alt-Right\":\"goLineEnd\",\"Ctrl-Backspace\":\"delGroupBefore\",\"Ctrl-Delete\":\"delGroupAfter\",\"Ctrl-S\":\"save\",\"Ctrl-F\":\"find\",\"Ctrl-G\":\"findNext\",\"Shift-Ctrl-G\":\"findPrev\",\"Shift-Ctrl-F\":\"replace\",\"Shift-Ctrl-R\":\"replaceAll\",\"Ctrl-[\":\"indentLess\",\"Ctrl-]\":\"indentMore\",\"Ctrl-U\":\"undoSelection\",\"Shift-Ctrl-U\":\"redoSelection\",\"Alt-U\":\"redoSelection\",fallthrough:\"basic\"},Ks.emacsy={\"Ctrl-F\":\"goCharRight\",\"Ctrl-B\":\"goCharLeft\",\"Ctrl-P\":\"goLineUp\",\"Ctrl-N\":\"goLineDown\",\"Ctrl-A\":\"goLineStart\",\"Ctrl-E\":\"goLineEnd\",\"Ctrl-V\":\"goPageDown\",\"Shift-Ctrl-V\":\"goPageUp\",\"Ctrl-D\":\"delCharAfter\",\"Ctrl-H\":\"delCharBefore\",\"Alt-Backspace\":\"delWordBefore\",\"Ctrl-K\":\"killLine\",\"Ctrl-T\":\"transposeChars\",\"Ctrl-O\":\"openLine\"},Ks.macDefault={\"Cmd-A\":\"selectAll\",\"Cmd-D\":\"deleteLine\",\"Cmd-Z\":\"undo\",\"Shift-Cmd-Z\":\"redo\",\"Cmd-Y\":\"redo\",\"Cmd-Home\":\"goDocStart\",\"Cmd-Up\":\"goDocStart\",\"Cmd-End\":\"goDocEnd\",\"Cmd-Down\":\"goDocEnd\",\"Alt-Left\":\"goGroupLeft\",\"Alt-Right\":\"goGroupRight\",\"Cmd-Left\":\"goLineLeft\",\"Cmd-Right\":\"goLineRight\",\"Alt-Backspace\":\"delGroupBefore\",\"Ctrl-Alt-Backspace\":\"delGroupAfter\",\"Alt-Delete\":\"delGroupAfter\",\"Cmd-S\":\"save\",\"Cmd-F\":\"find\",\"Cmd-G\":\"findNext\",\"Shift-Cmd-G\":\"findPrev\",\"Cmd-Alt-F\":\"replace\",\"Shift-Cmd-Alt-F\":\"replaceAll\",\"Cmd-[\":\"indentLess\",\"Cmd-]\":\"indentMore\",\"Cmd-Backspace\":\"delWrappedLineLeft\",\"Cmd-Delete\":\"delWrappedLineRight\",\"Cmd-U\":\"undoSelection\",\"Shift-Cmd-U\":\"redoSelection\",\"Ctrl-Up\":\"goDocStart\",\"Ctrl-Down\":\"goDocEnd\",fallthrough:[\"basic\",\"emacsy\"]},Ks[\"default\"]=Oa?Ks.macDefault:Ks.pcDefault;var Zs={selectAll:Bo,singleSelection:function(e){return e.setSelection(e.getCursor(\"anchor\"),e.getCursor(\"head\"),$a)},killLine:function(e){return Mi(e,function(t){if(t.empty()){var n=re(e.doc,t.head.line).text.length;return t.head.ch==n&&t.head.line<e.lastLine()?{from:t.head,to:de(t.head.line+1,0)}:{from:t.head,to:de(t.head.line,n)}}return{from:t.from(),to:t.to()}})},deleteLine:function(e){return Mi(e,function(t){return{from:de(t.from().line,0),to:Me(e.doc,de(t.to().line+1,0))}})},delLineLeft:function(e){return Mi(e,function(e){return{from:de(e.from().line,0),to:e.from()}})},delWrappedLineLeft:function(e){return Mi(e,function(t){var n=e.charCoords(t.head,\"div\").top+5,r=e.coordsChar({left:0,top:n},\"div\");return{from:r,to:t.from()}})},delWrappedLineRight:function(e){return Mi(e,function(t){var n=e.charCoords(t.head,\"div\").top+5,r=e.coordsChar({left:e.display.lineDiv.offsetWidth+100,top:n},\"div\");return{from:t.from(),to:r}})},undo:function(e){return e.undo()},redo:function(e){return e.redo()},undoSelection:function(e){return e.undoSelection()},redoSelection:function(e){return e.redoSelection()},goDocStart:function(e){return e.extendSelection(de(e.firstLine(),0))},goDocEnd:function(e){return e.extendSelection(de(e.lastLine()))},goLineStart:function(e){return e.extendSelectionsBy(function(t){return xi(e,t.head.line)},{origin:\"+move\",bias:1})},goLineStartSmart:function(e){return e.extendSelectionsBy(function(t){return Ci(e,t.head)},{origin:\"+move\",bias:1})},goLineEnd:function(e){return e.extendSelectionsBy(function(t){return Ti(e,t.head.line)},{origin:\"+move\",bias:-1})},goLineRight:function(e){return e.extendSelectionsBy(function(t){var n=e.cursorCoords(t.head,\"div\").top+5;return e.coordsChar({left:e.display.lineDiv.offsetWidth+100,top:n},\"div\")},ts)},goLineLeft:function(e){return e.extendSelectionsBy(function(t){var n=e.cursorCoords(t.head,\"div\").top+5;return e.coordsChar({left:0,top:n},\"div\")},ts)},goLineLeftSmart:function(e){return e.extendSelectionsBy(function(t){var n=e.cursorCoords(t.head,\"div\").top+5,r=e.coordsChar({left:0,top:n},\"div\");return r.ch<e.getLine(r.line).search(/\\S/)?Ci(e,t.head):r},ts)},goLineUp:function(e){return e.moveV(-1,\"line\")},goLineDown:function(e){return e.moveV(1,\"line\")},goPageUp:function(e){return e.moveV(-1,\"page\")},goPageDown:function(e){return e.moveV(1,\"page\")},goCharLeft:function(e){return e.moveH(-1,\"char\")},goCharRight:function(e){return e.moveH(1,\"char\")},goColumnLeft:function(e){return e.moveH(-1,\"column\")},goColumnRight:function(e){return e.moveH(1,\"column\")},goWordLeft:function(e){return e.moveH(-1,\"word\")},goGroupRight:function(e){return e.moveH(1,\"group\")},goGroupLeft:function(e){return e.moveH(-1,\"group\")},goWordRight:function(e){return e.moveH(1,\"word\")},delCharBefore:function(e){return e.deleteH(-1,\"codepoint\")},delCharAfter:function(e){return e.deleteH(1,\"char\")},delWordBefore:function(e){return e.deleteH(-1,\"word\")},delWordAfter:function(e){return e.deleteH(1,\"word\")},delGroupBefore:function(e){return e.deleteH(-1,\"group\")},delGroupAfter:function(e){return e.deleteH(1,\"group\")},indentAuto:function(e){return e.indentSelection(\"smart\")},indentMore:function(e){return e.indentSelection(\"add\")},indentLess:function(e){return e.indentSelection(\"subtract\")},insertTab:function(e){return e.replaceSelection(\"\t\")},insertSoftTab:function(e){for(var t=[],n=e.listSelections(),r=e.options.tabSize,o=0;o<n.length;o++){var i=n[o].from(),a=f(e.getLine(i.line),i.ch,r);t.push(M(r-a%r))}e.replaceSelections(t)},defaultTab:function(e){e.somethingSelected()?e.indentSelection(\"add\"):e.execCommand(\"insertTab\")},transposeChars:function(e){return br(e,function(){for(var t=e.listSelections(),n=[],r=0;r<t.length;r++)if(t[r].empty()){var o=t[r].head,i=re(e.doc,o.line).text;if(i)if(o.ch==i.length&&(o=new de(o.line,o.ch-1)),o.ch>0)o=new de(o.line,o.ch+1),e.replaceRange(i.charAt(o.ch-1)+i.charAt(o.ch-2),de(o.line,o.ch-2),o,\"+transpose\");else if(o.line>e.doc.first){var a=re(e.doc,o.line-1).text;a&&(o=new de(o.line,1),e.replaceRange(i.charAt(0)+e.doc.lineSeparator()+a.charAt(a.length-1),de(o.line-1,a.length-1),o,\"+transpose\"))}n.push(new Qs(o,o))}e.setSelections(n)})},newlineAndIndent:function(e){return br(e,function(){for(var t=e.listSelections(),n=t.length-1;n>=0;n--)e.replaceRange(e.doc.lineSeparator(),t[n].anchor,t[n].head,\"+input\");t=e.listSelections();for(var r=0;r<t.length;r++)e.indentLine(t[r].from().line,null,!0);tr(e)})},openLine:function(e){return e.replaceSelection(\"\\n\",\"start\")},toggleOverwrite:function(e){return e.toggleOverwrite()}},$s=new _a,el=null,tl=400,nl=function(e,t,n){this.time=e,this.pos=t,this.button=n};nl.prototype.compare=function(e,t,n){return this.time+tl>e&&0==pe(t,this.pos)&&n==this.button};var rl,ol,il={toString:function(){return\"CodeMirror.Init\"}},al={},sl={};$i.defaults=al,$i.optionHandlers=sl;var ll=[];$i.defineInitHook=function(e){return ll.push(e)};var cl=null,ul=function(e){this.cm=e,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new _a,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};ul.prototype.init=function(e){function t(e){for(var t=e.target;t;t=t.parentNode){if(t==s)return!0;if(/\\bCodeMirror-(?:line)?widget\\b/.test(t.className))break}return!1}function n(e){if(t(e)&&!R(i,e)){if(i.somethingSelected())na({lineWise:!1,text:i.getSelections()}),\"cut\"==e.type&&i.replaceSelection(\"\",null,\"cut\");else{if(!i.options.lineWiseCopyCut)return;var n=aa(i);na({lineWise:!0,text:n.text}),\"cut\"==e.type&&i.operation(function(){i.setSelections(n.ranges,0,$a),i.replaceSelection(\"\",null,\"cut\")})}if(e.clipboardData){e.clipboardData.clearData();var r=cl.text.join(\"\\n\");if(e.clipboardData.setData(\"Text\",r),e.clipboardData.getData(\"Text\")==r)return void e.preventDefault()}var l=la(),c=l.firstChild;sa(c),i.display.lineSpace.insertBefore(l,i.display.lineSpace.firstChild),c.value=cl.text.join(\"\\n\");var u=a(d(s));Xa(c),setTimeout(function(){i.display.lineSpace.removeChild(l),u.focus(),u==s&&o.showPrimarySelection()},50)}}var r=this,o=this,i=o.cm,s=o.div=e.lineDiv;s.contentEditable=!0,sa(s,i.options.spellcheck,i.options.autocorrect,i.options.autocapitalize),ls(s,\"paste\",function(e){!t(e)||R(i,e)||oa(e,i)||11>=Ia&&setTimeout(yr(i,function(){return r.updateFromDOM()}),20)}),ls(s,\"compositionstart\",function(e){r.composing={data:e.data,done:!1}}),ls(s,\"compositionupdate\",function(e){r.composing||(r.composing={data:e.data,done:!1})}),ls(s,\"compositionend\",function(e){r.composing&&(e.data!=r.composing.data&&r.readFromDOMSoon(),r.composing.done=!0)}),ls(s,\"touchstart\",function(){return o.forceCompositionEnd()}),ls(s,\"input\",function(){r.composing||r.readFromDOMSoon()}),ls(s,\"copy\",n),ls(s,\"cut\",n)},ul.prototype.screenReaderLabelChanged=function(e){e?this.div.setAttribute(\"aria-label\",e):this.div.removeAttribute(\"aria-label\")},ul.prototype.prepareSelection=function(){var e=Qn(this.cm,!1);return e.focus=a(d(this.div))==this.div,e},ul.prototype.showSelection=function(e,t){e&&this.cm.display.view.length&&((e.focus||t)&&this.showPrimarySelection(),this.showMultipleSelections(e))},ul.prototype.getSelection=function(){return this.cm.display.wrapper.ownerDocument.getSelection()},ul.prototype.showPrimarySelection=function(){var e=this.getSelection(),t=this.cm,n=t.doc.sel.primary(),r=n.from(),o=n.to();if(t.display.viewTo==t.display.viewFrom||r.line>=t.display.viewTo||o.line<t.display.viewFrom)return void e.removeAllRanges();var i=ma(t,e.anchorNode,e.anchorOffset),a=ma(t,e.focusNode,e.focusOffset);if(!i||i.bad||!a||a.bad||0!=pe(me(i,a),r)||0!=pe(fe(i,a),o)){var s=t.display.view,l=r.line>=t.display.viewFrom&&pa(t,r)||{node:s[0].measure.map[2],offset:0},c=o.line<t.display.viewTo&&pa(t,o);if(!c){var u=s[s.length-1].measure,d=u.maps?u.maps[u.maps.length-1]:u.map;c={node:d[d.length-1],offset:d[d.length-2]-d[d.length-3]}}if(!l||!c)return void e.removeAllRanges();var p,h=e.rangeCount&&e.getRangeAt(0);try{p=Pa(l.node,l.offset,c.offset,c.node)}catch(g){}p&&(!ya&&t.state.focused?(e.collapse(l.node,l.offset),p.collapsed||(e.removeAllRanges(),e.addRange(p))):(e.removeAllRanges(),e.addRange(p)),h&&null==e.anchorNode?e.addRange(h):ya&&this.startGracePeriod()),this.rememberSelection()}},ul.prototype.startGracePeriod=function(){var e=this;clearTimeout(this.gracePeriod),this.gracePeriod=setTimeout(function(){e.gracePeriod=!1,e.selectionChanged()&&e.cm.operation(function(){return e.cm.curOp.selectionChanged=!0})},20)},ul.prototype.showMultipleSelections=function(e){n(this.cm.display.cursorDiv,e.cursors),n(this.cm.display.selectionDiv,e.selection)},ul.prototype.rememberSelection=function(){var e=this.getSelection();this.lastAnchorNode=e.anchorNode,this.lastAnchorOffset=e.anchorOffset,this.lastFocusNode=e.focusNode,this.lastFocusOffset=e.focusOffset},ul.prototype.selectionInEditor=function(){var e=this.getSelection();if(!e.rangeCount)return!1;var t=e.getRangeAt(0).commonAncestorContainer;return i(this.div,t)},ul.prototype.focus=function(){\"nocursor\"!=this.cm.options.readOnly&&(this.selectionInEditor()&&a(d(this.div))==this.div||this.showSelection(this.prepareSelection(),!0),this.div.focus())},ul.prototype.blur=function(){this.div.blur()},ul.prototype.getField=function(){return this.div},ul.prototype.supportsTouch=function(){return!0},ul.prototype.receivedFocus=function(){function e(){n.cm.state.focused&&(n.pollSelection(),n.polling.set(n.cm.options.pollInterval,e))}var t=this,n=this;this.selectionInEditor()?setTimeout(function(){return t.pollSelection()},20):br(this.cm,function(){return n.cm.curOp.selectionChanged=!0}),this.polling.set(this.cm.options.pollInterval,e)},ul.prototype.selectionChanged=function(){var e=this.getSelection();return e.anchorNode!=this.lastAnchorNode||e.anchorOffset!=this.lastAnchorOffset||e.focusNode!=this.lastFocusNode||e.focusOffset!=this.lastFocusOffset},ul.prototype.pollSelection=function(){if(null==this.readDOMTimeout&&!this.gracePeriod&&this.selectionChanged()){var e=this.getSelection(),t=this.cm;if(za&&Sa&&this.cm.display.gutterSpecs.length&&ha(e.anchorNode))return this.cm.triggerOnKeyDown({type:\"keydown\",keyCode:8,preventDefault:Math.abs}),this.blur(),void this.focus();if(!this.composing){this.rememberSelection();var n=ma(t,e.anchorNode,e.anchorOffset),r=ma(t,e.focusNode,e.focusOffset);n&&r&&br(t,function(){Io(t.doc,Wr(n,r),$a),(n.bad||r.bad)&&(t.curOp.selectionChanged=!0)})}}},ul.prototype.pollContent=function(){null!=this.readDOMTimeout&&(clearTimeout(this.readDOMTimeout),this.readDOMTimeout=null);var e=this.cm,t=e.display,n=e.doc.sel.primary(),r=n.from(),o=n.to();if(0==r.ch&&r.line>e.firstLine()&&(r=de(r.line-1,re(e.doc,r.line-1).length)),o.ch==re(e.doc,o.line).text.length&&o.line<e.lastLine()&&(o=de(o.line+1,0)),r.line<t.viewFrom||o.line>t.viewTo-1)return!1;var i,a,s;r.line==t.viewFrom||0==(i=Sn(e,r.line))?(a=se(t.view[0].line),s=t.view[0].node):(a=se(t.view[i].line),s=t.view[i-1].node.nextSibling);var l,c,u=Sn(e,o.line);if(u==t.view.length-1?(l=t.viewTo-1,c=t.lineDiv.lastChild):(l=se(t.view[u+1].line)-1,c=t.view[u+1].node.previousSibling),!s)return!1;for(var d=e.doc.splitLines(fa(e,s,c,a,l)),p=oe(e.doc,de(a,0),de(l,re(e.doc,l).text.length));d.length>1&&p.length>1;)if(w(d)==w(p))d.pop(),p.pop(),l--;else{if(d[0]!=p[0])break;d.shift(),p.shift(),a++}for(var h=0,g=0,f=d[0],m=p[0],A=Math.min(f.length,m.length);A>h&&f.charCodeAt(h)==m.charCodeAt(h);)++h;for(var M=w(d),v=w(p),b=Math.min(M.length-(1==d.length?h:0),v.length-(1==p.length?h:0));b>g&&M.charCodeAt(M.length-g-1)==v.charCodeAt(v.length-g-1);)++g;if(1==d.length&&1==p.length&&a==r.line)for(;h&&h>r.ch&&M.charCodeAt(M.length-g-1)==v.charCodeAt(v.length-g-1);)h--,g++;d[d.length-1]=M.slice(0,M.length-g).replace(/^\\u200b+/,\"\"),d[0]=d[0].slice(h).replace(/\\u200b+$/,\"\");var y=de(a,h),x=de(l,p.length?w(p).length-g:0);return d.length>1||d[0]||pe(y,x)?(Po(e.doc,d,y,x,\"+input\"),!0):void 0},ul.prototype.ensurePolled=function(){this.forceCompositionEnd()},ul.prototype.reset=function(){this.forceCompositionEnd()},ul.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},ul.prototype.readFromDOMSoon=function(){var e=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout(function(){if(e.readDOMTimeout=null,e.composing){if(!e.composing.done)return;e.composing=null}e.updateFromDOM()},80))},ul.prototype.updateFromDOM=function(){var e=this;(this.cm.isReadOnly()||!this.pollContent())&&br(this.cm,function(){return Ln(e.cm)})},ul.prototype.setUneditable=function(e){e.contentEditable=\"false\"},ul.prototype.onKeyPress=function(e){0==e.charCode||this.composing||(e.preventDefault(),this.cm.isReadOnly()||yr(this.cm,ra)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0))},ul.prototype.readOnlyChanged=function(e){this.div.contentEditable=String(\"nocursor\"!=e)},ul.prototype.onContextMenu=function(){},ul.prototype.resetPosition=function(){},ul.prototype.needsContentAttribute=!0;var dl=function(e){this.cm=e,this.prevInput=\"\",this.pollingFast=!1,this.polling=new _a,this.hasSelection=!1,this.composing=null,this.resetting=!1};dl.prototype.init=function(e){function t(e){if(!R(o,e)){if(o.somethingSelected())na({lineWise:!1,text:o.getSelections()});else{if(!o.options.lineWiseCopyCut)return;var t=aa(o);na({lineWise:!0,text:t.text}),\"cut\"==e.type?o.setSelections(t.ranges,null,$a):(r.prevInput=\"\",i.value=t.text.join(\"\\n\"),Xa(i))}\"cut\"==e.type&&(o.state.cutIncoming=+new Date)}}var n=this,r=this,o=this.cm;this.createField(e);var i=this.textarea;e.wrapper.insertBefore(this.wrapper,e.wrapper.firstChild),Ra&&(i.style.width=\"0px\"),ls(i,\"input\",function(){Na&&Ia>=9&&n.hasSelection&&(n.hasSelection=null),r.poll()}),ls(i,\"paste\",function(e){R(o,e)||oa(e,o)||(o.state.pasteIncoming=+new Date,r.fastPoll())}),ls(i,\"cut\",t),ls(i,\"copy\",t),ls(e.scroller,\"paste\",function(t){if(!Ot(e,t)&&!R(o,t)){if(!i.dispatchEvent)return o.state.pasteIncoming=+new Date,void r.focus();var n=new Event(\"paste\");n.clipboardData=t.clipboardData,i.dispatchEvent(n)}}),ls(e.lineSpace,\"selectstart\",function(t){Ot(e,t)||H(t)}),ls(i,\"compositionstart\",function(){var e=o.getCursor(\"from\");r.composing&&r.composing.range.clear(),r.composing={start:e,range:o.markText(e,o.getCursor(\"to\"),{className:\"CodeMirror-composing\"})}}),ls(i,\"compositionend\",function(){r.composing&&(r.poll(),r.composing.range.clear(),r.composing=null)})},dl.prototype.createField=function(e){this.wrapper=la(),this.textarea=this.wrapper.firstChild;var t=this.cm.options;sa(this.textarea,t.spellcheck,t.autocorrect,t.autocapitalize)},dl.prototype.screenReaderLabelChanged=function(e){e?this.textarea.setAttribute(\"aria-label\",e):this.textarea.removeAttribute(\"aria-label\")},dl.prototype.prepareSelection=function(){var e=this.cm,t=e.display,n=e.doc,r=Qn(e);if(e.options.moveInputWithCursor){var o=hn(e,n.sel.primary().head,\"div\"),i=t.wrapper.getBoundingClientRect(),a=t.lineDiv.getBoundingClientRect();r.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,o.top+a.top-i.top)),r.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,o.left+a.left-i.left))}return r},dl.prototype.showSelection=function(e){var t=this.cm,r=t.display;n(r.cursorDiv,e.cursors),n(r.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+\"px\",this.wrapper.style.left=e.teLeft+\"px\")},dl.prototype.reset=function(e){if(!(this.contextMenuPending||this.composing&&e)){var t=this.cm;if(this.resetting=!0,t.somethingSelected()){this.prevInput=\"\";var n=t.getSelection();this.textarea.value=n,t.state.focused&&Xa(this.textarea),Na&&Ia>=9&&(this.hasSelection=n)}else e||(this.prevInput=this.textarea.value=\"\",Na&&Ia>=9&&(this.hasSelection=null));this.resetting=!1}},dl.prototype.getField=function(){return this.textarea},dl.prototype.supportsTouch=function(){return!1},dl.prototype.focus=function(){if(\"nocursor\"!=this.cm.options.readOnly&&(!Qa||a(d(this.textarea))!=this.textarea))try{this.textarea.focus()}catch(e){}},dl.prototype.blur=function(){this.textarea.blur()},dl.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},dl.prototype.receivedFocus=function(){this.slowPoll()},dl.prototype.slowPoll=function(){var e=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,function(){e.poll(),e.cm.state.focused&&e.slowPoll()})},dl.prototype.fastPoll=function(){function e(){var r=n.poll();r||t?(n.pollingFast=!1,n.slowPoll()):(t=!0,n.polling.set(60,e))}var t=!1,n=this;n.pollingFast=!0,n.polling.set(20,e)},dl.prototype.poll=function(){var e=this,t=this.cm,n=this.textarea,r=this.prevInput;if(this.contextMenuPending||this.resetting||!t.state.focused||ds(n)&&!r&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var o=n.value;if(o==r&&!t.somethingSelected())return!1;if(Na&&Ia>=9&&this.hasSelection===o||Oa&&/[\\uf700-\\uf7ff]/.test(o))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var i=o.charCodeAt(0);if(8203!=i||r||(r=\"​\"),8666==i)return this.reset(),this.cm.execCommand(\"undo\")}for(var a=0,s=Math.min(r.length,o.length);s>a&&r.charCodeAt(a)==o.charCodeAt(a);)++a;return br(t,function(){ra(t,o.slice(a),r.length-a,null,e.composing?\"*compose\":null),o.length>1e3||o.indexOf(\"\\n\")>-1?n.value=e.prevInput=\"\":e.prevInput=o,e.composing&&(e.composing.range.clear(),e.composing.range=t.markText(e.composing.start,t.getCursor(\"to\"),{className:\"CodeMirror-composing\"}))}),!0},dl.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},dl.prototype.onKeyPress=function(){Na&&Ia>=9&&(this.hasSelection=null),this.fastPoll()},dl.prototype.onContextMenu=function(e){function t(){if(null!=a.selectionStart){var e=o.somethingSelected(),t=\"​\"+(e?a.value:\"\");a.value=\"⇚\",a.value=t,r.prevInput=e?\"\":\"​\",a.selectionStart=1,a.selectionEnd=t.length,i.selForContextMenu=o.doc.sel}}function n(){if(r.contextMenuPending==n&&(r.contextMenuPending=!1,r.wrapper.style.cssText=d,a.style.cssText=u,Na&&9>Ia&&i.scrollbars.setScrollTop(i.scroller.scrollTop=l),null!=a.selectionStart)){(!Na||Na&&9>Ia)&&t();var e=0,s=function(){i.selForContextMenu==o.doc.sel&&0==a.selectionStart&&a.selectionEnd>0&&\"​\"==r.prevInput?yr(o,Bo)(o):e++<10?i.detectingSelectAll=setTimeout(s,500):(i.selForContextMenu=null,i.input.reset())};i.detectingSelectAll=setTimeout(s,200)}}var r=this,o=r.cm,i=o.display,a=r.textarea;r.contextMenuPending&&r.contextMenuPending();var s=Dn(o,e),l=i.scroller.scrollTop;if(s&&!ka){var c=o.options.resetSelectionOnContextMenu;c&&-1==o.doc.sel.contains(s)&&yr(o,Io)(o.doc,Wr(s),$a);var u=a.style.cssText,d=r.wrapper.style.cssText,p=r.wrapper.offsetParent.getBoundingClientRect();r.wrapper.style.cssText=\"position: static\",a.style.cssText=\"position: absolute; width: 30px; height: 30px;\\n      top: \"+(e.clientY-p.top-5)+\"px; left: \"+(e.clientX-p.left-5)+\"px;\\n      z-index: 1000; background: \"+(Na?\"rgba(255, 255, 255, .05)\":\"transparent\")+\";\\n      outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);\";var h;if(Ea&&(h=a.ownerDocument.defaultView.scrollY),i.input.focus(),Ea&&a.ownerDocument.defaultView.scrollTo(null,h),i.input.reset(),o.somethingSelected()||(a.value=r.prevInput=\" \"),r.contextMenuPending=n,i.selForContextMenu=o.doc.sel,clearTimeout(i.detectingSelectAll),Na&&Ia>=9&&t(),Ga){P(e);var g=function(){U(window,\"mouseup\",g),setTimeout(n,20)};ls(window,\"mouseup\",g)}else setTimeout(n,50)}},dl.prototype.readOnlyChanged=function(e){e||this.reset(),this.textarea.disabled=\"nocursor\"==e,this.textarea.readOnly=!!e},dl.prototype.setUneditable=function(){},dl.prototype.needsContentAttribute=!1,Ji($i),ca($i);var pl=\"iter insert remove copy getEditor constructor\".split(\" \");for(var hl in Ys.prototype)Ys.prototype.hasOwnProperty(hl)&&m(pl,hl)<0&&($i.prototype[hl]=function(e){return function(){return e.apply(this.doc,arguments)}}(Ys.prototype[hl]));return O(Ys),$i.inputStyles={textarea:dl,contenteditable:ul},$i.defineMode=function(e){$i.defaults.mode||\"null\"==e||($i.defaults.mode=e),q.apply(this,arguments)},$i.defineMIME=J,$i.defineMode(\"null\",function(){return{token:function(e){return e.skipToEnd()}}}),$i.defineMIME(\"text/plain\",\"null\"),$i.defineExtension=function(e,t){$i.prototype[e]=t},$i.defineDocExtension=function(e,t){Ys.prototype[e]=t},$i.fromTextArea=Ma,wa($i),$i.version=\"5.65.16\",$i})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";e.defineMode(\"javascript\",function(t,n){function r(e){for(var t,n=!1,r=!1;null!=(t=e.next());){if(!n){if(\"/\"==t&&!r)return;\"[\"==t?r=!0:r&&\"]\"==t&&(r=!1)}n=!n&&\"\\\\\"==t}}function o(e,t,n){return Pe=e,Ye=n,t}function i(e,t){var n=e.next();if('\"'==n||\"'\"==n)return t.tokenize=a(n),t.tokenize(e,t);if(\".\"==n&&e.match(/^\\d[\\d_]*(?:[eE][+\\-]?[\\d_]+)?/))return o(\"number\",\"number\");if(\".\"==n&&e.match(\"..\"))return o(\"spread\",\"meta\");if(/[\\[\\]{}\\(\\),;\\:\\.]/.test(n))return o(n);if(\"=\"==n&&e.eat(\">\"))return o(\"=>\",\"operator\");if(\"0\"==n&&e.match(/^(?:x[\\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/))return o(\"number\",\"number\");if(/\\d/.test(n))return e.match(/^[\\d_]*(?:n|(?:\\.[\\d_]*)?(?:[eE][+\\-]?[\\d_]+)?)?/),o(\"number\",\"number\");if(\"/\"==n)return e.eat(\"*\")?(t.tokenize=s,s(e,t)):e.eat(\"/\")?(e.skipToEnd(),o(\"comment\",\"comment\")):Ve(e,t,1)?(r(e),e.match(/^\\b(([gimyus])(?![gimyus]*\\2))+\\b/),o(\"regexp\",\"string-2\")):(e.eat(\"=\"),o(\"operator\",\"operator\",e.current()));if(\"`\"==n)return t.tokenize=l,l(e,t);if(\"#\"==n&&\"!\"==e.peek())return e.skipToEnd(),o(\"meta\",\"meta\");if(\"#\"==n&&e.eatWhile(Ke))return o(\"variable\",\"property\");if(\"<\"==n&&e.match(\"!--\")||\"-\"==n&&e.match(\"->\")&&!/\\S/.test(e.string.slice(0,e.start)))return e.skipToEnd(),o(\"comment\",\"comment\");if($e.test(n))return\">\"==n&&t.lexical&&\">\"==t.lexical.type||(e.eat(\"=\")?(\"!\"==n||\"=\"==n)&&e.eat(\"=\"):/[<>*+\\-|&?]/.test(n)&&(e.eat(n),\">\"==n&&e.eat(n))),\"?\"==n&&e.eat(\".\")?o(\".\"):o(\"operator\",\"operator\",e.current());if(Ke.test(n)){e.eatWhile(Ke);var i=e.current();if(\".\"!=t.lastType){if(Ze.propertyIsEnumerable(i)){var c=Ze[i];return o(c.type,c.style,i)}if(\"async\"==i&&e.match(/^(\\s|\\/\\*([^*]|\\*(?!\\/))*?\\*\\/)*[\\[\\(\\w]/,!1))return o(\"async\",\"keyword\",i)}return o(\"variable\",\"variable\",i)}}function a(e){return function(t,n){var r,a=!1;if(Xe&&\"@\"==t.peek()&&t.match(et))return n.tokenize=i,o(\"jsonld-keyword\",\"meta\");for(;null!=(r=t.next())&&(r!=e||a);)a=!a&&\"\\\\\"==r;return a||(n.tokenize=i),o(\"string\",\"string\")}}function s(e,t){for(var n,r=!1;n=e.next();){if(\"/\"==n&&r){t.tokenize=i;break}r=\"*\"==n}return o(\"comment\",\"comment\")}function l(e,t){for(var n,r=!1;null!=(n=e.next());){if(!r&&(\"`\"==n||\"$\"==n&&e.eat(\"{\"))){t.tokenize=i;break}r=!r&&\"\\\\\"==n}return o(\"quasi\",\"string-2\",e.current())}function c(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var n=e.string.indexOf(\"=>\",e.start);if(!(0>n)){if(Je){var r=/:\\s*(?:\\w+(?:<[^>]*>|\\[\\])?|\\{[^}]*\\})\\s*$/.exec(e.string.slice(e.start,n));r&&(n=r.index)}for(var o=0,i=!1,a=n-1;a>=0;--a){var s=e.string.charAt(a),l=tt.indexOf(s);if(l>=0&&3>l){if(!o){++a;break}if(0==--o){\"(\"==s&&(i=!0);break}}else if(l>=3&&6>l)++o;else if(Ke.test(s))i=!0;else if(/[\"'\\/`]/.test(s))for(;;--a){if(0==a)return;var c=e.string.charAt(a-1);if(c==s&&\"\\\\\"!=e.string.charAt(a-2)){a--;break}}else if(i&&!o){++a;break}}i&&!o&&(t.fatArrowAt=a)}}function u(e,t,n,r,o,i){this.indented=e,this.column=t,this.type=n,this.prev=o,this.info=i,null!=r&&(this.align=r)}function d(e,t){if(!qe)return!1;for(var n=e.localVars;n;n=n.next)if(n.name==t)return!0;for(var r=e.context;r;r=r.prev)for(var n=r.vars;n;n=n.next)if(n.name==t)return!0}function p(e,t,n,r,o){var i=e.cc;for(rt.state=e,rt.stream=o,rt.marked=null,rt.cc=i,rt.style=t,e.lexical.hasOwnProperty(\"align\")||(e.lexical.align=!0);;){var a=i.length?i.pop():_e?D:I;if(a(n,r)){for(;i.length&&i[i.length-1].lex;)i.pop()();return rt.marked?rt.marked:\"variable\"==n&&d(e,r)?\"variable-2\":t}}}function h(){for(var e=arguments.length-1;e>=0;e--)rt.cc.push(arguments[e])}function g(){return h.apply(null,arguments),!0}function f(e,t){for(var n=t;n;n=n.next)if(n.name==e)return!0;return!1}function m(e){var t=rt.state;if(rt.marked=\"def\",qe){if(t.context)if(\"var\"==t.lexical.info&&t.context&&t.context.block){var r=A(e,t.context);if(null!=r)return void(t.context=r)}else if(!f(e,t.localVars))return void(t.localVars=new v(e,t.localVars));n.globalVars&&!f(e,t.globalVars)&&(t.globalVars=new v(e,t.globalVars))}}function A(e,t){if(t){if(t.block){var n=A(e,t.prev);return n?n==t.prev?t:new w(n,t.vars,!0):null}return f(e,t.vars)?t:new w(t.prev,new v(e,t.vars),!1)}return null}function M(e){return\"public\"==e||\"private\"==e||\"protected\"==e||\"abstract\"==e||\"readonly\"==e}function w(e,t,n){this.prev=e,this.vars=t,this.block=n}function v(e,t){this.name=e,this.next=t}function b(){\nrt.state.context=new w(rt.state.context,rt.state.localVars,!1),rt.state.localVars=ot}function y(){rt.state.context=new w(rt.state.context,rt.state.localVars,!0),rt.state.localVars=null}function x(){rt.state.localVars=rt.state.context.vars,rt.state.context=rt.state.context.prev}function T(e,t){var n=function(){var n=rt.state,r=n.indented;if(\"stat\"==n.lexical.type)r=n.lexical.indented;else for(var o=n.lexical;o&&\")\"==o.type&&o.align;o=o.prev)r=o.indented;n.lexical=new u(r,rt.stream.column(),e,null,n.lexical,t)};return n.lex=!0,n}function C(){var e=rt.state;e.lexical.prev&&(\")\"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function N(e){function t(n){return n==e?g():\";\"==e||\"}\"==n||\")\"==n||\"]\"==n?h():g(t)}return t}function I(e,t){return\"var\"==e?g(T(\"vardef\",t),pe,N(\";\"),C):\"keyword a\"==e?g(T(\"form\"),L,I,C):\"keyword b\"==e?g(T(\"form\"),I,C):\"keyword d\"==e?rt.stream.match(/^\\s*$/,!1)?g():g(T(\"stat\"),j,N(\";\"),C):\"debugger\"==e?g(N(\";\")):\"{\"==e?g(T(\"}\"),y,J,C,x):\";\"==e?g():\"if\"==e?(\"else\"==rt.state.lexical.info&&rt.state.cc[rt.state.cc.length-1]==C&&rt.state.cc.pop()(),g(T(\"form\"),L,I,C,Me)):\"function\"==e?g(ye):\"for\"==e?g(T(\"form\"),y,we,I,x,C):\"class\"==e||Je&&\"interface\"==t?(rt.marked=\"keyword\",g(T(\"form\",\"class\"==e?e:t),Ie,C)):\"variable\"==e?Je&&\"declare\"==t?(rt.marked=\"keyword\",g(I)):Je&&(\"module\"==t||\"enum\"==t||\"type\"==t)&&rt.stream.match(/^\\s*\\w/,!1)?(rt.marked=\"keyword\",\"enum\"==t?g(Oe):\"type\"==t?g(Te,N(\"operator\"),te,N(\";\")):g(T(\"form\"),he,N(\"{\"),T(\"}\"),J,C,C)):Je&&\"namespace\"==t?(rt.marked=\"keyword\",g(T(\"form\"),D,I,C)):Je&&\"abstract\"==t?(rt.marked=\"keyword\",g(I)):g(T(\"stat\"),P):\"switch\"==e?g(T(\"form\"),L,N(\"{\"),T(\"}\",\"switch\"),y,J,C,C,x):\"case\"==e?g(D,N(\":\")):\"default\"==e?g(N(\":\")):\"catch\"==e?g(T(\"form\"),b,E,I,C,x):\"export\"==e?g(T(\"stat\"),Le,C):\"import\"==e?g(T(\"stat\"),je,C):\"async\"==e?g(I):\"@\"==t?g(D,I):h(T(\"stat\"),D,N(\";\"),C)}function E(e){return\"(\"==e?g(Ce,N(\")\")):void 0}function D(e,t){return k(e,t,!1)}function S(e,t){return k(e,t,!0)}function L(e){return\"(\"!=e?h():g(T(\")\"),j,N(\")\"),C)}function k(e,t,n){if(rt.state.fatArrowAt==rt.stream.start){var r=n?O:Q;if(\"(\"==e)return g(b,T(\")\"),_(Ce,\")\"),C,N(\"=>\"),r,x);if(\"variable\"==e)return h(b,he,N(\"=>\"),r,x)}var o=n?B:U;return nt.hasOwnProperty(e)?g(o):\"function\"==e?g(ye,o):\"class\"==e||Je&&\"interface\"==t?(rt.marked=\"keyword\",g(T(\"form\"),Ne,C)):\"keyword c\"==e||\"async\"==e?g(n?S:D):\"(\"==e?g(T(\")\"),j,N(\")\"),C,o):\"operator\"==e||\"spread\"==e?g(n?S:D):\"[\"==e?g(T(\"]\"),Qe,C,o):\"{\"==e?q(G,\"}\",null,o):\"quasi\"==e?h(R,o):\"new\"==e?g(H(n)):g()}function j(e){return e.match(/[;\\}\\)\\],]/)?h():h(D)}function U(e,t){return\",\"==e?g(j):B(e,t,!1)}function B(e,t,n){var r=0==n?U:B,o=0==n?D:S;return\"=>\"==e?g(b,n?O:Q,x):\"operator\"==e?/\\+\\+|--/.test(t)||Je&&\"!\"==t?g(r):Je&&\"<\"==t&&rt.stream.match(/^([^<>]|<[^<>]*>)*>\\s*\\(/,!1)?g(T(\">\"),_(te,\">\"),C,r):\"?\"==t?g(D,N(\":\"),o):g(o):\"quasi\"==e?h(R,r):\";\"!=e?\"(\"==e?q(S,\")\",\"call\",r):\".\"==e?g(Y,r):\"[\"==e?g(T(\"]\"),j,N(\"]\"),C,r):Je&&\"as\"==t?(rt.marked=\"keyword\",g(te,r)):\"regexp\"==e?(rt.state.lastType=rt.marked=\"operator\",rt.stream.backUp(rt.stream.pos-rt.stream.start-1),g(o)):void 0:void 0}function R(e,t){return\"quasi\"!=e?h():\"${\"!=t.slice(t.length-2)?g(R):g(j,z)}function z(e){return\"}\"==e?(rt.marked=\"string-2\",rt.state.tokenize=l,g(R)):void 0}function Q(e){return c(rt.stream,rt.state),h(\"{\"==e?I:D)}function O(e){return c(rt.stream,rt.state),h(\"{\"==e?I:S)}function H(e){return function(t){return\".\"==t?g(e?V:F):\"variable\"==t&&Je?g(ce,e?B:U):h(e?S:D)}}function F(e,t){return\"target\"==t?(rt.marked=\"keyword\",g(U)):void 0}function V(e,t){return\"target\"==t?(rt.marked=\"keyword\",g(B)):void 0}function P(e){return\":\"==e?g(C,I):h(U,N(\";\"),C)}function Y(e){return\"variable\"==e?(rt.marked=\"property\",g()):void 0}function G(e,t){if(\"async\"==e)return rt.marked=\"property\",g(G);if(\"variable\"==e||\"keyword\"==rt.style){if(rt.marked=\"property\",\"get\"==t||\"set\"==t)return g(W);var n;return Je&&rt.state.fatArrowAt==rt.stream.start&&(n=rt.stream.match(/^\\s*:\\s*/,!1))&&(rt.state.fatArrowAt=rt.stream.pos+n[0].length),g(X)}return\"number\"==e||\"string\"==e?(rt.marked=Xe?\"property\":rt.style+\" property\",g(X)):\"jsonld-keyword\"==e?g(X):Je&&M(t)?(rt.marked=\"keyword\",g(G)):\"[\"==e?g(D,K,N(\"]\"),X):\"spread\"==e?g(S,X):\"*\"==t?(rt.marked=\"keyword\",g(G)):\":\"==e?h(X):void 0}function W(e){return\"variable\"!=e?h(X):(rt.marked=\"property\",g(ye))}function X(e){return\":\"==e?g(S):\"(\"==e?h(ye):void 0}function _(e,t,n){function r(o,i){if(n?n.indexOf(o)>-1:\",\"==o){var a=rt.state.lexical;return\"call\"==a.info&&(a.pos=(a.pos||0)+1),g(function(n,r){return n==t||r==t?h():h(e)},r)}return o==t||i==t?g():n&&n.indexOf(\";\")>-1?h(e):g(N(t))}return function(n,o){return n==t||o==t?g():h(e,r)}}function q(e,t,n){for(var r=3;r<arguments.length;r++)rt.cc.push(arguments[r]);return g(T(t,n),_(e,t),C)}function J(e){return\"}\"==e?g():h(I,J)}function K(e,t){if(Je){if(\":\"==e)return g(te);if(\"?\"==t)return g(K)}}function Z(e,t){return!Je||\":\"!=e&&\"in\"!=t?void 0:g(te)}function $(e){return Je&&\":\"==e?rt.stream.match(/^\\s*\\w+\\s+is\\b/,!1)?g(D,ee,te):g(te):void 0}function ee(e,t){return\"is\"==t?(rt.marked=\"keyword\",g()):void 0}function te(e,t){return\"keyof\"==t||\"typeof\"==t||\"infer\"==t||\"readonly\"==t?(rt.marked=\"keyword\",g(\"typeof\"==t?S:te)):\"variable\"==e||\"void\"==t?(rt.marked=\"type\",g(le)):\"|\"==t||\"&\"==t?g(te):\"string\"==e||\"number\"==e||\"atom\"==e?g(le):\"[\"==e?g(T(\"]\"),_(te,\"]\",\",\"),C,le):\"{\"==e?g(T(\"}\"),re,C,le):\"(\"==e?g(_(se,\")\"),ne,le):\"<\"==e?g(_(te,\">\"),te):\"quasi\"==e?h(ie,le):void 0}function ne(e){return\"=>\"==e?g(te):void 0}function re(e){return e.match(/[\\}\\)\\]]/)?g():\",\"==e||\";\"==e?g(re):h(oe,re)}function oe(e,t){return\"variable\"==e||\"keyword\"==rt.style?(rt.marked=\"property\",g(oe)):\"?\"==t||\"number\"==e||\"string\"==e?g(oe):\":\"==e?g(te):\"[\"==e?g(N(\"variable\"),Z,N(\"]\"),oe):\"(\"==e?h(xe,oe):e.match(/[;\\}\\)\\],]/)?void 0:g()}function ie(e,t){return\"quasi\"!=e?h():\"${\"!=t.slice(t.length-2)?g(ie):g(te,ae)}function ae(e){return\"}\"==e?(rt.marked=\"string-2\",rt.state.tokenize=l,g(ie)):void 0}function se(e,t){return\"variable\"==e&&rt.stream.match(/^\\s*[?:]/,!1)||\"?\"==t?g(se):\":\"==e?g(te):\"spread\"==e?g(se):h(te)}function le(e,t){return\"<\"==t?g(T(\">\"),_(te,\">\"),C,le):\"|\"==t||\".\"==e||\"&\"==t?g(te):\"[\"==e?g(te,N(\"]\"),le):\"extends\"==t||\"implements\"==t?(rt.marked=\"keyword\",g(te)):\"?\"==t?g(te,N(\":\"),te):void 0}function ce(e,t){return\"<\"==t?g(T(\">\"),_(te,\">\"),C,le):void 0}function ue(){return h(te,de)}function de(e,t){return\"=\"==t?g(te):void 0}function pe(e,t){return\"enum\"==t?(rt.marked=\"keyword\",g(Oe)):h(he,K,me,Ae)}function he(e,t){return Je&&M(t)?(rt.marked=\"keyword\",g(he)):\"variable\"==e?(m(t),g()):\"spread\"==e?g(he):\"[\"==e?q(fe,\"]\"):\"{\"==e?q(ge,\"}\"):void 0}function ge(e,t){return\"variable\"!=e||rt.stream.match(/^\\s*:/,!1)?(\"variable\"==e&&(rt.marked=\"property\"),\"spread\"==e?g(he):\"}\"==e?h():\"[\"==e?g(D,N(\"]\"),N(\":\"),ge):g(N(\":\"),he,me)):(m(t),g(me))}function fe(){return h(he,me)}function me(e,t){return\"=\"==t?g(S):void 0}function Ae(e){return\",\"==e?g(pe):void 0}function Me(e,t){return\"keyword b\"==e&&\"else\"==t?g(T(\"form\",\"else\"),I,C):void 0}function we(e,t){return\"await\"==t?g(we):\"(\"==e?g(T(\")\"),ve,C):void 0}function ve(e){return\"var\"==e?g(pe,be):\"variable\"==e?g(be):h(be)}function be(e,t){return\")\"==e?g():\";\"==e?g(be):\"in\"==t||\"of\"==t?(rt.marked=\"keyword\",g(D,be)):h(D,be)}function ye(e,t){return\"*\"==t?(rt.marked=\"keyword\",g(ye)):\"variable\"==e?(m(t),g(ye)):\"(\"==e?g(b,T(\")\"),_(Ce,\")\"),C,$,I,x):Je&&\"<\"==t?g(T(\">\"),_(ue,\">\"),C,ye):void 0}function xe(e,t){return\"*\"==t?(rt.marked=\"keyword\",g(xe)):\"variable\"==e?(m(t),g(xe)):\"(\"==e?g(b,T(\")\"),_(Ce,\")\"),C,$,x):Je&&\"<\"==t?g(T(\">\"),_(ue,\">\"),C,xe):void 0}function Te(e,t){return\"keyword\"==e||\"variable\"==e?(rt.marked=\"type\",g(Te)):\"<\"==t?g(T(\">\"),_(ue,\">\"),C):void 0}function Ce(e,t){return\"@\"==t&&g(D,Ce),\"spread\"==e?g(Ce):Je&&M(t)?(rt.marked=\"keyword\",g(Ce)):Je&&\"this\"==e?g(K,me):h(he,K,me)}function Ne(e,t){return\"variable\"==e?Ie(e,t):Ee(e,t)}function Ie(e,t){return\"variable\"==e?(m(t),g(Ee)):void 0}function Ee(e,t){return\"<\"==t?g(T(\">\"),_(ue,\">\"),C,Ee):\"extends\"==t||\"implements\"==t||Je&&\",\"==e?(\"implements\"==t&&(rt.marked=\"keyword\"),g(Je?te:D,Ee)):\"{\"==e?g(T(\"}\"),De,C):void 0}function De(e,t){return\"async\"==e||\"variable\"==e&&(\"static\"==t||\"get\"==t||\"set\"==t||Je&&M(t))&&rt.stream.match(/^\\s+#?[\\w$\\xa1-\\uffff]/,!1)?(rt.marked=\"keyword\",g(De)):\"variable\"==e||\"keyword\"==rt.style?(rt.marked=\"property\",g(Se,De)):\"number\"==e||\"string\"==e?g(Se,De):\"[\"==e?g(D,K,N(\"]\"),Se,De):\"*\"==t?(rt.marked=\"keyword\",g(De)):Je&&\"(\"==e?h(xe,De):\";\"==e||\",\"==e?g(De):\"}\"==e?g():\"@\"==t?g(D,De):void 0}function Se(e,t){if(\"!\"==t)return g(Se);if(\"?\"==t)return g(Se);if(\":\"==e)return g(te,me);if(\"=\"==t)return g(S);var n=rt.state.lexical.prev,r=n&&\"interface\"==n.info;return h(r?xe:ye)}function Le(e,t){return\"*\"==t?(rt.marked=\"keyword\",g(ze,N(\";\"))):\"default\"==t?(rt.marked=\"keyword\",g(D,N(\";\"))):\"{\"==e?g(_(ke,\"}\"),ze,N(\";\")):h(I)}function ke(e,t){return\"as\"==t?(rt.marked=\"keyword\",g(N(\"variable\"))):\"variable\"==e?h(S,ke):void 0}function je(e){return\"string\"==e?g():\"(\"==e?h(D):\".\"==e?h(U):h(Ue,Be,ze)}function Ue(e,t){return\"{\"==e?q(Ue,\"}\"):(\"variable\"==e&&m(t),\"*\"==t&&(rt.marked=\"keyword\"),g(Re))}function Be(e){return\",\"==e?g(Ue,Be):void 0}function Re(e,t){return\"as\"==t?(rt.marked=\"keyword\",g(Ue)):void 0}function ze(e,t){return\"from\"==t?(rt.marked=\"keyword\",g(D)):void 0}function Qe(e){return\"]\"==e?g():h(_(S,\"]\"))}function Oe(){return h(T(\"form\"),he,N(\"{\"),T(\"}\"),_(He,\"}\"),C,C)}function He(){return h(he,me)}function Fe(e,t){return\"operator\"==e.lastType||\",\"==e.lastType||$e.test(t.charAt(0))||/[,.]/.test(t.charAt(0))}function Ve(e,t,n){return t.tokenize==i&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\\[{}\\(,;:]|=>)$/.test(t.lastType)||\"quasi\"==t.lastType&&/\\{\\s*$/.test(e.string.slice(0,e.pos-(n||0)))}var Pe,Ye,Ge=t.indentUnit,We=n.statementIndent,Xe=n.jsonld,_e=n.json||Xe,qe=n.trackScope!==!1,Je=n.typescript,Ke=n.wordCharacters||/[\\w$\\xa1-\\uffff]/,Ze=function(){function e(e){return{type:e,style:\"keyword\"}}var t=e(\"keyword a\"),n=e(\"keyword b\"),r=e(\"keyword c\"),o=e(\"keyword d\"),i=e(\"operator\"),a={type:\"atom\",style:\"atom\"};return{\"if\":e(\"if\"),\"while\":t,\"with\":t,\"else\":n,\"do\":n,\"try\":n,\"finally\":n,\"return\":o,\"break\":o,\"continue\":o,\"new\":e(\"new\"),\"delete\":r,\"void\":r,\"throw\":r,\"debugger\":e(\"debugger\"),\"var\":e(\"var\"),\"const\":e(\"var\"),let:e(\"var\"),\"function\":e(\"function\"),\"catch\":e(\"catch\"),\"for\":e(\"for\"),\"switch\":e(\"switch\"),\"case\":e(\"case\"),\"default\":e(\"default\"),\"in\":i,\"typeof\":i,\"instanceof\":i,\"true\":a,\"false\":a,\"null\":a,undefined:a,NaN:a,Infinity:a,\"this\":e(\"this\"),\"class\":e(\"class\"),\"super\":e(\"atom\"),\"yield\":r,\"export\":e(\"export\"),\"import\":e(\"import\"),\"extends\":r,await:r}}(),$e=/[+\\-*&%=<>!?|~^@]/,et=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)\"/,tt=\"([{}])\",nt={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,\"this\":!0,\"import\":!0,\"jsonld-keyword\":!0},rt={state:null,column:null,marked:null,cc:null},ot=new v(\"this\",new v(\"arguments\",null));return b.lex=y.lex=!0,x.lex=!0,C.lex=!0,{startState:function(e){var t={tokenize:i,lastType:\"sof\",cc:[],lexical:new u((e||0)-Ge,0,\"block\",!1),localVars:n.localVars,context:n.localVars&&new w(null,null,!1),indented:e||0};return n.globalVars&&\"object\"==typeof n.globalVars&&(t.globalVars=n.globalVars),t},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty(\"align\")||(t.lexical.align=!1),t.indented=e.indentation(),c(e,t)),t.tokenize!=s&&e.eatSpace())return null;var n=t.tokenize(e,t);return\"comment\"==Pe?n:(t.lastType=\"operator\"!=Pe||\"++\"!=Ye&&\"--\"!=Ye?Pe:\"incdec\",p(t,n,Pe,Ye,e))},indent:function(t,r){if(t.tokenize==s||t.tokenize==l)return e.Pass;if(t.tokenize!=i)return 0;var o,a=r&&r.charAt(0),c=t.lexical;if(!/^\\s*else\\b/.test(r))for(var u=t.cc.length-1;u>=0;--u){var d=t.cc[u];if(d==C)c=c.prev;else if(d!=Me&&d!=x)break}for(;(\"stat\"==c.type||\"form\"==c.type)&&(\"}\"==a||(o=t.cc[t.cc.length-1])&&(o==U||o==B)&&!/^[,\\.=+\\-*:?[\\(]/.test(r));)c=c.prev;We&&\")\"==c.type&&\"stat\"==c.prev.type&&(c=c.prev);var p=c.type,h=a==p;return\"vardef\"==p?c.indented+(\"operator\"==t.lastType||\",\"==t.lastType?c.info.length+1:0):\"form\"==p&&\"{\"==a?c.indented:\"form\"==p?c.indented+Ge:\"stat\"==p?c.indented+(Fe(t,r)?We||Ge:0):\"switch\"!=c.info||h||0==n.doubleIndentSwitch?c.align?c.column+(h?0:1):c.indented+(h?0:Ge):c.indented+(/^(?:case|default)\\b/.test(r)?Ge:2*Ge)},electricInput:/^\\s*(?:case .*?:|default:|\\{|\\})$/,blockCommentStart:_e?null:\"/*\",blockCommentEnd:_e?null:\"*/\",blockCommentContinue:_e?null:\" * \",lineComment:_e?null:\"//\",fold:\"brace\",closeBrackets:\"()[]{}''\\\"\\\"``\",helperType:_e?\"json\":\"javascript\",jsonldMode:Xe,jsonMode:_e,expressionAllowed:Ve,skipExpression:function(t){p(t,\"atom\",\"atom\",\"true\",new e.StringStream(\"\",2,null))}}}),e.registerHelper(\"wordChars\",\"javascript\",/[\\w$]/),e.defineMIME(\"text/javascript\",\"javascript\"),e.defineMIME(\"text/ecmascript\",\"javascript\"),e.defineMIME(\"application/javascript\",\"javascript\"),e.defineMIME(\"application/x-javascript\",\"javascript\"),e.defineMIME(\"application/ecmascript\",\"javascript\"),e.defineMIME(\"application/json\",{name:\"javascript\",json:!0}),e.defineMIME(\"application/x-json\",{name:\"javascript\",json:!0}),e.defineMIME(\"application/manifest+json\",{name:\"javascript\",json:!0}),e.defineMIME(\"application/ld+json\",{name:\"javascript\",jsonld:!0}),e.defineMIME(\"text/typescript\",{name:\"javascript\",typescript:!0}),e.defineMIME(\"application/typescript\",{name:\"javascript\",typescript:!0})})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(e){for(var t={},n=0;n<e.length;++n)t[e[n].toLowerCase()]=!0;return t}function n(e,t){for(var n,r=!1;null!=(n=e.next());){if(r&&\"/\"==n){t.tokenize=null;break}r=\"*\"==n}return[\"comment\",\"comment\"]}e.defineMode(\"css\",function(t,n){function r(e,t){return g=t,e}function o(e,t){var n=e.next();if(A[n]){var o=A[n](e,t);if(o!==!1)return o}return\"@\"==n?(e.eatWhile(/[\\w\\\\\\-]/),r(\"def\",e.current())):\"=\"==n||(\"~\"==n||\"|\"==n)&&e.eat(\"=\")?r(null,\"compare\"):'\"'==n||\"'\"==n?(t.tokenize=i(n),t.tokenize(e,t)):\"#\"==n?(e.eatWhile(/[\\w\\\\\\-]/),r(\"atom\",\"hash\")):\"!\"==n?(e.match(/^\\s*\\w*/),r(\"keyword\",\"important\")):/\\d/.test(n)||\".\"==n&&e.eat(/\\d/)?(e.eatWhile(/[\\w.%]/),r(\"number\",\"unit\")):\"-\"!==n?/[,+>*\\/]/.test(n)?r(null,\"select-op\"):\".\"==n&&e.match(/^-?[_a-z][_a-z0-9-]*/i)?r(\"qualifier\",\"qualifier\"):/[:;{}\\[\\]\\(\\)]/.test(n)?r(null,n):e.match(/^[\\w-.]+(?=\\()/)?(/^(url(-prefix)?|domain|regexp)$/i.test(e.current())&&(t.tokenize=a),r(\"variable callee\",\"variable\")):/[\\w\\\\\\-]/.test(n)?(e.eatWhile(/[\\w\\\\\\-]/),r(\"property\",\"word\")):r(null,null):/[\\d.]/.test(e.peek())?(e.eatWhile(/[\\w.%]/),r(\"number\",\"unit\")):e.match(/^-[\\w\\\\\\-]*/)?(e.eatWhile(/[\\w\\\\\\-]/),e.match(/^\\s*:/,!1)?r(\"variable-2\",\"variable-definition\"):r(\"variable-2\",\"variable\")):e.match(/^\\w+-/)?r(\"meta\",\"meta\"):void 0}function i(e){return function(t,n){for(var o,i=!1;null!=(o=t.next());){if(o==e&&!i){\")\"==e&&t.backUp(1);break}i=!i&&\"\\\\\"==o}return(o==e||!i&&\")\"!=e)&&(n.tokenize=null),r(\"string\",\"string\")}}function a(e,t){return e.next(),e.match(/^\\s*[\\\"\\')]/,!1)?t.tokenize=null:t.tokenize=i(\")\"),r(null,\"(\")}function s(e,t,n){this.type=e,this.indent=t,this.prev=n}function l(e,t,n,r){return e.context=new s(n,t.indentation()+(r===!1?0:m),e.context),n}function c(e){return e.context.prev&&(e.context=e.context.prev),e.context.type}function u(e,t,n){return k[n.context.type](e,t,n)}function d(e,t,n,r){for(var o=r||1;o>0;o--)n.context=n.context.prev;return u(e,t,n)}function p(e){var t=e.current().toLowerCase();f=I.hasOwnProperty(t)?\"atom\":N.hasOwnProperty(t)?\"keyword\":\"variable\"}var h=n.inline;n.propertyKeywords||(n=e.resolveMode(\"text/css\"));var g,f,m=t.indentUnit,A=n.tokenHooks,M=n.documentTypes||{},w=n.mediaTypes||{},v=n.mediaFeatures||{},b=n.mediaValueKeywords||{},y=n.propertyKeywords||{},x=n.nonStandardPropertyKeywords||{},T=n.fontProperties||{},C=n.counterDescriptors||{},N=n.colorKeywords||{},I=n.valueKeywords||{},E=n.allowNested,D=n.lineComment,S=n.supportsAtComponent===!0,L=t.highlightNonStandardPropertyKeywords!==!1,k={};return k.top=function(e,t,n){if(\"{\"==e)return l(n,t,\"block\");if(\"}\"==e&&n.context.prev)return c(n);if(S&&/@component/i.test(e))return l(n,t,\"atComponentBlock\");if(/^@(-moz-)?document$/i.test(e))return l(n,t,\"documentTypes\");if(/^@(media|supports|(-moz-)?document|import)$/i.test(e))return l(n,t,\"atBlock\");if(/^@(font-face|counter-style)/i.test(e))return n.stateArg=e,\"restricted_atBlock_before\";if(/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(e))return\"keyframes\";if(e&&\"@\"==e.charAt(0))return l(n,t,\"at\");if(\"hash\"==e)f=\"builtin\";else if(\"word\"==e)f=\"tag\";else{if(\"variable-definition\"==e)return\"maybeprop\";if(\"interpolation\"==e)return l(n,t,\"interpolation\");if(\":\"==e)return\"pseudo\";if(E&&\"(\"==e)return l(n,t,\"parens\")}return n.context.type},k.block=function(e,t,n){if(\"word\"==e){var r=t.current().toLowerCase();return y.hasOwnProperty(r)?(f=\"property\",\"maybeprop\"):x.hasOwnProperty(r)?(f=L?\"string-2\":\"property\",\"maybeprop\"):E?(f=t.match(/^\\s*:(?:\\s|$)/,!1)?\"property\":\"tag\",\"block\"):(f+=\" error\",\"maybeprop\")}return\"meta\"==e?\"block\":E||\"hash\"!=e&&\"qualifier\"!=e?k.top(e,t,n):(f=\"error\",\"block\")},k.maybeprop=function(e,t,n){return\":\"==e?l(n,t,\"prop\"):u(e,t,n)},k.prop=function(e,t,n){if(\";\"==e)return c(n);if(\"{\"==e&&E)return l(n,t,\"propBlock\");if(\"}\"==e||\"{\"==e)return d(e,t,n);if(\"(\"==e)return l(n,t,\"parens\");if(\"hash\"!=e||/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(t.current())){if(\"word\"==e)p(t);else if(\"interpolation\"==e)return l(n,t,\"interpolation\")}else f+=\" error\";return\"prop\"},k.propBlock=function(e,t,n){return\"}\"==e?c(n):\"word\"==e?(f=\"property\",\"maybeprop\"):n.context.type},k.parens=function(e,t,n){return\"{\"==e||\"}\"==e?d(e,t,n):\")\"==e?c(n):\"(\"==e?l(n,t,\"parens\"):\"interpolation\"==e?l(n,t,\"interpolation\"):(\"word\"==e&&p(t),\"parens\")},k.pseudo=function(e,t,n){return\"meta\"==e?\"pseudo\":\"word\"==e?(f=\"variable-3\",n.context.type):u(e,t,n)},k.documentTypes=function(e,t,n){return\"word\"==e&&M.hasOwnProperty(t.current())?(f=\"tag\",n.context.type):k.atBlock(e,t,n)},k.atBlock=function(e,t,n){if(\"(\"==e)return l(n,t,\"atBlock_parens\");if(\"}\"==e||\";\"==e)return d(e,t,n);if(\"{\"==e)return c(n)&&l(n,t,E?\"block\":\"top\");if(\"interpolation\"==e)return l(n,t,\"interpolation\");if(\"word\"==e){var r=t.current().toLowerCase();f=\"only\"==r||\"not\"==r||\"and\"==r||\"or\"==r?\"keyword\":w.hasOwnProperty(r)?\"attribute\":v.hasOwnProperty(r)?\"property\":b.hasOwnProperty(r)?\"keyword\":y.hasOwnProperty(r)?\"property\":x.hasOwnProperty(r)?L?\"string-2\":\"property\":I.hasOwnProperty(r)?\"atom\":N.hasOwnProperty(r)?\"keyword\":\"error\"}return n.context.type},k.atComponentBlock=function(e,t,n){return\"}\"==e?d(e,t,n):\"{\"==e?c(n)&&l(n,t,E?\"block\":\"top\",!1):(\"word\"==e&&(f=\"error\"),n.context.type)},k.atBlock_parens=function(e,t,n){return\")\"==e?c(n):\"{\"==e||\"}\"==e?d(e,t,n,2):k.atBlock(e,t,n)},k.restricted_atBlock_before=function(e,t,n){return\"{\"==e?l(n,t,\"restricted_atBlock\"):\"word\"==e&&\"@counter-style\"==n.stateArg?(f=\"variable\",\"restricted_atBlock_before\"):u(e,t,n)},k.restricted_atBlock=function(e,t,n){return\"}\"==e?(n.stateArg=null,c(n)):\"word\"==e?(f=\"@font-face\"==n.stateArg&&!T.hasOwnProperty(t.current().toLowerCase())||\"@counter-style\"==n.stateArg&&!C.hasOwnProperty(t.current().toLowerCase())?\"error\":\"property\",\"maybeprop\"):\"restricted_atBlock\"},k.keyframes=function(e,t,n){return\"word\"==e?(f=\"variable\",\"keyframes\"):\"{\"==e?l(n,t,\"top\"):u(e,t,n)},k.at=function(e,t,n){return\";\"==e?c(n):\"{\"==e||\"}\"==e?d(e,t,n):(\"word\"==e?f=\"tag\":\"hash\"==e&&(f=\"builtin\"),\"at\")},k.interpolation=function(e,t,n){return\"}\"==e?c(n):\"{\"==e||\";\"==e?d(e,t,n):(\"word\"==e?f=\"variable\":\"variable\"!=e&&\"(\"!=e&&\")\"!=e&&(f=\"error\"),\"interpolation\")},{startState:function(e){return{tokenize:null,state:h?\"block\":\"top\",stateArg:null,context:new s(h?\"block\":\"top\",e||0,null)}},token:function(e,t){if(!t.tokenize&&e.eatSpace())return null;var n=(t.tokenize||o)(e,t);return n&&\"object\"==typeof n&&(g=n[1],n=n[0]),f=n,\"comment\"!=g&&(t.state=k[t.state](g,e,t)),f},indent:function(e,t){var n=e.context,r=t&&t.charAt(0),o=n.indent;return\"prop\"!=n.type||\"}\"!=r&&\")\"!=r||(n=n.prev),n.prev&&(\"}\"!=r||\"block\"!=n.type&&\"top\"!=n.type&&\"interpolation\"!=n.type&&\"restricted_atBlock\"!=n.type?(\")\"==r&&(\"parens\"==n.type||\"atBlock_parens\"==n.type)||\"{\"==r&&(\"at\"==n.type||\"atBlock\"==n.type))&&(o=Math.max(0,n.indent-m)):(n=n.prev,o=n.indent)),o},electricChars:\"}\",blockCommentStart:\"/*\",blockCommentEnd:\"*/\",blockCommentContinue:\" * \",lineComment:D,fold:\"brace\"}});var r=[\"domain\",\"regexp\",\"url\",\"url-prefix\"],o=t(r),i=[\"all\",\"aural\",\"braille\",\"handheld\",\"print\",\"projection\",\"screen\",\"tty\",\"tv\",\"embossed\"],a=t(i),s=[\"width\",\"min-width\",\"max-width\",\"height\",\"min-height\",\"max-height\",\"device-width\",\"min-device-width\",\"max-device-width\",\"device-height\",\"min-device-height\",\"max-device-height\",\"aspect-ratio\",\"min-aspect-ratio\",\"max-aspect-ratio\",\"device-aspect-ratio\",\"min-device-aspect-ratio\",\"max-device-aspect-ratio\",\"color\",\"min-color\",\"max-color\",\"color-index\",\"min-color-index\",\"max-color-index\",\"monochrome\",\"min-monochrome\",\"max-monochrome\",\"resolution\",\"min-resolution\",\"max-resolution\",\"scan\",\"grid\",\"orientation\",\"device-pixel-ratio\",\"min-device-pixel-ratio\",\"max-device-pixel-ratio\",\"pointer\",\"any-pointer\",\"hover\",\"any-hover\",\"prefers-color-scheme\",\"dynamic-range\",\"video-dynamic-range\"],l=t(s),c=[\"landscape\",\"portrait\",\"none\",\"coarse\",\"fine\",\"on-demand\",\"hover\",\"interlace\",\"progressive\",\"dark\",\"light\",\"standard\",\"high\"],u=t(c),d=[\"align-content\",\"align-items\",\"align-self\",\"alignment-adjust\",\"alignment-baseline\",\"all\",\"anchor-point\",\"animation\",\"animation-delay\",\"animation-direction\",\"animation-duration\",\"animation-fill-mode\",\"animation-iteration-count\",\"animation-name\",\"animation-play-state\",\"animation-timing-function\",\"appearance\",\"azimuth\",\"backdrop-filter\",\"backface-visibility\",\"background\",\"background-attachment\",\"background-blend-mode\",\"background-clip\",\"background-color\",\"background-image\",\"background-origin\",\"background-position\",\"background-position-x\",\"background-position-y\",\"background-repeat\",\"background-size\",\"baseline-shift\",\"binding\",\"bleed\",\"block-size\",\"bookmark-label\",\"bookmark-level\",\"bookmark-state\",\"bookmark-target\",\"border\",\"border-bottom\",\"border-bottom-color\",\"border-bottom-left-radius\",\"border-bottom-right-radius\",\"border-bottom-style\",\"border-bottom-width\",\"border-collapse\",\"border-color\",\"border-image\",\"border-image-outset\",\"border-image-repeat\",\"border-image-slice\",\"border-image-source\",\"border-image-width\",\"border-left\",\"border-left-color\",\"border-left-style\",\"border-left-width\",\"border-radius\",\"border-right\",\"border-right-color\",\"border-right-style\",\"border-right-width\",\"border-spacing\",\"border-style\",\"border-top\",\"border-top-color\",\"border-top-left-radius\",\"border-top-right-radius\",\"border-top-style\",\"border-top-width\",\"border-width\",\"bottom\",\"box-decoration-break\",\"box-shadow\",\"box-sizing\",\"break-after\",\"break-before\",\"break-inside\",\"caption-side\",\"caret-color\",\"clear\",\"clip\",\"color\",\"color-profile\",\"column-count\",\"column-fill\",\"column-gap\",\"column-rule\",\"column-rule-color\",\"column-rule-style\",\"column-rule-width\",\"column-span\",\"column-width\",\"columns\",\"contain\",\"content\",\"counter-increment\",\"counter-reset\",\"crop\",\"cue\",\"cue-after\",\"cue-before\",\"cursor\",\"direction\",\"display\",\"dominant-baseline\",\"drop-initial-after-adjust\",\"drop-initial-after-align\",\"drop-initial-before-adjust\",\"drop-initial-before-align\",\"drop-initial-size\",\"drop-initial-value\",\"elevation\",\"empty-cells\",\"fit\",\"fit-content\",\"fit-position\",\"flex\",\"flex-basis\",\"flex-direction\",\"flex-flow\",\"flex-grow\",\"flex-shrink\",\"flex-wrap\",\"float\",\"float-offset\",\"flow-from\",\"flow-into\",\"font\",\"font-family\",\"font-feature-settings\",\"font-kerning\",\"font-language-override\",\"font-optical-sizing\",\"font-size\",\"font-size-adjust\",\"font-stretch\",\"font-style\",\"font-synthesis\",\"font-variant\",\"font-variant-alternates\",\"font-variant-caps\",\"font-variant-east-asian\",\"font-variant-ligatures\",\"font-variant-numeric\",\"font-variant-position\",\"font-variation-settings\",\"font-weight\",\"gap\",\"grid\",\"grid-area\",\"grid-auto-columns\",\"grid-auto-flow\",\"grid-auto-rows\",\"grid-column\",\"grid-column-end\",\"grid-column-gap\",\"grid-column-start\",\"grid-gap\",\"grid-row\",\"grid-row-end\",\"grid-row-gap\",\"grid-row-start\",\"grid-template\",\"grid-template-areas\",\"grid-template-columns\",\"grid-template-rows\",\"hanging-punctuation\",\"height\",\"hyphens\",\"icon\",\"image-orientation\",\"image-rendering\",\"image-resolution\",\"inline-box-align\",\"inset\",\"inset-block\",\"inset-block-end\",\"inset-block-start\",\"inset-inline\",\"inset-inline-end\",\"inset-inline-start\",\"isolation\",\"justify-content\",\"justify-items\",\"justify-self\",\"left\",\"letter-spacing\",\"line-break\",\"line-height\",\"line-height-step\",\"line-stacking\",\"line-stacking-ruby\",\"line-stacking-shift\",\"line-stacking-strategy\",\"list-style\",\"list-style-image\",\"list-style-position\",\"list-style-type\",\"margin\",\"margin-bottom\",\"margin-left\",\"margin-right\",\"margin-top\",\"marks\",\"marquee-direction\",\"marquee-loop\",\"marquee-play-count\",\"marquee-speed\",\"marquee-style\",\"mask-clip\",\"mask-composite\",\"mask-image\",\"mask-mode\",\"mask-origin\",\"mask-position\",\"mask-repeat\",\"mask-size\",\"mask-type\",\"max-block-size\",\"max-height\",\"max-inline-size\",\"max-width\",\"min-block-size\",\"min-height\",\"min-inline-size\",\"min-width\",\"mix-blend-mode\",\"move-to\",\"nav-down\",\"nav-index\",\"nav-left\",\"nav-right\",\"nav-up\",\"object-fit\",\"object-position\",\"offset\",\"offset-anchor\",\"offset-distance\",\"offset-path\",\"offset-position\",\"offset-rotate\",\"opacity\",\"order\",\"orphans\",\"outline\",\"outline-color\",\"outline-offset\",\"outline-style\",\"outline-width\",\"overflow\",\"overflow-style\",\"overflow-wrap\",\"overflow-x\",\"overflow-y\",\"padding\",\"padding-bottom\",\"padding-left\",\"padding-right\",\"padding-top\",\"page\",\"page-break-after\",\"page-break-before\",\"page-break-inside\",\"page-policy\",\"pause\",\"pause-after\",\"pause-before\",\"perspective\",\"perspective-origin\",\"pitch\",\"pitch-range\",\"place-content\",\"place-items\",\"place-self\",\"play-during\",\"position\",\"presentation-level\",\"punctuation-trim\",\"quotes\",\"region-break-after\",\"region-break-before\",\"region-break-inside\",\"region-fragment\",\"rendering-intent\",\"resize\",\"rest\",\"rest-after\",\"rest-before\",\"richness\",\"right\",\"rotate\",\"rotation\",\"rotation-point\",\"row-gap\",\"ruby-align\",\"ruby-overhang\",\"ruby-position\",\"ruby-span\",\"scale\",\"scroll-behavior\",\"scroll-margin\",\"scroll-margin-block\",\"scroll-margin-block-end\",\"scroll-margin-block-start\",\"scroll-margin-bottom\",\"scroll-margin-inline\",\"scroll-margin-inline-end\",\"scroll-margin-inline-start\",\"scroll-margin-left\",\"scroll-margin-right\",\"scroll-margin-top\",\"scroll-padding\",\"scroll-padding-block\",\"scroll-padding-block-end\",\"scroll-padding-block-start\",\"scroll-padding-bottom\",\"scroll-padding-inline\",\"scroll-padding-inline-end\",\"scroll-padding-inline-start\",\"scroll-padding-left\",\"scroll-padding-right\",\"scroll-padding-top\",\"scroll-snap-align\",\"scroll-snap-type\",\"shape-image-threshold\",\"shape-inside\",\"shape-margin\",\"shape-outside\",\"size\",\"speak\",\"speak-as\",\"speak-header\",\"speak-numeral\",\"speak-punctuation\",\"speech-rate\",\"stress\",\"string-set\",\"tab-size\",\"table-layout\",\"target\",\"target-name\",\"target-new\",\"target-position\",\"text-align\",\"text-align-last\",\"text-combine-upright\",\"text-decoration\",\"text-decoration-color\",\"text-decoration-line\",\"text-decoration-skip\",\"text-decoration-skip-ink\",\"text-decoration-style\",\"text-emphasis\",\"text-emphasis-color\",\"text-emphasis-position\",\"text-emphasis-style\",\"text-height\",\"text-indent\",\"text-justify\",\"text-orientation\",\"text-outline\",\"text-overflow\",\"text-rendering\",\"text-shadow\",\"text-size-adjust\",\"text-space-collapse\",\"text-transform\",\"text-underline-position\",\"text-wrap\",\"top\",\"touch-action\",\"transform\",\"transform-origin\",\"transform-style\",\"transition\",\"transition-delay\",\"transition-duration\",\"transition-property\",\"transition-timing-function\",\"translate\",\"unicode-bidi\",\"user-select\",\"vertical-align\",\"visibility\",\"voice-balance\",\"voice-duration\",\"voice-family\",\"voice-pitch\",\"voice-range\",\"voice-rate\",\"voice-stress\",\"voice-volume\",\"volume\",\"white-space\",\"widows\",\"width\",\"will-change\",\"word-break\",\"word-spacing\",\"word-wrap\",\"writing-mode\",\"z-index\",\"clip-path\",\"clip-rule\",\"mask\",\"enable-background\",\"filter\",\"flood-color\",\"flood-opacity\",\"lighting-color\",\"stop-color\",\"stop-opacity\",\"pointer-events\",\"color-interpolation\",\"color-interpolation-filters\",\"color-rendering\",\"fill\",\"fill-opacity\",\"fill-rule\",\"image-rendering\",\"marker\",\"marker-end\",\"marker-mid\",\"marker-start\",\"paint-order\",\"shape-rendering\",\"stroke\",\"stroke-dasharray\",\"stroke-dashoffset\",\"stroke-linecap\",\"stroke-linejoin\",\"stroke-miterlimit\",\"stroke-opacity\",\"stroke-width\",\"text-rendering\",\"baseline-shift\",\"dominant-baseline\",\"glyph-orientation-horizontal\",\"glyph-orientation-vertical\",\"text-anchor\",\"writing-mode\"],p=t(d),h=[\"accent-color\",\"aspect-ratio\",\"border-block\",\"border-block-color\",\"border-block-end\",\"border-block-end-color\",\"border-block-end-style\",\"border-block-end-width\",\"border-block-start\",\"border-block-start-color\",\"border-block-start-style\",\"border-block-start-width\",\"border-block-style\",\"border-block-width\",\"border-inline\",\"border-inline-color\",\"border-inline-end\",\"border-inline-end-color\",\"border-inline-end-style\",\"border-inline-end-width\",\"border-inline-start\",\"border-inline-start-color\",\"border-inline-start-style\",\"border-inline-start-width\",\"border-inline-style\",\"border-inline-width\",\"content-visibility\",\"margin-block\",\"margin-block-end\",\"margin-block-start\",\"margin-inline\",\"margin-inline-end\",\"margin-inline-start\",\"overflow-anchor\",\"overscroll-behavior\",\"padding-block\",\"padding-block-end\",\"padding-block-start\",\"padding-inline\",\"padding-inline-end\",\"padding-inline-start\",\"scroll-snap-stop\",\"scrollbar-3d-light-color\",\"scrollbar-arrow-color\",\"scrollbar-base-color\",\"scrollbar-dark-shadow-color\",\"scrollbar-face-color\",\"scrollbar-highlight-color\",\"scrollbar-shadow-color\",\"scrollbar-track-color\",\"searchfield-cancel-button\",\"searchfield-decoration\",\"searchfield-results-button\",\"searchfield-results-decoration\",\"shape-inside\",\"zoom\"],g=t(h),f=[\"font-display\",\"font-family\",\"src\",\"unicode-range\",\"font-variant\",\"font-feature-settings\",\"font-stretch\",\"font-weight\",\"font-style\"],m=t(f),A=[\"additive-symbols\",\"fallback\",\"negative\",\"pad\",\"prefix\",\"range\",\"speak-as\",\"suffix\",\"symbols\",\"system\"],M=t(A),w=[\"aliceblue\",\"antiquewhite\",\"aqua\",\"aquamarine\",\"azure\",\"beige\",\"bisque\",\"black\",\"blanchedalmond\",\"blue\",\"blueviolet\",\"brown\",\"burlywood\",\"cadetblue\",\"chartreuse\",\"chocolate\",\"coral\",\"cornflowerblue\",\"cornsilk\",\"crimson\",\"cyan\",\"darkblue\",\"darkcyan\",\"darkgoldenrod\",\"darkgray\",\"darkgreen\",\"darkgrey\",\"darkkhaki\",\"darkmagenta\",\"darkolivegreen\",\"darkorange\",\"darkorchid\",\"darkred\",\"darksalmon\",\"darkseagreen\",\"darkslateblue\",\"darkslategray\",\"darkslategrey\",\"darkturquoise\",\"darkviolet\",\"deeppink\",\"deepskyblue\",\"dimgray\",\"dimgrey\",\"dodgerblue\",\"firebrick\",\"floralwhite\",\"forestgreen\",\"fuchsia\",\"gainsboro\",\"ghostwhite\",\"gold\",\"goldenrod\",\"gray\",\"grey\",\"green\",\"greenyellow\",\"honeydew\",\"hotpink\",\"indianred\",\"indigo\",\"ivory\",\"khaki\",\"lavender\",\"lavenderblush\",\"lawngreen\",\"lemonchiffon\",\"lightblue\",\"lightcoral\",\"lightcyan\",\"lightgoldenrodyellow\",\"lightgray\",\"lightgreen\",\"lightgrey\",\"lightpink\",\"lightsalmon\",\"lightseagreen\",\"lightskyblue\",\"lightslategray\",\"lightslategrey\",\"lightsteelblue\",\"lightyellow\",\"lime\",\"limegreen\",\"linen\",\"magenta\",\"maroon\",\"mediumaquamarine\",\"mediumblue\",\"mediumorchid\",\"mediumpurple\",\"mediumseagreen\",\"mediumslateblue\",\"mediumspringgreen\",\"mediumturquoise\",\"mediumvioletred\",\"midnightblue\",\"mintcream\",\"mistyrose\",\"moccasin\",\"navajowhite\",\"navy\",\"oldlace\",\"olive\",\"olivedrab\",\"orange\",\"orangered\",\"orchid\",\"palegoldenrod\",\"palegreen\",\"paleturquoise\",\"palevioletred\",\"papayawhip\",\"peachpuff\",\"peru\",\"pink\",\"plum\",\"powderblue\",\"purple\",\"rebeccapurple\",\"red\",\"rosybrown\",\"royalblue\",\"saddlebrown\",\"salmon\",\"sandybrown\",\"seagreen\",\"seashell\",\"sienna\",\"silver\",\"skyblue\",\"slateblue\",\"slategray\",\"slategrey\",\"snow\",\"springgreen\",\"steelblue\",\"tan\",\"teal\",\"thistle\",\"tomato\",\"turquoise\",\"violet\",\"wheat\",\"white\",\"whitesmoke\",\"yellow\",\"yellowgreen\"],v=t(w),b=[\"above\",\"absolute\",\"activeborder\",\"additive\",\"activecaption\",\"afar\",\"after-white-space\",\"ahead\",\"alias\",\"all\",\"all-scroll\",\"alphabetic\",\"alternate\",\"always\",\"amharic\",\"amharic-abegede\",\"antialiased\",\"appworkspace\",\"arabic-indic\",\"armenian\",\"asterisks\",\"attr\",\"auto\",\"auto-flow\",\"avoid\",\"avoid-column\",\"avoid-page\",\"avoid-region\",\"axis-pan\",\"background\",\"backwards\",\"baseline\",\"below\",\"bidi-override\",\"binary\",\"bengali\",\"blink\",\"block\",\"block-axis\",\"blur\",\"bold\",\"bolder\",\"border\",\"border-box\",\"both\",\"bottom\",\"break\",\"break-all\",\"break-word\",\"brightness\",\"bullets\",\"button\",\"buttonface\",\"buttonhighlight\",\"buttonshadow\",\"buttontext\",\"calc\",\"cambodian\",\"capitalize\",\"caps-lock-indicator\",\"caption\",\"captiontext\",\"caret\",\"cell\",\"center\",\"checkbox\",\"circle\",\"cjk-decimal\",\"cjk-earthly-branch\",\"cjk-heavenly-stem\",\"cjk-ideographic\",\"clear\",\"clip\",\"close-quote\",\"col-resize\",\"collapse\",\"color\",\"color-burn\",\"color-dodge\",\"column\",\"column-reverse\",\"compact\",\"condensed\",\"conic-gradient\",\"contain\",\"content\",\"contents\",\"content-box\",\"context-menu\",\"continuous\",\"contrast\",\"copy\",\"counter\",\"counters\",\"cover\",\"crop\",\"cross\",\"crosshair\",\"cubic-bezier\",\"currentcolor\",\"cursive\",\"cyclic\",\"darken\",\"dashed\",\"decimal\",\"decimal-leading-zero\",\"default\",\"default-button\",\"dense\",\"destination-atop\",\"destination-in\",\"destination-out\",\"destination-over\",\"devanagari\",\"difference\",\"disc\",\"discard\",\"disclosure-closed\",\"disclosure-open\",\"document\",\"dot-dash\",\"dot-dot-dash\",\"dotted\",\"double\",\"down\",\"drop-shadow\",\"e-resize\",\"ease\",\"ease-in\",\"ease-in-out\",\"ease-out\",\"element\",\"ellipse\",\"ellipsis\",\"embed\",\"end\",\"ethiopic\",\"ethiopic-abegede\",\"ethiopic-abegede-am-et\",\"ethiopic-abegede-gez\",\"ethiopic-abegede-ti-er\",\"ethiopic-abegede-ti-et\",\"ethiopic-halehame-aa-er\",\"ethiopic-halehame-aa-et\",\"ethiopic-halehame-am-et\",\"ethiopic-halehame-gez\",\"ethiopic-halehame-om-et\",\"ethiopic-halehame-sid-et\",\"ethiopic-halehame-so-et\",\"ethiopic-halehame-ti-er\",\"ethiopic-halehame-ti-et\",\"ethiopic-halehame-tig\",\"ethiopic-numeric\",\"ew-resize\",\"exclusion\",\"expanded\",\"extends\",\"extra-condensed\",\"extra-expanded\",\"fantasy\",\"fast\",\"fill\",\"fill-box\",\"fixed\",\"flat\",\"flex\",\"flex-end\",\"flex-start\",\"footnotes\",\"forwards\",\"from\",\"geometricPrecision\",\"georgian\",\"grayscale\",\"graytext\",\"grid\",\"groove\",\"gujarati\",\"gurmukhi\",\"hand\",\"hangul\",\"hangul-consonant\",\"hard-light\",\"hebrew\",\"help\",\"hidden\",\"hide\",\"higher\",\"highlight\",\"highlighttext\",\"hiragana\",\"hiragana-iroha\",\"horizontal\",\"hsl\",\"hsla\",\"hue\",\"hue-rotate\",\"icon\",\"ignore\",\"inactiveborder\",\"inactivecaption\",\"inactivecaptiontext\",\"infinite\",\"infobackground\",\"infotext\",\"inherit\",\"initial\",\"inline\",\"inline-axis\",\"inline-block\",\"inline-flex\",\"inline-grid\",\"inline-table\",\"inset\",\"inside\",\"intrinsic\",\"invert\",\"italic\",\"japanese-formal\",\"japanese-informal\",\"justify\",\"kannada\",\"katakana\",\"katakana-iroha\",\"keep-all\",\"khmer\",\"korean-hangul-formal\",\"korean-hanja-formal\",\"korean-hanja-informal\",\"landscape\",\"lao\",\"large\",\"larger\",\"left\",\"level\",\"lighter\",\"lighten\",\"line-through\",\"linear\",\"linear-gradient\",\"lines\",\"list-item\",\"listbox\",\"listitem\",\"local\",\"logical\",\"loud\",\"lower\",\"lower-alpha\",\"lower-armenian\",\"lower-greek\",\"lower-hexadecimal\",\"lower-latin\",\"lower-norwegian\",\"lower-roman\",\"lowercase\",\"ltr\",\"luminosity\",\"malayalam\",\"manipulation\",\"match\",\"matrix\",\"matrix3d\",\"media-play-button\",\"media-slider\",\"media-sliderthumb\",\"media-volume-slider\",\"media-volume-sliderthumb\",\"medium\",\"menu\",\"menulist\",\"menulist-button\",\"menutext\",\"message-box\",\"middle\",\"min-intrinsic\",\"mix\",\"mongolian\",\"monospace\",\"move\",\"multiple\",\"multiple_mask_images\",\"multiply\",\"myanmar\",\"n-resize\",\"narrower\",\"ne-resize\",\"nesw-resize\",\"no-close-quote\",\"no-drop\",\"no-open-quote\",\"no-repeat\",\"none\",\"normal\",\"not-allowed\",\"nowrap\",\"ns-resize\",\"numbers\",\"numeric\",\"nw-resize\",\"nwse-resize\",\"oblique\",\"octal\",\"opacity\",\"open-quote\",\"optimizeLegibility\",\"optimizeSpeed\",\"oriya\",\"oromo\",\"outset\",\"outside\",\"outside-shape\",\"overlay\",\"overline\",\"padding\",\"padding-box\",\"painted\",\"page\",\"paused\",\"persian\",\"perspective\",\"pinch-zoom\",\"plus-darker\",\"plus-lighter\",\"pointer\",\"polygon\",\"portrait\",\"pre\",\"pre-line\",\"pre-wrap\",\"preserve-3d\",\"progress\",\"push-button\",\"radial-gradient\",\"radio\",\"read-only\",\"read-write\",\"read-write-plaintext-only\",\"rectangle\",\"region\",\"relative\",\"repeat\",\"repeating-linear-gradient\",\"repeating-radial-gradient\",\"repeating-conic-gradient\",\"repeat-x\",\"repeat-y\",\"reset\",\"reverse\",\"rgb\",\"rgba\",\"ridge\",\"right\",\"rotate\",\"rotate3d\",\"rotateX\",\"rotateY\",\"rotateZ\",\"round\",\"row\",\"row-resize\",\"row-reverse\",\"rtl\",\"run-in\",\"running\",\"s-resize\",\"sans-serif\",\"saturate\",\"saturation\",\"scale\",\"scale3d\",\"scaleX\",\"scaleY\",\"scaleZ\",\"screen\",\"scroll\",\"scrollbar\",\"scroll-position\",\"se-resize\",\"searchfield\",\"searchfield-cancel-button\",\"searchfield-decoration\",\"searchfield-results-button\",\"searchfield-results-decoration\",\"self-start\",\"self-end\",\"semi-condensed\",\"semi-expanded\",\"separate\",\"sepia\",\"serif\",\"show\",\"sidama\",\"simp-chinese-formal\",\"simp-chinese-informal\",\"single\",\"skew\",\"skewX\",\"skewY\",\"skip-white-space\",\"slide\",\"slider-horizontal\",\"slider-vertical\",\"sliderthumb-horizontal\",\"sliderthumb-vertical\",\"slow\",\"small\",\"small-caps\",\"small-caption\",\"smaller\",\"soft-light\",\"solid\",\"somali\",\"source-atop\",\"source-in\",\"source-out\",\"source-over\",\"space\",\"space-around\",\"space-between\",\"space-evenly\",\"spell-out\",\"square\",\"square-button\",\"start\",\"static\",\"status-bar\",\"stretch\",\"stroke\",\"stroke-box\",\"sub\",\"subpixel-antialiased\",\"svg_masks\",\"super\",\"sw-resize\",\"symbolic\",\"symbols\",\"system-ui\",\"table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row\",\"table-row-group\",\"tamil\",\"telugu\",\"text\",\"text-bottom\",\"text-top\",\"textarea\",\"textfield\",\"thai\",\"thick\",\"thin\",\"threeddarkshadow\",\"threedface\",\"threedhighlight\",\"threedlightshadow\",\"threedshadow\",\"tibetan\",\"tigre\",\"tigrinya-er\",\"tigrinya-er-abegede\",\"tigrinya-et\",\"tigrinya-et-abegede\",\"to\",\"top\",\"trad-chinese-formal\",\"trad-chinese-informal\",\"transform\",\"translate\",\"translate3d\",\"translateX\",\"translateY\",\"translateZ\",\"transparent\",\"ultra-condensed\",\"ultra-expanded\",\"underline\",\"unidirectional-pan\",\"unset\",\"up\",\"upper-alpha\",\"upper-armenian\",\"upper-greek\",\"upper-hexadecimal\",\"upper-latin\",\"upper-norwegian\",\"upper-roman\",\"uppercase\",\"urdu\",\"url\",\"var\",\"vertical\",\"vertical-text\",\"view-box\",\"visible\",\"visibleFill\",\"visiblePainted\",\"visibleStroke\",\"visual\",\"w-resize\",\"wait\",\"wave\",\"wider\",\"window\",\"windowframe\",\"windowtext\",\"words\",\"wrap\",\"wrap-reverse\",\"x-large\",\"x-small\",\"xor\",\"xx-large\",\"xx-small\"],y=t(b),x=r.concat(i).concat(s).concat(c).concat(d).concat(h).concat(w).concat(b);\ne.registerHelper(\"hintWords\",\"css\",x),e.defineMIME(\"text/css\",{documentTypes:o,mediaTypes:a,mediaFeatures:l,mediaValueKeywords:u,propertyKeywords:p,nonStandardPropertyKeywords:g,fontProperties:m,counterDescriptors:M,colorKeywords:v,valueKeywords:y,tokenHooks:{\"/\":function(e,t){return e.eat(\"*\")?(t.tokenize=n,n(e,t)):!1}},name:\"css\"}),e.defineMIME(\"text/x-scss\",{mediaTypes:a,mediaFeatures:l,mediaValueKeywords:u,propertyKeywords:p,nonStandardPropertyKeywords:g,colorKeywords:v,valueKeywords:y,fontProperties:m,allowNested:!0,lineComment:\"//\",tokenHooks:{\"/\":function(e,t){return e.eat(\"/\")?(e.skipToEnd(),[\"comment\",\"comment\"]):e.eat(\"*\")?(t.tokenize=n,n(e,t)):[\"operator\",\"operator\"]},\":\":function(e){return e.match(/^\\s*\\{/,!1)?[null,null]:!1},$:function(e){return e.match(/^[\\w-]+/),e.match(/^\\s*:/,!1)?[\"variable-2\",\"variable-definition\"]:[\"variable-2\",\"variable\"]},\"#\":function(e){return e.eat(\"{\")?[null,\"interpolation\"]:!1}},name:\"css\",helperType:\"scss\"}),e.defineMIME(\"text/x-less\",{mediaTypes:a,mediaFeatures:l,mediaValueKeywords:u,propertyKeywords:p,nonStandardPropertyKeywords:g,colorKeywords:v,valueKeywords:y,fontProperties:m,allowNested:!0,lineComment:\"//\",tokenHooks:{\"/\":function(e,t){return e.eat(\"/\")?(e.skipToEnd(),[\"comment\",\"comment\"]):e.eat(\"*\")?(t.tokenize=n,n(e,t)):[\"operator\",\"operator\"]},\"@\":function(e){return e.eat(\"{\")?[null,\"interpolation\"]:e.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\\b/i,!1)?!1:(e.eatWhile(/[\\w\\\\\\-]/),e.match(/^\\s*:/,!1)?[\"variable-2\",\"variable-definition\"]:[\"variable-2\",\"variable\"])},\"&\":function(){return[\"atom\",\"atom\"]}},name:\"css\",helperType:\"less\"}),e.defineMIME(\"text/x-gss\",{documentTypes:o,mediaTypes:a,mediaFeatures:l,propertyKeywords:p,nonStandardPropertyKeywords:g,fontProperties:m,counterDescriptors:M,colorKeywords:v,valueKeywords:y,supportsAtComponent:!0,tokenHooks:{\"/\":function(e,t){return e.eat(\"*\")?(t.tokenize=n,n(e,t)):!1}},name:\"css\",helperType:\"gss\"})})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";var t={autoSelfClosers:{area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},implicitlyClosed:{dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},contextGrabbers:{dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!0,caseFold:!0},n={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1,allowMissingTagName:!1,caseFold:!1};e.defineMode(\"xml\",function(r,o){function i(e,t){function n(n){return t.tokenize=n,n(e,t)}var r=e.next();if(\"<\"==r)return e.eat(\"!\")?e.eat(\"[\")?e.match(\"CDATA[\")?n(l(\"atom\",\"]]>\")):null:e.match(\"--\")?n(l(\"comment\",\"-->\")):e.match(\"DOCTYPE\",!0,!0)?(e.eatWhile(/[\\w\\._\\-]/),n(c(1))):null:e.eat(\"?\")?(e.eatWhile(/[\\w\\._\\-]/),t.tokenize=l(\"meta\",\"?>\"),\"meta\"):(I=e.eat(\"/\")?\"closeTag\":\"openTag\",t.tokenize=a,\"tag bracket\");if(\"&\"==r){var o;return o=e.eat(\"#\")?e.eat(\"x\")?e.eatWhile(/[a-fA-F\\d]/)&&e.eat(\";\"):e.eatWhile(/[\\d]/)&&e.eat(\";\"):e.eatWhile(/[\\w\\.\\-:]/)&&e.eat(\";\"),o?\"atom\":\"error\"}return e.eatWhile(/[^&<]/),null}function a(e,t){var n=e.next();if(\">\"==n||\"/\"==n&&e.eat(\">\"))return t.tokenize=i,I=\">\"==n?\"endTag\":\"selfcloseTag\",\"tag bracket\";if(\"=\"==n)return I=\"equals\",null;if(\"<\"==n){t.tokenize=i,t.state=g,t.tagName=t.tagStart=null;var r=t.tokenize(e,t);return r?r+\" tag error\":\"tag error\"}return/[\\'\\\"]/.test(n)?(t.tokenize=s(n),t.stringStartCol=e.column(),t.tokenize(e,t)):(e.match(/^[^\\s\\u00a0=<>\\\"\\']*[^\\s\\u00a0=<>\\\"\\'\\/]/),\"word\")}function s(e){var t=function(t,n){for(;!t.eol();)if(t.next()==e){n.tokenize=a;break}return\"string\"};return t.isInAttribute=!0,t}function l(e,t){return function(n,r){for(;!n.eol();){if(n.match(t)){r.tokenize=i;break}n.next()}return e}}function c(e){return function(t,n){for(var r;null!=(r=t.next());){if(\"<\"==r)return n.tokenize=c(e+1),n.tokenize(t,n);if(\">\"==r){if(1==e){n.tokenize=i;break}return n.tokenize=c(e-1),n.tokenize(t,n)}}return\"meta\"}}function u(e){return e&&e.toLowerCase()}function d(e,t,n){this.prev=e.context,this.tagName=t||\"\",this.indent=e.indented,this.startOfLine=n,(T.doNotIndent.hasOwnProperty(t)||e.context&&e.context.noIndent)&&(this.noIndent=!0)}function p(e){e.context&&(e.context=e.context.prev)}function h(e,t){for(var n;;){if(!e.context)return;if(n=e.context.tagName,!T.contextGrabbers.hasOwnProperty(u(n))||!T.contextGrabbers[u(n)].hasOwnProperty(u(t)))return;p(e)}}function g(e,t,n){return\"openTag\"==e?(n.tagStart=t.column(),f):\"closeTag\"==e?m:g}function f(e,t,n){return\"word\"==e?(n.tagName=t.current(),E=\"tag\",w):T.allowMissingTagName&&\"endTag\"==e?(E=\"tag bracket\",w(e,t,n)):(E=\"error\",f)}function m(e,t,n){if(\"word\"==e){var r=t.current();return n.context&&n.context.tagName!=r&&T.implicitlyClosed.hasOwnProperty(u(n.context.tagName))&&p(n),n.context&&n.context.tagName==r||T.matchClosing===!1?(E=\"tag\",A):(E=\"tag error\",M)}return T.allowMissingTagName&&\"endTag\"==e?(E=\"tag bracket\",A(e,t,n)):(E=\"error\",M)}function A(e,t,n){return\"endTag\"!=e?(E=\"error\",A):(p(n),g)}function M(e,t,n){return E=\"error\",A(e,t,n)}function w(e,t,n){if(\"word\"==e)return E=\"attribute\",v;if(\"endTag\"==e||\"selfcloseTag\"==e){var r=n.tagName,o=n.tagStart;return n.tagName=n.tagStart=null,\"selfcloseTag\"==e||T.autoSelfClosers.hasOwnProperty(u(r))?h(n,r):(h(n,r),n.context=new d(n,r,o==n.indented)),g}return E=\"error\",w}function v(e,t,n){return\"equals\"==e?b:(T.allowMissing||(E=\"error\"),w(e,t,n))}function b(e,t,n){return\"string\"==e?y:\"word\"==e&&T.allowUnquoted?(E=\"string\",w):(E=\"error\",w(e,t,n))}function y(e,t,n){return\"string\"==e?y:w(e,t,n)}var x=r.indentUnit,T={},C=o.htmlMode?t:n;for(var N in C)T[N]=C[N];for(var N in o)T[N]=o[N];var I,E;return i.isInText=!0,{startState:function(e){var t={tokenize:i,state:g,indented:e||0,tagName:null,tagStart:null,context:null};return null!=e&&(t.baseIndent=e),t},token:function(e,t){if(!t.tagName&&e.sol()&&(t.indented=e.indentation()),e.eatSpace())return null;I=null;var n=t.tokenize(e,t);return(n||I)&&\"comment\"!=n&&(E=null,t.state=t.state(I||n,e,t),E&&(n=\"error\"==E?n+\" error\":E)),n},indent:function(t,n,r){var o=t.context;if(t.tokenize.isInAttribute)return t.tagStart==t.indented?t.stringStartCol+1:t.indented+x;if(o&&o.noIndent)return e.Pass;if(t.tokenize!=a&&t.tokenize!=i)return r?r.match(/^(\\s*)/)[0].length:0;if(t.tagName)return T.multilineTagIndentPastTag!==!1?t.tagStart+t.tagName.length+2:t.tagStart+x*(T.multilineTagIndentFactor||1);if(T.alignCDATA&&/<!\\[CDATA\\[/.test(n))return 0;var s=n&&/^<(\\/)?([\\w_:\\.-]*)/.exec(n);if(s&&s[1])for(;o;){if(o.tagName==s[2]){o=o.prev;break}if(!T.implicitlyClosed.hasOwnProperty(u(o.tagName)))break;o=o.prev}else if(s)for(;o;){var l=T.contextGrabbers[u(o.tagName)];if(!l||!l.hasOwnProperty(u(s[2])))break;o=o.prev}for(;o&&o.prev&&!o.startOfLine;)o=o.prev;return o?o.indent+x:t.baseIndent||0},electricInput:/<\\/[\\s\\w:]+>$/,blockCommentStart:\"<!--\",blockCommentEnd:\"-->\",configuration:T.htmlMode?\"html\":\"xml\",helperType:T.htmlMode?\"html\":\"xml\",skipAttribute:function(e){e.state==b&&(e.state=w)},xmlCurrentTag:function(e){return e.tagName?{name:e.tagName,close:\"closeTag\"==e.type}:null},xmlCurrentContext:function(e){for(var t=[],n=e.context;n;n=n.prev)t.push(n.tagName);return t.reverse()}}}),e.defineMIME(\"text/xml\",\"xml\"),e.defineMIME(\"application/xml\",\"xml\"),e.mimeModes.hasOwnProperty(\"text/html\")||e.defineMIME(\"text/html\",{name:\"xml\",htmlMode:!0})})},function(e,t,n){!function(e){e(n(268),n(271),n(269),n(270))}(function(e){\"use strict\";function t(e,t,n){var r=e.current(),o=r.search(t);return o>-1?e.backUp(r.length-o):r.match(/<\\/?$/)&&(e.backUp(r.length),e.match(t,!1)||e.match(r)),n}function n(e){var t=l[e];return t?t:l[e]=new RegExp(\"\\\\s+\"+e+\"\\\\s*=\\\\s*('|\\\")?([^'\\\"]+)('|\\\")?\\\\s*\")}function r(e,t){var r=e.match(n(t));return r?/^\\s*(.*?)\\s*$/.exec(r[2])[1]:\"\"}function o(e,t){return new RegExp((t?\"^\":\"\")+\"</\\\\s*\"+e+\"\\\\s*>\",\"i\")}function i(e,t){for(var n in e)for(var r=t[n]||(t[n]=[]),o=e[n],i=o.length-1;i>=0;i--)r.unshift(o[i])}function a(e,t){for(var n=0;n<e.length;n++){var o=e[n];if(!o[0]||o[1].test(r(t,o[0])))return o[2]}}var s={script:[[\"lang\",/(javascript|babel)/i,\"javascript\"],[\"type\",/^(?:text|application)\\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,\"javascript\"],[\"type\",/./,\"text/plain\"],[null,null,\"javascript\"]],style:[[\"lang\",/^css$/i,\"css\"],[\"type\",/^(text\\/)?(x-)?(stylesheet|css)$/i,\"css\"],[\"type\",/./,\"text/plain\"],[null,null,\"css\"]]},l={};e.defineMode(\"htmlmixed\",function(n,r){function l(r,i){var s,d=c.token(r,i.htmlState),p=/\\btag\\b/.test(d);if(p&&!/[<>\\s\\/]/.test(r.current())&&(s=i.htmlState.tagName&&i.htmlState.tagName.toLowerCase())&&u.hasOwnProperty(s))i.inTag=s+\" \";else if(i.inTag&&p&&/>$/.test(r.current())){var h=/^([\\S]+) (.*)/.exec(i.inTag);i.inTag=null;var g=\">\"==r.current()&&a(u[h[1]],h[2]),f=e.getMode(n,g),m=o(h[1],!0),A=o(h[1],!1);i.token=function(e,n){return e.match(m,!1)?(n.token=l,n.localState=n.localMode=null,null):t(e,A,n.localMode.token(e,n.localState))},i.localMode=f,i.localState=e.startState(f,c.indent(i.htmlState,\"\",\"\"))}else i.inTag&&(i.inTag+=r.current(),r.eol()&&(i.inTag+=\" \"));return d}var c=e.getMode(n,{name:\"xml\",htmlMode:!0,multilineTagIndentFactor:r.multilineTagIndentFactor,multilineTagIndentPastTag:r.multilineTagIndentPastTag,allowMissingTagName:r.allowMissingTagName}),u={},d=r&&r.tags,p=r&&r.scriptTypes;if(i(s,u),d&&i(d,u),p)for(var h=p.length-1;h>=0;h--)u.script.unshift([\"type\",p[h].matches,p[h].mode]);return{startState:function(){var t=e.startState(c);return{token:l,inTag:null,localMode:null,localState:null,htmlState:t}},copyState:function(t){var n;return t.localState&&(n=e.copyState(t.localMode,t.localState)),{token:t.token,inTag:t.inTag,localMode:t.localMode,localState:n,htmlState:e.copyState(c,t.htmlState)}},token:function(e,t){return t.token(e,t)},indent:function(t,n,r){return!t.localMode||/^\\s*<\\//.test(n)?c.indent(t.htmlState,n,r):t.localMode.indent?t.localMode.indent(t.localState,n,r):e.Pass},innerMode:function(e){return{state:e.localState||e.htmlState,mode:e.localMode||c}}}},\"xml\",\"javascript\",\"css\"),e.defineMIME(\"text/html\",\"htmlmixed\")})},function(e,t,n){!function(e){e(n(268),n(271),n(274))}(function(e){\"use strict\";e.defineMode(\"markdown\",function(t,n){function r(n){if(e.findModeByName){var r=e.findModeByName(n);r&&(n=r.mime||r.mimes[0])}var o=e.getMode(t,n);return\"null\"==o.name?null:o}function o(e,t,n){return t.f=t.inline=n,n(e,t)}function i(e,t,n){return t.f=t.block=n,n(e,t)}function a(e){return!e||!/\\S/.test(e.string)}function s(t){if(t.linkTitle=!1,t.linkHref=!1,t.linkText=!1,t.em=!1,t.strong=!1,t.strikethrough=!1,t.quote=0,t.indentedCode=!1,t.f==c){var n=b;if(!n){var r=e.innerMode(v,t.htmlState);n=\"xml\"==r.mode.name&&null===r.state.tagStart&&!r.state.context&&r.state.tokenize.isInText}n&&(t.f=h,t.block=l,t.htmlState=null)}return t.trailingSpace=0,t.trailingSpaceNewLine=!1,t.prevLine=t.thisLine,t.thisLine={stream:null},null}function l(t,i){var s=t.column()===i.indentation,l=a(i.prevLine.stream),c=i.indentedCode,p=i.prevLine.hr,h=i.list!==!1,g=(i.listStack[i.listStack.length-1]||0)+3;i.indentedCode=!1;var f=i.indentation;if(null===i.indentationDiff&&(i.indentationDiff=i.indentation,h)){for(i.list=null;f<i.listStack[i.listStack.length-1];)i.listStack.pop(),i.listStack.length?i.indentation=i.listStack[i.listStack.length-1]:i.list=!1;i.list!==!1&&(i.indentationDiff=f-i.listStack[i.listStack.length-1])}var m=!(l||p||i.prevLine.header||h&&c||i.prevLine.fencedCodeEnd),M=(i.list===!1||p||l)&&i.indentation<=g&&t.match(T),w=null;if(i.indentationDiff>=4&&(c||i.prevLine.fencedCodeEnd||i.prevLine.header||l))return t.skipToEnd(),i.indentedCode=!0,y.code;if(t.eatSpace())return null;if(s&&i.indentation<=g&&(w=t.match(I))&&w[1].length<=6)return i.quote=0,i.header=w[1].length,i.thisLine.header=!0,n.highlightFormatting&&(i.formatting=\"header\"),i.f=i.inline,d(i);if(i.indentation<=g&&t.eat(\">\"))return i.quote=s?1:i.quote+1,n.highlightFormatting&&(i.formatting=\"quote\"),t.eatSpace(),d(i);if(!M&&!i.setext&&s&&i.indentation<=g&&(w=t.match(C))){var v=w[1]?\"ol\":\"ul\";return i.indentation=f+t.current().length,i.list=!0,i.quote=0,i.listStack.push(i.indentation),i.em=!1,i.strong=!1,i.code=!1,i.strikethrough=!1,n.taskLists&&t.match(N,!1)&&(i.taskList=!0),i.f=i.inline,n.highlightFormatting&&(i.formatting=[\"list\",\"list-\"+v]),d(i)}return s&&i.indentation<=g&&(w=t.match(S,!0))?(i.quote=0,i.fencedEndRE=new RegExp(w[1]+\"+ *$\"),i.localMode=n.fencedCodeBlockHighlighting&&r(w[2]||n.fencedCodeBlockDefaultMode),i.localMode&&(i.localState=e.startState(i.localMode)),i.f=i.block=u,n.highlightFormatting&&(i.formatting=\"code-block\"),i.code=-1,d(i)):i.setext||!(m&&h||i.quote||i.list!==!1||i.code||M||L.test(t.string))&&(w=t.lookAhead(1))&&(w=w.match(E))?(i.setext?(i.header=i.setext,i.setext=0,t.skipToEnd(),n.highlightFormatting&&(i.formatting=\"header\")):(i.header=\"=\"==w[0].charAt(0)?1:2,i.setext=i.header),i.thisLine.header=!0,i.f=i.inline,d(i)):M?(t.skipToEnd(),i.hr=!0,i.thisLine.hr=!0,y.hr):\"[\"===t.peek()?o(t,i,A):o(t,i,i.inline)}function c(t,n){var r=v.token(t,n.htmlState);if(!b){var o=e.innerMode(v,n.htmlState);(\"xml\"==o.mode.name&&null===o.state.tagStart&&!o.state.context&&o.state.tokenize.isInText||n.md_inside&&t.current().indexOf(\">\")>-1)&&(n.f=h,n.block=l,n.htmlState=null)}return r}function u(e,t){var r=t.listStack[t.listStack.length-1]||0,o=t.indentation<r,a=r+3;if(t.fencedEndRE&&t.indentation<=a&&(o||e.match(t.fencedEndRE))){n.highlightFormatting&&(t.formatting=\"code-block\");var s;return o||(s=d(t)),t.localMode=t.localState=null,t.block=l,t.f=h,t.fencedEndRE=null,t.code=0,t.thisLine.fencedCodeEnd=!0,o?i(e,t,t.block):s}return t.localMode?t.localMode.token(e,t.localState):(e.skipToEnd(),y.code)}function d(e){var t=[];if(e.formatting){t.push(y.formatting),\"string\"==typeof e.formatting&&(e.formatting=[e.formatting]);for(var r=0;r<e.formatting.length;r++)t.push(y.formatting+\"-\"+e.formatting[r]),\"header\"===e.formatting[r]&&t.push(y.formatting+\"-\"+e.formatting[r]+\"-\"+e.header),\"quote\"===e.formatting[r]&&(!n.maxBlockquoteDepth||n.maxBlockquoteDepth>=e.quote?t.push(y.formatting+\"-\"+e.formatting[r]+\"-\"+e.quote):t.push(\"error\"))}if(e.taskOpen)return t.push(\"meta\"),t.length?t.join(\" \"):null;if(e.taskClosed)return t.push(\"property\"),t.length?t.join(\" \"):null;if(e.linkHref?t.push(y.linkHref,\"url\"):(e.strong&&t.push(y.strong),e.em&&t.push(y.em),e.strikethrough&&t.push(y.strikethrough),e.emoji&&t.push(y.emoji),e.linkText&&t.push(y.linkText),e.code&&t.push(y.code),e.image&&t.push(y.image),e.imageAltText&&t.push(y.imageAltText,\"link\"),e.imageMarker&&t.push(y.imageMarker)),e.header&&t.push(y.header,y.header+\"-\"+e.header),e.quote&&(t.push(y.quote),!n.maxBlockquoteDepth||n.maxBlockquoteDepth>=e.quote?t.push(y.quote+\"-\"+e.quote):t.push(y.quote+\"-\"+n.maxBlockquoteDepth)),e.list!==!1){var o=(e.listStack.length-1)%3;o?1===o?t.push(y.list2):t.push(y.list3):t.push(y.list1)}return e.trailingSpaceNewLine?t.push(\"trailing-space-new-line\"):e.trailingSpace&&t.push(\"trailing-space-\"+(e.trailingSpace%2?\"a\":\"b\")),t.length?t.join(\" \"):null}function p(e,t){return e.match(D,!0)?d(t):void 0}function h(t,r){var o=r.text(t,r);if(\"undefined\"!=typeof o)return o;if(r.list)return r.list=null,d(r);if(r.taskList){var a=\" \"===t.match(N,!0)[1];return a?r.taskOpen=!0:r.taskClosed=!0,n.highlightFormatting&&(r.formatting=\"task\"),r.taskList=!1,d(r)}if(r.taskOpen=!1,r.taskClosed=!1,r.header&&t.match(/^#+$/,!0))return n.highlightFormatting&&(r.formatting=\"header\"),d(r);var s=t.next();if(r.linkTitle){r.linkTitle=!1;var l=s;\"(\"===s&&(l=\")\"),l=(l+\"\").replace(/([.?*+^\\[\\]\\\\(){}|-])/g,\"\\\\$1\");var u=\"^\\\\s*(?:[^\"+l+\"\\\\\\\\]+|\\\\\\\\\\\\\\\\|\\\\\\\\.)\"+l;if(t.match(new RegExp(u),!0))return y.linkHref}if(\"`\"===s){var p=r.formatting;n.highlightFormatting&&(r.formatting=\"code\"),t.eatWhile(\"`\");var m=t.current().length;if(0!=r.code||r.quote&&1!=m){if(m==r.code){var A=d(r);return r.code=0,A}return r.formatting=p,d(r)}return r.code=m,d(r)}if(r.code)return d(r);if(\"\\\\\"===s&&(t.next(),n.highlightFormatting)){var M=d(r),w=y.formatting+\"-escape\";return M?M+\" \"+w:w}if(\"!\"===s&&t.match(/\\[[^\\]]*\\] ?(?:\\(|\\[)/,!1))return r.imageMarker=!0,r.image=!0,n.highlightFormatting&&(r.formatting=\"image\"),d(r);if(\"[\"===s&&r.imageMarker&&t.match(/[^\\]]*\\](\\(.*?\\)| ?\\[.*?\\])/,!1))return r.imageMarker=!1,r.imageAltText=!0,n.highlightFormatting&&(r.formatting=\"image\"),d(r);if(\"]\"===s&&r.imageAltText){n.highlightFormatting&&(r.formatting=\"image\");var M=d(r);return r.imageAltText=!1,r.image=!1,r.inline=r.f=f,M}if(\"[\"===s&&!r.image)return r.linkText&&t.match(/^.*?\\]/)?d(r):(r.linkText=!0,n.highlightFormatting&&(r.formatting=\"link\"),d(r));if(\"]\"===s&&r.linkText){n.highlightFormatting&&(r.formatting=\"link\");var M=d(r);return r.linkText=!1,r.inline=r.f=t.match(/\\(.*?\\)| ?\\[.*?\\]/,!1)?f:h,M}if(\"<\"===s&&t.match(/^(https?|ftps?):\\/\\/(?:[^\\\\>]|\\\\.)+>/,!1)){r.f=r.inline=g,n.highlightFormatting&&(r.formatting=\"link\");var M=d(r);return M?M+=\" \":M=\"\",M+y.linkInline}if(\"<\"===s&&t.match(/^[^> \\\\]+@(?:[^\\\\>]|\\\\.)+>/,!1)){r.f=r.inline=g,n.highlightFormatting&&(r.formatting=\"link\");var M=d(r);return M?M+=\" \":M=\"\",M+y.linkEmail}if(n.xml&&\"<\"===s&&t.match(/^(!--|\\?|!\\[CDATA\\[|[a-z][a-z0-9-]*(?:\\s+[a-z_:.\\-]+(?:\\s*=\\s*[^>]+)?)*\\s*(?:>|$))/i,!1)){var b=t.string.indexOf(\">\",t.pos);if(-1!=b){var x=t.string.substring(t.start,b);/markdown\\s*=\\s*('|\"){0,1}1('|\"){0,1}/.test(x)&&(r.md_inside=!0)}return t.backUp(1),r.htmlState=e.startState(v),i(t,r,c)}if(n.xml&&\"<\"===s&&t.match(/^\\/\\w*?>/))return r.md_inside=!1,\"tag\";if(\"*\"===s||\"_\"===s){for(var T=1,C=1==t.pos?\" \":t.string.charAt(t.pos-2);3>T&&t.eat(s);)T++;var I=t.peek()||\" \",E=!/\\s/.test(I)&&(!k.test(I)||/\\s/.test(C)||k.test(C)),D=!/\\s/.test(C)&&(!k.test(C)||/\\s/.test(I)||k.test(I)),S=null,L=null;if(T%2&&(r.em||!E||\"*\"!==s&&D&&!k.test(C)?r.em!=s||!D||\"*\"!==s&&E&&!k.test(I)||(S=!1):S=!0),T>1&&(r.strong||!E||\"*\"!==s&&D&&!k.test(C)?r.strong!=s||!D||\"*\"!==s&&E&&!k.test(I)||(L=!1):L=!0),null!=L||null!=S){n.highlightFormatting&&(r.formatting=null==S?\"strong\":null==L?\"em\":\"strong em\"),S===!0&&(r.em=s),L===!0&&(r.strong=s);var A=d(r);return S===!1&&(r.em=!1),L===!1&&(r.strong=!1),A}}else if(\" \"===s&&(t.eat(\"*\")||t.eat(\"_\"))){if(\" \"===t.peek())return d(r);t.backUp(1)}if(n.strikethrough)if(\"~\"===s&&t.eatWhile(s)){if(r.strikethrough){n.highlightFormatting&&(r.formatting=\"strikethrough\");var A=d(r);return r.strikethrough=!1,A}if(t.match(/^[^\\s]/,!1))return r.strikethrough=!0,n.highlightFormatting&&(r.formatting=\"strikethrough\"),d(r)}else if(\" \"===s&&t.match(\"~~\",!0)){if(\" \"===t.peek())return d(r);t.backUp(2)}if(n.emoji&&\":\"===s&&t.match(/^(?:[a-z_\\d+][a-z_\\d+-]*|\\-[a-z_\\d+][a-z_\\d+-]*):/)){r.emoji=!0,n.highlightFormatting&&(r.formatting=\"emoji\");var j=d(r);return r.emoji=!1,j}return\" \"===s&&(t.match(/^ +$/,!1)?r.trailingSpace++:r.trailingSpace&&(r.trailingSpaceNewLine=!0)),d(r)}function g(e,t){var r=e.next();if(\">\"===r){t.f=t.inline=h,n.highlightFormatting&&(t.formatting=\"link\");var o=d(t);return o?o+=\" \":o=\"\",o+y.linkInline}return e.match(/^[^>]+/,!0),y.linkInline}function f(e,t){if(e.eatSpace())return null;var r=e.next();return\"(\"===r||\"[\"===r?(t.f=t.inline=m(\"(\"===r?\")\":\"]\"),n.highlightFormatting&&(t.formatting=\"link-string\"),t.linkHref=!0,d(t)):\"error\"}function m(e){return function(t,r){var o=t.next();if(o===e){r.f=r.inline=h,n.highlightFormatting&&(r.formatting=\"link-string\");var i=d(r);return r.linkHref=!1,i}return t.match(U[e]),r.linkHref=!0,d(r)}}function A(e,t){return e.match(/^([^\\]\\\\]|\\\\.)*\\]:/,!1)?(t.f=M,e.next(),n.highlightFormatting&&(t.formatting=\"link\"),t.linkText=!0,d(t)):o(e,t,h)}function M(e,t){if(e.match(\"]:\",!0)){t.f=t.inline=w,n.highlightFormatting&&(t.formatting=\"link\");var r=d(t);return t.linkText=!1,r}return e.match(/^([^\\]\\\\]|\\\\.)+/,!0),y.linkText}function w(e,t){return e.eatSpace()?null:(e.match(/^[^\\s]+/,!0),void 0===e.peek()?t.linkTitle=!0:e.match(/^(?:\\s+(?:\"(?:[^\"\\\\]|\\\\.)+\"|'(?:[^'\\\\]|\\\\.)+'|\\((?:[^)\\\\]|\\\\.)+\\)))?/,!0),t.f=t.inline=h,y.linkHref+\" url\")}var v=e.getMode(t,\"text/html\"),b=\"null\"==v.name;void 0===n.highlightFormatting&&(n.highlightFormatting=!1),void 0===n.maxBlockquoteDepth&&(n.maxBlockquoteDepth=0),void 0===n.taskLists&&(n.taskLists=!1),void 0===n.strikethrough&&(n.strikethrough=!1),void 0===n.emoji&&(n.emoji=!1),void 0===n.fencedCodeBlockHighlighting&&(n.fencedCodeBlockHighlighting=!0),void 0===n.fencedCodeBlockDefaultMode&&(n.fencedCodeBlockDefaultMode=\"text/plain\"),void 0===n.xml&&(n.xml=!0),void 0===n.tokenTypeOverrides&&(n.tokenTypeOverrides={});var y={header:\"header\",code:\"comment\",quote:\"quote\",list1:\"variable-2\",list2:\"variable-3\",list3:\"keyword\",hr:\"hr\",image:\"image\",imageAltText:\"image-alt-text\",imageMarker:\"image-marker\",formatting:\"formatting\",linkInline:\"link\",linkEmail:\"link\",linkText:\"link\",linkHref:\"string\",em:\"em\",strong:\"strong\",strikethrough:\"strikethrough\",emoji:\"builtin\"};for(var x in y)y.hasOwnProperty(x)&&n.tokenTypeOverrides[x]&&(y[x]=n.tokenTypeOverrides[x]);var T=/^([*\\-_])(?:\\s*\\1){2,}\\s*$/,C=/^(?:[*\\-+]|^[0-9]+([.)]))\\s+/,N=/^\\[(x| )\\](?=\\s)/i,I=n.allowAtxHeaderWithoutSpace?/^(#+)/:/^(#+)(?: |$)/,E=/^ {0,3}(?:\\={1,}|-{2,})\\s*$/,D=/^[^#!\\[\\]*_\\\\<>` \"'(~:]+/,S=/^(~~~+|```+)[ \\t]*([\\w\\/+#-]*)[^\\n`]*$/,L=/^\\s*\\[[^\\]]+?\\]:.*$/,k=/[!\"#$%&'()*+,\\-.\\/:;<=>?@\\[\\\\\\]^_`{|}~\\xA1\\xA7\\xAB\\xB6\\xB7\\xBB\\xBF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2308-\\u230B\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E42\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA8FC\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]|\\uD800[\\uDD00-\\uDD02\\uDF9F\\uDFD0]|\\uD801\\uDD6F|\\uD802[\\uDC57\\uDD1F\\uDD3F\\uDE50-\\uDE58\\uDE7F\\uDEF0-\\uDEF6\\uDF39-\\uDF3F\\uDF99-\\uDF9C]|\\uD804[\\uDC47-\\uDC4D\\uDCBB\\uDCBC\\uDCBE-\\uDCC1\\uDD40-\\uDD43\\uDD74\\uDD75\\uDDC5-\\uDDC9\\uDDCD\\uDDDB\\uDDDD-\\uDDDF\\uDE38-\\uDE3D\\uDEA9]|\\uD805[\\uDCC6\\uDDC1-\\uDDD7\\uDE41-\\uDE43\\uDF3C-\\uDF3E]|\\uD809[\\uDC70-\\uDC74]|\\uD81A[\\uDE6E\\uDE6F\\uDEF5\\uDF37-\\uDF3B\\uDF44]|\\uD82F\\uDC9F|\\uD836[\\uDE87-\\uDE8B]/,j=\"    \",U={\")\":/^(?:[^\\\\\\(\\)]|\\\\.|\\((?:[^\\\\\\(\\)]|\\\\.)*\\))*?(?=\\))/,\"]\":/^(?:[^\\\\\\[\\]]|\\\\.|\\[(?:[^\\\\\\[\\]]|\\\\.)*\\])*?(?=\\])/},B={startState:function(){return{f:l,prevLine:{stream:null},thisLine:{stream:null},block:l,htmlState:null,indentation:0,inline:h,text:p,formatting:!1,linkText:!1,linkHref:!1,linkTitle:!1,code:0,em:!1,strong:!1,header:0,setext:0,hr:!1,taskList:!1,list:!1,listStack:[],quote:0,trailingSpace:0,trailingSpaceNewLine:!1,strikethrough:!1,emoji:!1,fencedEndRE:null}},copyState:function(t){return{f:t.f,prevLine:t.prevLine,thisLine:t.thisLine,block:t.block,htmlState:t.htmlState&&e.copyState(v,t.htmlState),indentation:t.indentation,localMode:t.localMode,localState:t.localMode?e.copyState(t.localMode,t.localState):null,inline:t.inline,text:t.text,formatting:!1,linkText:t.linkText,linkTitle:t.linkTitle,linkHref:t.linkHref,code:t.code,em:t.em,strong:t.strong,strikethrough:t.strikethrough,emoji:t.emoji,header:t.header,setext:t.setext,hr:t.hr,taskList:t.taskList,list:t.list,listStack:t.listStack.slice(0),quote:t.quote,indentedCode:t.indentedCode,trailingSpace:t.trailingSpace,trailingSpaceNewLine:t.trailingSpaceNewLine,md_inside:t.md_inside,fencedEndRE:t.fencedEndRE}},token:function(e,t){if(t.formatting=!1,e!=t.thisLine.stream){if(t.header=0,t.hr=!1,e.match(/^\\s*$/,!0))return s(t),null;if(t.prevLine=t.thisLine,t.thisLine={stream:e},t.taskList=!1,t.trailingSpace=0,t.trailingSpaceNewLine=!1,!t.localState&&(t.f=t.block,t.f!=c)){var n=e.match(/^\\s*/,!0)[0].replace(/\\t/g,j).length;if(t.indentation=n,t.indentationDiff=null,n>0)return null}}return t.f(e,t)},innerMode:function(e){return e.block==c?{state:e.htmlState,mode:v}:e.localState?{state:e.localState,mode:e.localMode}:{state:e,mode:B}},indent:function(t,n,r){return t.block==c&&v.indent?v.indent(t.htmlState,n,r):t.localState&&t.localMode.indent?t.localMode.indent(t.localState,n,r):e.Pass},blankLine:s,getType:d,blockCommentStart:\"<!--\",blockCommentEnd:\"-->\",closeBrackets:\"()[]{}''\\\"\\\"``\",fold:\"markdown\"};return B},\"xml\"),e.defineMIME(\"text/markdown\",\"markdown\"),e.defineMIME(\"text/x-markdown\",\"markdown\")})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";e.modeInfo=[{name:\"APL\",mime:\"text/apl\",mode:\"apl\",ext:[\"dyalog\",\"apl\"]},{name:\"PGP\",mimes:[\"application/pgp\",\"application/pgp-encrypted\",\"application/pgp-keys\",\"application/pgp-signature\"],mode:\"asciiarmor\",ext:[\"asc\",\"pgp\",\"sig\"]},{name:\"ASN.1\",mime:\"text/x-ttcn-asn\",mode:\"asn.1\",ext:[\"asn\",\"asn1\"]},{name:\"Asterisk\",mime:\"text/x-asterisk\",mode:\"asterisk\",file:/^extensions\\.conf$/i},{name:\"Brainfuck\",mime:\"text/x-brainfuck\",mode:\"brainfuck\",ext:[\"b\",\"bf\"]},{name:\"C\",mime:\"text/x-csrc\",mode:\"clike\",ext:[\"c\",\"h\",\"ino\"]},{name:\"C++\",mime:\"text/x-c++src\",mode:\"clike\",ext:[\"cpp\",\"c++\",\"cc\",\"cxx\",\"hpp\",\"h++\",\"hh\",\"hxx\"],alias:[\"cpp\"]},{name:\"Cobol\",mime:\"text/x-cobol\",mode:\"cobol\",ext:[\"cob\",\"cpy\",\"cbl\"]},{name:\"C#\",mime:\"text/x-csharp\",mode:\"clike\",ext:[\"cs\"],alias:[\"csharp\",\"cs\"]},{name:\"Clojure\",mime:\"text/x-clojure\",mode:\"clojure\",ext:[\"clj\",\"cljc\",\"cljx\"]},{name:\"ClojureScript\",mime:\"text/x-clojurescript\",mode:\"clojure\",ext:[\"cljs\"]},{name:\"Closure Stylesheets (GSS)\",mime:\"text/x-gss\",mode:\"css\",ext:[\"gss\"]},{name:\"CMake\",mime:\"text/x-cmake\",mode:\"cmake\",ext:[\"cmake\",\"cmake.in\"],file:/^CMakeLists\\.txt$/},{name:\"CoffeeScript\",mimes:[\"application/vnd.coffeescript\",\"text/coffeescript\",\"text/x-coffeescript\"],mode:\"coffeescript\",ext:[\"coffee\"],alias:[\"coffee\",\"coffee-script\"]},{name:\"Common Lisp\",mime:\"text/x-common-lisp\",mode:\"commonlisp\",ext:[\"cl\",\"lisp\",\"el\"],alias:[\"lisp\"]},{name:\"Cypher\",mime:\"application/x-cypher-query\",mode:\"cypher\",ext:[\"cyp\",\"cypher\"]},{name:\"Cython\",mime:\"text/x-cython\",mode:\"python\",ext:[\"pyx\",\"pxd\",\"pxi\"]},{name:\"Crystal\",mime:\"text/x-crystal\",mode:\"crystal\",ext:[\"cr\"]},{name:\"CSS\",mime:\"text/css\",mode:\"css\",ext:[\"css\"]},{name:\"CQL\",mime:\"text/x-cassandra\",mode:\"sql\",ext:[\"cql\"]},{name:\"D\",mime:\"text/x-d\",mode:\"d\",ext:[\"d\"]},{name:\"Dart\",mimes:[\"application/dart\",\"text/x-dart\"],mode:\"dart\",ext:[\"dart\"]},{name:\"diff\",mime:\"text/x-diff\",mode:\"diff\",ext:[\"diff\",\"patch\"]},{name:\"Django\",mime:\"text/x-django\",mode:\"django\"},{name:\"Dockerfile\",mime:\"text/x-dockerfile\",mode:\"dockerfile\",file:/^Dockerfile$/},{name:\"DTD\",mime:\"application/xml-dtd\",mode:\"dtd\",ext:[\"dtd\"]},{name:\"Dylan\",mime:\"text/x-dylan\",mode:\"dylan\",ext:[\"dylan\",\"dyl\",\"intr\"]},{name:\"EBNF\",mime:\"text/x-ebnf\",mode:\"ebnf\"},{name:\"ECL\",mime:\"text/x-ecl\",mode:\"ecl\",ext:[\"ecl\"]},{name:\"edn\",mime:\"application/edn\",mode:\"clojure\",ext:[\"edn\"]},{name:\"Eiffel\",mime:\"text/x-eiffel\",mode:\"eiffel\",ext:[\"e\"]},{name:\"Elm\",mime:\"text/x-elm\",mode:\"elm\",ext:[\"elm\"]},{name:\"Embedded JavaScript\",mime:\"application/x-ejs\",mode:\"htmlembedded\",ext:[\"ejs\"]},{name:\"Embedded Ruby\",mime:\"application/x-erb\",mode:\"htmlembedded\",ext:[\"erb\"]},{name:\"Erlang\",mime:\"text/x-erlang\",mode:\"erlang\",ext:[\"erl\"]},{name:\"Esper\",mime:\"text/x-esper\",mode:\"sql\"},{name:\"Factor\",mime:\"text/x-factor\",mode:\"factor\",ext:[\"factor\"]},{name:\"FCL\",mime:\"text/x-fcl\",mode:\"fcl\"},{name:\"Forth\",mime:\"text/x-forth\",mode:\"forth\",ext:[\"forth\",\"fth\",\"4th\"]},{name:\"Fortran\",mime:\"text/x-fortran\",mode:\"fortran\",ext:[\"f\",\"for\",\"f77\",\"f90\",\"f95\"]},{name:\"F#\",mime:\"text/x-fsharp\",mode:\"mllike\",ext:[\"fs\"],alias:[\"fsharp\"]},{name:\"Gas\",mime:\"text/x-gas\",mode:\"gas\",ext:[\"s\"]},{name:\"Gherkin\",mime:\"text/x-feature\",mode:\"gherkin\",ext:[\"feature\"]},{name:\"GitHub Flavored Markdown\",mime:\"text/x-gfm\",mode:\"gfm\",file:/^(readme|contributing|history)\\.md$/i},{name:\"Go\",mime:\"text/x-go\",mode:\"go\",ext:[\"go\"]},{name:\"Groovy\",mime:\"text/x-groovy\",mode:\"groovy\",ext:[\"groovy\",\"gradle\"],file:/^Jenkinsfile$/},{name:\"HAML\",mime:\"text/x-haml\",mode:\"haml\",ext:[\"haml\"]},{name:\"Haskell\",mime:\"text/x-haskell\",mode:\"haskell\",ext:[\"hs\"]},{name:\"Haskell (Literate)\",mime:\"text/x-literate-haskell\",mode:\"haskell-literate\",ext:[\"lhs\"]},{name:\"Haxe\",mime:\"text/x-haxe\",mode:\"haxe\",ext:[\"hx\"]},{name:\"HXML\",mime:\"text/x-hxml\",mode:\"haxe\",ext:[\"hxml\"]},{name:\"ASP.NET\",mime:\"application/x-aspx\",mode:\"htmlembedded\",ext:[\"aspx\"],alias:[\"asp\",\"aspx\"]},{name:\"HTML\",mime:\"text/html\",mode:\"htmlmixed\",ext:[\"html\",\"htm\",\"handlebars\",\"hbs\"],alias:[\"xhtml\"]},{name:\"HTTP\",mime:\"message/http\",mode:\"http\"},{name:\"IDL\",mime:\"text/x-idl\",mode:\"idl\",ext:[\"pro\"]},{name:\"Pug\",mime:\"text/x-pug\",mode:\"pug\",ext:[\"jade\",\"pug\"],alias:[\"jade\"]},{name:\"Java\",mime:\"text/x-java\",mode:\"clike\",ext:[\"java\"]},{name:\"Java Server Pages\",mime:\"application/x-jsp\",mode:\"htmlembedded\",ext:[\"jsp\"],alias:[\"jsp\"]},{name:\"JavaScript\",mimes:[\"text/javascript\",\"text/ecmascript\",\"application/javascript\",\"application/x-javascript\",\"application/ecmascript\"],mode:\"javascript\",ext:[\"js\"],alias:[\"ecmascript\",\"js\",\"node\"]},{name:\"JSON\",mimes:[\"application/json\",\"application/x-json\"],mode:\"javascript\",ext:[\"json\",\"map\"],alias:[\"json5\"]},{name:\"JSON-LD\",mime:\"application/ld+json\",mode:\"javascript\",ext:[\"jsonld\"],alias:[\"jsonld\"]},{name:\"JSX\",mime:\"text/jsx\",mode:\"jsx\",ext:[\"jsx\"]},{name:\"Jinja2\",mime:\"text/jinja2\",mode:\"jinja2\",ext:[\"j2\",\"jinja\",\"jinja2\"]},{name:\"Julia\",mime:\"text/x-julia\",mode:\"julia\",ext:[\"jl\"],alias:[\"jl\"]},{name:\"Kotlin\",mime:\"text/x-kotlin\",mode:\"clike\",ext:[\"kt\"]},{name:\"LESS\",mime:\"text/x-less\",mode:\"css\",ext:[\"less\"]},{name:\"LiveScript\",mime:\"text/x-livescript\",mode:\"livescript\",ext:[\"ls\"],alias:[\"ls\"]},{name:\"Lua\",mime:\"text/x-lua\",mode:\"lua\",ext:[\"lua\"]},{name:\"Markdown\",mime:\"text/x-markdown\",mode:\"markdown\",ext:[\"markdown\",\"md\",\"mkd\"]},{name:\"mIRC\",mime:\"text/mirc\",mode:\"mirc\"},{name:\"MariaDB SQL\",mime:\"text/x-mariadb\",mode:\"sql\"},{name:\"Mathematica\",mime:\"text/x-mathematica\",mode:\"mathematica\",ext:[\"m\",\"nb\",\"wl\",\"wls\"]},{name:\"Modelica\",mime:\"text/x-modelica\",mode:\"modelica\",ext:[\"mo\"]},{name:\"MUMPS\",mime:\"text/x-mumps\",mode:\"mumps\",ext:[\"mps\"]},{name:\"MS SQL\",mime:\"text/x-mssql\",mode:\"sql\"},{name:\"mbox\",mime:\"application/mbox\",mode:\"mbox\",ext:[\"mbox\"]},{name:\"MySQL\",mime:\"text/x-mysql\",mode:\"sql\"},{name:\"Nginx\",mime:\"text/x-nginx-conf\",mode:\"nginx\",file:/nginx.*\\.conf$/i},{name:\"NSIS\",mime:\"text/x-nsis\",mode:\"nsis\",ext:[\"nsh\",\"nsi\"]},{name:\"NTriples\",mimes:[\"application/n-triples\",\"application/n-quads\",\"text/n-triples\"],mode:\"ntriples\",ext:[\"nt\",\"nq\"]},{name:\"Objective-C\",mime:\"text/x-objectivec\",mode:\"clike\",ext:[\"m\"],alias:[\"objective-c\",\"objc\"]},{name:\"Objective-C++\",mime:\"text/x-objectivec++\",mode:\"clike\",ext:[\"mm\"],alias:[\"objective-c++\",\"objc++\"]},{name:\"OCaml\",mime:\"text/x-ocaml\",mode:\"mllike\",ext:[\"ml\",\"mli\",\"mll\",\"mly\"]},{name:\"Octave\",mime:\"text/x-octave\",mode:\"octave\",ext:[\"m\"]},{name:\"Oz\",mime:\"text/x-oz\",mode:\"oz\",ext:[\"oz\"]},{name:\"Pascal\",mime:\"text/x-pascal\",mode:\"pascal\",ext:[\"p\",\"pas\"]},{name:\"PEG.js\",\nmime:\"null\",mode:\"pegjs\",ext:[\"jsonld\"]},{name:\"Perl\",mime:\"text/x-perl\",mode:\"perl\",ext:[\"pl\",\"pm\"]},{name:\"PHP\",mimes:[\"text/x-php\",\"application/x-httpd-php\",\"application/x-httpd-php-open\"],mode:\"php\",ext:[\"php\",\"php3\",\"php4\",\"php5\",\"php7\",\"phtml\"]},{name:\"Pig\",mime:\"text/x-pig\",mode:\"pig\",ext:[\"pig\"]},{name:\"Plain Text\",mime:\"text/plain\",mode:\"null\",ext:[\"txt\",\"text\",\"conf\",\"def\",\"list\",\"log\"]},{name:\"PLSQL\",mime:\"text/x-plsql\",mode:\"sql\",ext:[\"pls\"]},{name:\"PostgreSQL\",mime:\"text/x-pgsql\",mode:\"sql\"},{name:\"PowerShell\",mime:\"application/x-powershell\",mode:\"powershell\",ext:[\"ps1\",\"psd1\",\"psm1\"]},{name:\"Properties files\",mime:\"text/x-properties\",mode:\"properties\",ext:[\"properties\",\"ini\",\"in\"],alias:[\"ini\",\"properties\"]},{name:\"ProtoBuf\",mime:\"text/x-protobuf\",mode:\"protobuf\",ext:[\"proto\"]},{name:\"Python\",mime:\"text/x-python\",mode:\"python\",ext:[\"BUILD\",\"bzl\",\"py\",\"pyw\"],file:/^(BUCK|BUILD)$/},{name:\"Puppet\",mime:\"text/x-puppet\",mode:\"puppet\",ext:[\"pp\"]},{name:\"Q\",mime:\"text/x-q\",mode:\"q\",ext:[\"q\"]},{name:\"R\",mime:\"text/x-rsrc\",mode:\"r\",ext:[\"r\",\"R\"],alias:[\"rscript\"]},{name:\"reStructuredText\",mime:\"text/x-rst\",mode:\"rst\",ext:[\"rst\"],alias:[\"rst\"]},{name:\"RPM Changes\",mime:\"text/x-rpm-changes\",mode:\"rpm\"},{name:\"RPM Spec\",mime:\"text/x-rpm-spec\",mode:\"rpm\",ext:[\"spec\"]},{name:\"Ruby\",mime:\"text/x-ruby\",mode:\"ruby\",ext:[\"rb\"],alias:[\"jruby\",\"macruby\",\"rake\",\"rb\",\"rbx\"]},{name:\"Rust\",mime:\"text/x-rustsrc\",mode:\"rust\",ext:[\"rs\"]},{name:\"SAS\",mime:\"text/x-sas\",mode:\"sas\",ext:[\"sas\"]},{name:\"Sass\",mime:\"text/x-sass\",mode:\"sass\",ext:[\"sass\"]},{name:\"Scala\",mime:\"text/x-scala\",mode:\"clike\",ext:[\"scala\"]},{name:\"Scheme\",mime:\"text/x-scheme\",mode:\"scheme\",ext:[\"scm\",\"ss\"]},{name:\"SCSS\",mime:\"text/x-scss\",mode:\"css\",ext:[\"scss\"]},{name:\"Shell\",mimes:[\"text/x-sh\",\"application/x-sh\"],mode:\"shell\",ext:[\"sh\",\"ksh\",\"bash\"],alias:[\"bash\",\"sh\",\"zsh\"],file:/^PKGBUILD$/},{name:\"Sieve\",mime:\"application/sieve\",mode:\"sieve\",ext:[\"siv\",\"sieve\"]},{name:\"Slim\",mimes:[\"text/x-slim\",\"application/x-slim\"],mode:\"slim\",ext:[\"slim\"]},{name:\"Smalltalk\",mime:\"text/x-stsrc\",mode:\"smalltalk\",ext:[\"st\"]},{name:\"Smarty\",mime:\"text/x-smarty\",mode:\"smarty\",ext:[\"tpl\"]},{name:\"Solr\",mime:\"text/x-solr\",mode:\"solr\"},{name:\"SML\",mime:\"text/x-sml\",mode:\"mllike\",ext:[\"sml\",\"sig\",\"fun\",\"smackspec\"]},{name:\"Soy\",mime:\"text/x-soy\",mode:\"soy\",ext:[\"soy\"],alias:[\"closure template\"]},{name:\"SPARQL\",mime:\"application/sparql-query\",mode:\"sparql\",ext:[\"rq\",\"sparql\"],alias:[\"sparul\"]},{name:\"Spreadsheet\",mime:\"text/x-spreadsheet\",mode:\"spreadsheet\",alias:[\"excel\",\"formula\"]},{name:\"SQL\",mime:\"text/x-sql\",mode:\"sql\",ext:[\"sql\"]},{name:\"SQLite\",mime:\"text/x-sqlite\",mode:\"sql\"},{name:\"Squirrel\",mime:\"text/x-squirrel\",mode:\"clike\",ext:[\"nut\"]},{name:\"Stylus\",mime:\"text/x-styl\",mode:\"stylus\",ext:[\"styl\"]},{name:\"Swift\",mime:\"text/x-swift\",mode:\"swift\",ext:[\"swift\"]},{name:\"sTeX\",mime:\"text/x-stex\",mode:\"stex\"},{name:\"LaTeX\",mime:\"text/x-latex\",mode:\"stex\",ext:[\"text\",\"ltx\",\"tex\"],alias:[\"tex\"]},{name:\"SystemVerilog\",mime:\"text/x-systemverilog\",mode:\"verilog\",ext:[\"v\",\"sv\",\"svh\"]},{name:\"Tcl\",mime:\"text/x-tcl\",mode:\"tcl\",ext:[\"tcl\"]},{name:\"Textile\",mime:\"text/x-textile\",mode:\"textile\",ext:[\"textile\"]},{name:\"TiddlyWiki\",mime:\"text/x-tiddlywiki\",mode:\"tiddlywiki\"},{name:\"Tiki wiki\",mime:\"text/tiki\",mode:\"tiki\"},{name:\"TOML\",mime:\"text/x-toml\",mode:\"toml\",ext:[\"toml\"]},{name:\"Tornado\",mime:\"text/x-tornado\",mode:\"tornado\"},{name:\"troff\",mime:\"text/troff\",mode:\"troff\",ext:[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\"]},{name:\"TTCN\",mime:\"text/x-ttcn\",mode:\"ttcn\",ext:[\"ttcn\",\"ttcn3\",\"ttcnpp\"]},{name:\"TTCN_CFG\",mime:\"text/x-ttcn-cfg\",mode:\"ttcn-cfg\",ext:[\"cfg\"]},{name:\"Turtle\",mime:\"text/turtle\",mode:\"turtle\",ext:[\"ttl\"]},{name:\"TypeScript\",mime:\"application/typescript\",mode:\"javascript\",ext:[\"ts\"],alias:[\"ts\"]},{name:\"TypeScript-JSX\",mime:\"text/typescript-jsx\",mode:\"jsx\",ext:[\"tsx\"],alias:[\"tsx\"]},{name:\"Twig\",mime:\"text/x-twig\",mode:\"twig\"},{name:\"Web IDL\",mime:\"text/x-webidl\",mode:\"webidl\",ext:[\"webidl\"]},{name:\"VB.NET\",mime:\"text/x-vb\",mode:\"vb\",ext:[\"vb\"]},{name:\"VBScript\",mime:\"text/vbscript\",mode:\"vbscript\",ext:[\"vbs\"]},{name:\"Velocity\",mime:\"text/velocity\",mode:\"velocity\",ext:[\"vtl\"]},{name:\"Verilog\",mime:\"text/x-verilog\",mode:\"verilog\",ext:[\"v\"]},{name:\"VHDL\",mime:\"text/x-vhdl\",mode:\"vhdl\",ext:[\"vhd\",\"vhdl\"]},{name:\"Vue.js Component\",mimes:[\"script/x-vue\",\"text/x-vue\"],mode:\"vue\",ext:[\"vue\"]},{name:\"XML\",mimes:[\"application/xml\",\"text/xml\"],mode:\"xml\",ext:[\"xml\",\"xsl\",\"xsd\",\"svg\"],alias:[\"rss\",\"wsdl\",\"xsd\"]},{name:\"XQuery\",mime:\"application/xquery\",mode:\"xquery\",ext:[\"xy\",\"xquery\"]},{name:\"Yacas\",mime:\"text/x-yacas\",mode:\"yacas\",ext:[\"ys\"]},{name:\"YAML\",mimes:[\"text/x-yaml\",\"text/yaml\"],mode:\"yaml\",ext:[\"yaml\",\"yml\"],alias:[\"yml\"]},{name:\"Z80\",mime:\"text/x-z80\",mode:\"z80\",ext:[\"z80\"]},{name:\"mscgen\",mime:\"text/x-mscgen\",mode:\"mscgen\",ext:[\"mscgen\",\"mscin\",\"msc\"]},{name:\"xu\",mime:\"text/x-xu\",mode:\"mscgen\",ext:[\"xu\"]},{name:\"msgenny\",mime:\"text/x-msgenny\",mode:\"mscgen\",ext:[\"msgenny\"]},{name:\"WebAssembly\",mime:\"text/webassembly\",mode:\"wast\",ext:[\"wat\",\"wast\"]}];for(var t=0;t<e.modeInfo.length;t++){var n=e.modeInfo[t];n.mimes&&(n.mime=n.mimes[0])}e.findModeByMIME=function(t){t=t.toLowerCase();for(var n=0;n<e.modeInfo.length;n++){var r=e.modeInfo[n];if(r.mime==t)return r;if(r.mimes)for(var o=0;o<r.mimes.length;o++)if(r.mimes[o]==t)return r}return/\\+xml$/.test(t)?e.findModeByMIME(\"application/xml\"):/\\+json$/.test(t)?e.findModeByMIME(\"application/json\"):void 0},e.findModeByExtension=function(t){t=t.toLowerCase();for(var n=0;n<e.modeInfo.length;n++){var r=e.modeInfo[n];if(r.ext)for(var o=0;o<r.ext.length;o++)if(r.ext[o]==t)return r}},e.findModeByFileName=function(t){for(var n=0;n<e.modeInfo.length;n++){var r=e.modeInfo[n];if(r.file&&r.file.test(t))return r}var o=t.lastIndexOf(\".\"),i=o>-1&&t.substring(o+1,t.length);return i?e.findModeByExtension(i):void 0},e.findModeByName=function(t){t=t.toLowerCase();for(var n=0;n<e.modeInfo.length;n++){var r=e.modeInfo[n];if(r.name.toLowerCase()==t)return r;if(r.alias)for(var o=0;o<r.alias.length;o++)if(r.alias[o].toLowerCase()==t)return r}}})},function(e,t,n){!function(e){e(n(268))}(function(e){function t(t,n,r){var o,i=t.getWrapperElement();return o=i.appendChild(document.createElement(\"div\")),r?o.className=\"CodeMirror-dialog CodeMirror-dialog-bottom\":o.className=\"CodeMirror-dialog CodeMirror-dialog-top\",\"string\"==typeof n?o.innerHTML=n:o.appendChild(n),e.addClass(i,\"dialog-opened\"),o}function n(e,t){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=t}e.defineExtension(\"openDialog\",function(r,o,i){function a(t){if(\"string\"==typeof t)d.value=t;else{if(c)return;c=!0,e.rmClass(l.parentNode,\"dialog-opened\"),l.parentNode.removeChild(l),u.focus(),i.onClose&&i.onClose(l)}}i||(i={}),n(this,null);var s,l=t(this,r,i.bottom),c=!1,u=this,d=l.getElementsByTagName(\"input\")[0];return d?(d.focus(),i.value&&(d.value=i.value,i.selectValueOnOpen!==!1&&d.select()),i.onInput&&e.on(d,\"input\",function(e){i.onInput(e,d.value,a)}),i.onKeyUp&&e.on(d,\"keyup\",function(e){i.onKeyUp(e,d.value,a)}),e.on(d,\"keydown\",function(t){i&&i.onKeyDown&&i.onKeyDown(t,d.value,a)||((27==t.keyCode||i.closeOnEnter!==!1&&13==t.keyCode)&&(d.blur(),e.e_stop(t),a()),13==t.keyCode&&o(d.value,t))}),i.closeOnBlur!==!1&&e.on(l,\"focusout\",function(e){null!==e.relatedTarget&&a()})):(s=l.getElementsByTagName(\"button\")[0])&&(e.on(s,\"click\",function(){a(),u.focus()}),i.closeOnBlur!==!1&&e.on(s,\"blur\",a),s.focus()),a}),e.defineExtension(\"openConfirm\",function(r,o,i){function a(){c||(c=!0,e.rmClass(s.parentNode,\"dialog-opened\"),s.parentNode.removeChild(s),u.focus())}n(this,null);var s=t(this,r,i&&i.bottom),l=s.getElementsByTagName(\"button\"),c=!1,u=this,d=1;l[0].focus();for(var p=0;p<l.length;++p){var h=l[p];!function(t){e.on(h,\"click\",function(n){e.e_preventDefault(n),a(),t&&t(u)})}(o[p]),e.on(h,\"blur\",function(){--d,setTimeout(function(){0>=d&&a()},200)}),e.on(h,\"focus\",function(){++d})}}),e.defineExtension(\"openNotification\",function(r,o){function i(){l||(l=!0,clearTimeout(a),e.rmClass(s.parentNode,\"dialog-opened\"),s.parentNode.removeChild(s))}n(this,i);var a,s=t(this,r,o&&o.bottom),l=!1,c=o&&\"undefined\"!=typeof o.duration?o.duration:5e3;return e.on(s,\"click\",function(t){e.e_preventDefault(t),i()}),c&&(a=setTimeout(i,c)),i})})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(e){var t=e.flags;return null!=t?t:(e.ignoreCase?\"i\":\"\")+(e.global?\"g\":\"\")+(e.multiline?\"m\":\"\")}function n(e,n){for(var r=t(e),o=r,i=0;i<n.length;i++)-1==o.indexOf(n.charAt(i))&&(o+=n.charAt(i));return r==o?e:new RegExp(e.source,o)}function r(e){return/\\\\s|\\\\n|\\n|\\\\W|\\\\D|\\[\\^/.test(e.source)}function o(e,t,r){t=n(t,\"g\");for(var o=r.line,i=r.ch,a=e.lastLine();a>=o;o++,i=0){t.lastIndex=i;var s=e.getLine(o),l=t.exec(s);if(l)return{from:f(o,l.index),to:f(o,l.index+l[0].length),match:l}}}function i(e,t,i){if(!r(t))return o(e,t,i);t=n(t,\"gm\");for(var a,s=1,l=i.line,c=e.lastLine();c>=l;){for(var u=0;s>u&&!(l>c);u++){var d=e.getLine(l++);a=null==a?d:a+\"\\n\"+d}s=2*s,t.lastIndex=i.ch;var p=t.exec(a);if(p){var h=a.slice(0,p.index).split(\"\\n\"),g=p[0].split(\"\\n\"),m=i.line+h.length-1,A=h[h.length-1].length;return{from:f(m,A),to:f(m+g.length-1,1==g.length?A+g[0].length:g[g.length-1].length),match:p}}}}function a(e,t,n){for(var r,o=0;o<=e.length;){t.lastIndex=o;var i=t.exec(e);if(!i)break;var a=i.index+i[0].length;if(a>e.length-n)break;(!r||a>r.index+r[0].length)&&(r=i),o=i.index+1}return r}function s(e,t,r){t=n(t,\"g\");for(var o=r.line,i=r.ch,s=e.firstLine();o>=s;o--,i=-1){var l=e.getLine(o),c=a(l,t,0>i?0:l.length-i);if(c)return{from:f(o,c.index),to:f(o,c.index+c[0].length),match:c}}}function l(e,t,o){if(!r(t))return s(e,t,o);t=n(t,\"gm\");for(var i,l=1,c=e.getLine(o.line).length-o.ch,u=o.line,d=e.firstLine();u>=d;){for(var p=0;l>p&&u>=d;p++){var h=e.getLine(u--);i=null==i?h:h+\"\\n\"+i}l*=2;var g=a(i,t,c);if(g){var m=i.slice(0,g.index).split(\"\\n\"),A=g[0].split(\"\\n\"),M=u+m.length,w=m[m.length-1].length;return{from:f(M,w),to:f(M+A.length-1,1==A.length?w+A[0].length:A[A.length-1].length),match:g}}}}function c(e,t,n,r){if(e.length==t.length)return n;for(var o=0,i=n+Math.max(0,e.length-t.length);;){if(o==i)return o;var a=o+i>>1,s=r(e.slice(0,a)).length;if(s==n)return a;s>n?i=a:o=a+1}}function u(e,t,n,r){if(!t.length)return null;var o=r?h:g,i=o(t).split(/\\r|\\n\\r?/);e:for(var a=n.line,s=n.ch,l=e.lastLine()+1-i.length;l>=a;a++,s=0){var u=e.getLine(a).slice(s),d=o(u);if(1==i.length){var p=d.indexOf(i[0]);if(-1==p)continue e;var n=c(u,d,p,o)+s;return{from:f(a,c(u,d,p,o)+s),to:f(a,c(u,d,p+i[0].length,o)+s)}}var m=d.length-i[0].length;if(d.slice(m)==i[0]){for(var A=1;A<i.length-1;A++)if(o(e.getLine(a+A))!=i[A])continue e;var M=e.getLine(a+i.length-1),w=o(M),v=i[i.length-1];if(w.slice(0,v.length)==v)return{from:f(a,c(u,d,m,o)+s),to:f(a+i.length-1,c(M,w,v.length,o))}}}}function d(e,t,n,r){if(!t.length)return null;var o=r?h:g,i=o(t).split(/\\r|\\n\\r?/);e:for(var a=n.line,s=n.ch,l=e.firstLine()-1+i.length;a>=l;a--,s=-1){var u=e.getLine(a);s>-1&&(u=u.slice(0,s));var d=o(u);if(1==i.length){var p=d.lastIndexOf(i[0]);if(-1==p)continue e;return{from:f(a,c(u,d,p,o)),to:f(a,c(u,d,p+i[0].length,o))}}var m=i[i.length-1];if(d.slice(0,m.length)==m){for(var A=1,n=a-i.length+1;A<i.length-1;A++)if(o(e.getLine(n+A))!=i[A])continue e;var M=e.getLine(a+1-i.length),w=o(M);if(w.slice(w.length-i[0].length)==i[0])return{from:f(a+1-i.length,c(M,w,M.length-i[0].length,o)),to:f(a,c(u,d,m.length,o))}}}}function p(e,t,r,a){this.atOccurrence=!1,this.afterEmptyMatch=!1,this.doc=e,r=r?e.clipPos(r):f(0,0),this.pos={from:r,to:r};var c;\"object\"==typeof a?c=a.caseFold:(c=a,a=null),\"string\"==typeof t?(null==c&&(c=!1),this.matches=function(n,r){return(n?d:u)(e,t,r,c)}):(t=n(t,\"gm\"),a&&a.multiline===!1?this.matches=function(n,r){return(n?s:o)(e,t,r)}:this.matches=function(n,r){return(n?l:i)(e,t,r)})}var h,g,f=e.Pos;String.prototype.normalize?(h=function(e){return e.normalize(\"NFD\").toLowerCase()},g=function(e){return e.normalize(\"NFD\")}):(h=function(e){return e.toLowerCase()},g=function(e){return e}),p.prototype={findNext:function(){return this.find(!1)},findPrevious:function(){return this.find(!0)},find:function(t){var n=this.doc.clipPos(t?this.pos.from:this.pos.to);if(this.afterEmptyMatch&&this.atOccurrence&&(n=f(n.line,n.ch),t?(n.ch--,n.ch<0&&(n.line--,n.ch=(this.doc.getLine(n.line)||\"\").length)):(n.ch++,n.ch>(this.doc.getLine(n.line)||\"\").length&&(n.ch=0,n.line++)),0!=e.cmpPos(n,this.doc.clipPos(n))))return this.atOccurrence=!1;var r=this.matches(t,n);if(this.afterEmptyMatch=r&&0==e.cmpPos(r.from,r.to),r)return this.pos=r,this.atOccurrence=!0,this.pos.match||!0;var o=f(t?this.doc.firstLine():this.doc.lastLine()+1,0);return this.pos={from:o,to:o},this.atOccurrence=!1},from:function(){return this.atOccurrence?this.pos.from:void 0},to:function(){return this.atOccurrence?this.pos.to:void 0},replace:function(t,n){if(this.atOccurrence){var r=e.splitLines(t);this.doc.replaceRange(r,this.pos.from,this.pos.to,n),this.pos.to=f(this.pos.from.line+r.length-1,r[r.length-1].length+(1==r.length?this.pos.from.ch:0))}}},e.defineExtension(\"getSearchCursor\",function(e,t,n){return new p(this.doc,e,t,n)}),e.defineDocExtension(\"getSearchCursor\",function(e,t,n){return new p(this,e,t,n)}),e.defineExtension(\"selectMatches\",function(t,n){for(var r=[],o=this.getSearchCursor(t,this.getCursor(\"from\"),n);o.findNext()&&!(e.cmpPos(o.to(),this.getCursor(\"to\"))>0);)r.push({anchor:o.from(),head:o.to()});r.length&&this.setSelections(r,0)})})},function(e,t,n){!function(e){e(n(268),n(276),n(275))}(function(e){\"use strict\";function t(e,t){return\"string\"==typeof e?e=new RegExp(e.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g,\"\\\\$&\"),t?\"gi\":\"g\"):e.global||(e=new RegExp(e.source,e.ignoreCase?\"gi\":\"g\")),{token:function(t){e.lastIndex=t.pos;var n=e.exec(t.string);return n&&n.index==t.pos?(t.pos+=n[0].length||1,\"searching\"):void(n?t.pos=n.index:t.skipToEnd())}}}function n(){this.posFrom=this.posTo=this.lastQuery=this.query=null,this.overlay=null}function r(e){return e.state.search||(e.state.search=new n)}function o(e){return\"string\"==typeof e&&e==e.toLowerCase()}function i(e,t,n){return e.getSearchCursor(t,n,{caseFold:o(t),multiline:!0})}function a(e,t,n,r,o){e.openDialog(t,r,{value:n,selectValueOnOpen:!0,closeOnEnter:!1,onClose:function(){g(e)},onKeyDown:o,bottom:e.options.search.bottom})}function s(e,t,n,r,o){e.openDialog?e.openDialog(t,o,{value:r,selectValueOnOpen:!0,bottom:e.options.search.bottom}):o(prompt(n,r))}function l(e,t,n,r){e.openConfirm?e.openConfirm(t,r):confirm(n)&&r[0]()}function c(e){return e.replace(/\\\\([nrt\\\\])/g,function(e,t){return\"n\"==t?\"\\n\":\"r\"==t?\"\\r\":\"t\"==t?\"\t\":\"\\\\\"==t?\"\\\\\":e})}function u(e){var t=e.match(/^\\/(.*)\\/([a-z]*)$/);if(t)try{e=new RegExp(t[1],-1==t[2].indexOf(\"i\")?\"\":\"i\")}catch(n){}else e=c(e);return(\"string\"==typeof e?\"\"==e:e.test(\"\"))&&(e=/x^/),e}function d(e,n,r){n.queryText=r,n.query=u(r),e.removeOverlay(n.overlay,o(n.query)),n.overlay=t(n.query,o(n.query)),e.addOverlay(n.overlay),e.showMatchesOnScrollbar&&(n.annotate&&(n.annotate.clear(),n.annotate=null),n.annotate=e.showMatchesOnScrollbar(n.query,o(n.query)))}function p(t,n,o,i){var l=r(t);if(l.query)return h(t,n);var c=t.getSelection()||l.lastQuery;if(c instanceof RegExp&&\"x^\"==c.source&&(c=null),o&&t.openDialog){var u=null,p=function(n,r){e.e_stop(r),n&&(n!=l.queryText&&(d(t,l,n),l.posFrom=l.posTo=t.getCursor()),u&&(u.style.opacity=1),h(t,r.shiftKey,function(e,n){var r;n.line<3&&document.querySelector&&(r=t.display.wrapper.querySelector(\".CodeMirror-dialog\"))&&r.getBoundingClientRect().bottom-4>t.cursorCoords(n,\"window\").top&&((u=r).style.opacity=.4)}))};a(t,m(t),c,p,function(n,o){var i=e.keyName(n),a=t.getOption(\"extraKeys\"),s=a&&a[i]||e.keyMap[t.getOption(\"keyMap\")][i];\"findNext\"==s||\"findPrev\"==s||\"findPersistentNext\"==s||\"findPersistentPrev\"==s?(e.e_stop(n),d(t,r(t),o),t.execCommand(s)):(\"find\"==s||\"findPersistent\"==s)&&(e.e_stop(n),p(o,n))}),i&&c&&(d(t,l,c),h(t,n))}else s(t,m(t),\"Search for:\",c,function(e){e&&!l.query&&t.operation(function(){d(t,l,e),l.posFrom=l.posTo=t.getCursor(),h(t,n)})})}function h(t,n,o){t.operation(function(){var a=r(t),s=i(t,a.query,n?a.posFrom:a.posTo);(s.find(n)||(s=i(t,a.query,n?e.Pos(t.lastLine()):e.Pos(t.firstLine(),0)),s.find(n)))&&(t.setSelection(s.from(),s.to()),t.scrollIntoView({from:s.from(),to:s.to()},20),a.posFrom=s.from(),a.posTo=s.to(),o&&o(s.from(),s.to()))})}function g(e){e.operation(function(){var t=r(e);t.lastQuery=t.query,t.query&&(t.query=t.queryText=null,e.removeOverlay(t.overlay),t.annotate&&(t.annotate.clear(),t.annotate=null))})}function f(e,t){var n=e?document.createElement(e):document.createDocumentFragment();for(var r in t)n[r]=t[r];for(var o=2;o<arguments.length;o++){var i=arguments[o];n.appendChild(\"string\"==typeof i?document.createTextNode(i):i)}return n}function m(e){var t=f(\"label\",{className:\"CodeMirror-search-label\"},e.phrase(\"Search:\"),f(\"input\",{type:\"text\",style:\"width: 10em\",className:\"CodeMirror-search-field\",id:\"CodeMirror-search-field\"}));return t.setAttribute(\"for\",\"CodeMirror-search-field\"),f(\"\",null,t,\" \",f(\"span\",{style:\"color: #666\",className:\"CodeMirror-search-hint\"},e.phrase(\"(Use /re/ syntax for regexp search)\")))}function A(e){return f(\"\",null,\" \",f(\"input\",{type:\"text\",style:\"width: 10em\",className:\"CodeMirror-search-field\"}),\" \",f(\"span\",{style:\"color: #666\",className:\"CodeMirror-search-hint\"},e.phrase(\"(Use /re/ syntax for regexp search)\")))}function M(e){return f(\"\",null,f(\"span\",{className:\"CodeMirror-search-label\"},e.phrase(\"With:\")),\" \",f(\"input\",{type:\"text\",style:\"width: 10em\",className:\"CodeMirror-search-field\"}))}function w(e){return f(\"\",null,f(\"span\",{className:\"CodeMirror-search-label\"},e.phrase(\"Replace?\")),\" \",f(\"button\",{},e.phrase(\"Yes\")),\" \",f(\"button\",{},e.phrase(\"No\")),\" \",f(\"button\",{},e.phrase(\"All\")),\" \",f(\"button\",{},e.phrase(\"Stop\")))}function v(e,t,n){e.operation(function(){for(var r=i(e,t);r.findNext();)if(\"string\"!=typeof t){var o=e.getRange(r.from(),r.to()).match(t);r.replace(n.replace(/\\$(\\d)/g,function(e,t){return o[t]}))}else r.replace(n)})}function b(e,t){if(!e.getOption(\"readOnly\")){var n=e.getSelection()||r(e).lastQuery,o=t?e.phrase(\"Replace all:\"):e.phrase(\"Replace:\"),a=f(\"\",null,f(\"span\",{className:\"CodeMirror-search-label\"},o),A(e));s(e,a,o,n,function(n){n&&(n=u(n),s(e,M(e),e.phrase(\"Replace with:\"),\"\",function(r){if(r=c(r),t)v(e,n,r);else{g(e);var o=i(e,n,e.getCursor(\"from\")),a=function(){var t,c=o.from();!(t=o.findNext())&&(o=i(e,n),!(t=o.findNext())||c&&o.from().line==c.line&&o.from().ch==c.ch)||(e.setSelection(o.from(),o.to()),e.scrollIntoView({from:o.from(),to:o.to()}),l(e,w(e),e.phrase(\"Replace?\"),[function(){s(t)},a,function(){v(e,n,r)}]))},s=function(e){o.replace(\"string\"==typeof n?r:r.replace(/\\$(\\d)/g,function(t,n){return e[n]})),a()};a()}}))})}}e.defineOption(\"search\",{bottom:!1}),e.commands.find=function(e){g(e),p(e)},e.commands.findPersistent=function(e){g(e),p(e,!1,!0)},e.commands.findPersistentNext=function(e){p(e,!1,!0,!0)},e.commands.findPersistentPrev=function(e){p(e,!0,!0,!0)},e.commands.findNext=p,e.commands.findPrev=function(e){p(e,!0)},e.commands.clearSearch=g,e.commands.replace=b,e.commands.replaceAll=function(e){b(e,!0)}})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(e,t){function n(e){clearTimeout(r.doRedraw),r.doRedraw=setTimeout(function(){r.redraw()},e)}this.cm=e,this.options=t,this.buttonHeight=t.scrollButtonHeight||e.getOption(\"scrollButtonHeight\"),this.annotations=[],this.doRedraw=this.doUpdate=null,this.div=e.getWrapperElement().appendChild(document.createElement(\"div\")),this.div.style.cssText=\"position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none\",this.computeScale();var r=this;e.on(\"refresh\",this.resizeHandler=function(){clearTimeout(r.doUpdate),r.doUpdate=setTimeout(function(){r.computeScale()&&n(20)},100)}),e.on(\"markerAdded\",this.resizeHandler),e.on(\"markerCleared\",this.resizeHandler),t.listenForChanges!==!1&&e.on(\"changes\",this.changeHandler=function(){n(250)})}e.defineExtension(\"annotateScrollbar\",function(e){return\"string\"==typeof e&&(e={className:e}),new t(this,e)}),e.defineOption(\"scrollButtonHeight\",0),t.prototype.computeScale=function(){var e=this.cm,t=(e.getWrapperElement().clientHeight-e.display.barHeight-2*this.buttonHeight)/e.getScrollerElement().scrollHeight;return t!=this.hScale?(this.hScale=t,!0):void 0},t.prototype.update=function(e){this.annotations=e,this.redraw()},t.prototype.redraw=function(e){function t(e,t){if(l!=e.line){l=e.line,c=n.getLineHandle(e.line);var r=n.getLineHandleVisualStart(c);r!=c&&(l=n.getLineNumber(r),c=r)}if(c.widgets&&c.widgets.length||a&&c.height>s)return n.charCoords(e,\"local\")[t?\"top\":\"bottom\"];var o=n.heightAtLine(c,\"local\");return o+(t?0:c.height)}e!==!1&&this.computeScale();var n=this.cm,r=this.hScale,o=document.createDocumentFragment(),i=this.annotations,a=n.getOption(\"lineWrapping\"),s=a&&1.5*n.defaultTextHeight(),l=null,c=null,u=n.lastLine();if(n.display.barWidth)for(var d,p=0;p<i.length;p++){var h=i[p];if(!(h.to.line>u)){for(var g=d||t(h.from,!0)*r,f=t(h.to,!1)*r;p<i.length-1&&!(i[p+1].to.line>u)&&(d=t(i[p+1].from,!0)*r,!(d>f+.9));)h=i[++p],f=t(h.to,!1)*r;if(f!=g){var m=Math.max(f-g,3),A=o.appendChild(document.createElement(\"div\"));A.style.cssText=\"position: absolute; right: 0px; width: \"+Math.max(n.display.barWidth-1,2)+\"px; top: \"+(g+this.buttonHeight)+\"px; height: \"+m+\"px\",A.className=this.options.className,h.id&&A.setAttribute(\"annotation-id\",h.id)}}}this.div.textContent=\"\",this.div.appendChild(o)},t.prototype.clear=function(){this.cm.off(\"refresh\",this.resizeHandler),this.cm.off(\"markerAdded\",this.resizeHandler),this.cm.off(\"markerCleared\",this.resizeHandler),this.changeHandler&&this.cm.off(\"changes\",this.changeHandler),this.div.parentNode.removeChild(this.div)}})},function(e,t,n){!function(e){e(n(268),n(276),n(278))}(function(e){\"use strict\";function t(e,t,n,r){this.cm=e,this.options=r;var o={listenForChanges:!1};for(var i in r)o[i]=r[i];o.className||(o.className=\"CodeMirror-search-match\"),this.annotation=e.annotateScrollbar(o),this.query=t,this.caseFold=n,this.gap={from:e.firstLine(),to:e.lastLine()+1},this.matches=[],this.update=null,this.findMatches(),this.annotation.update(this.matches);var a=this;e.on(\"change\",this.changeHandler=function(e,t){a.onChange(t)})}function n(e,t,n){return t>=e?e:Math.max(t,e+n)}e.defineExtension(\"showMatchesOnScrollbar\",function(e,n,r){return\"string\"==typeof r&&(r={className:r}),r||(r={}),new t(this,e,n,r)});var r=1e3;t.prototype.findMatches=function(){if(this.gap){for(var t=0;t<this.matches.length;t++){var n=this.matches[t];if(n.from.line>=this.gap.to)break;n.to.line>=this.gap.from&&this.matches.splice(t--,1)}for(var o=this.cm.getSearchCursor(this.query,e.Pos(this.gap.from,0),{caseFold:this.caseFold,multiline:this.options.multiline}),i=this.options&&this.options.maxMatches||r;o.findNext();){var n={from:o.from(),to:o.to()};if(n.from.line>=this.gap.to)break;if(this.matches.splice(t++,0,n),this.matches.length>i)break}this.gap=null}},t.prototype.onChange=function(t){var r=t.from.line,o=e.changeEnd(t).line,i=o-t.to.line;if(this.gap?(this.gap.from=Math.min(n(this.gap.from,r,i),t.from.line),this.gap.to=Math.max(n(this.gap.to,r,i),t.from.line)):this.gap={from:t.from.line,to:o+1},i)for(var a=0;a<this.matches.length;a++){var s=this.matches[a],l=n(s.from.line,r,i);l!=s.from.line&&(s.from=e.Pos(l,s.from.ch));var c=n(s.to.line,r,i);c!=s.to.line&&(s.to=e.Pos(c,s.to.ch))}clearTimeout(this.update);var u=this;this.update=setTimeout(function(){u.updateAfterChange()},250)},t.prototype.updateAfterChange=function(){this.findMatches(),this.annotation.update(this.matches)},t.prototype.clear=function(){this.cm.off(\"change\",this.changeHandler),this.annotation.clear()}})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(t,o,i,a){function s(e){var n=l(t,o);if(!n||n.to.line-n.from.line<c)return null;if(\"fold\"===a)return n;for(var r=t.findMarksAt(n.from),i=0;i<r.length;++i)if(r[i].__isFold){if(!e)return null;n.cleared=!0,r[i].clear()}return n}if(i&&i.call){var l=i;i=null}else var l=r(t,i,\"rangeFinder\");\"number\"==typeof o&&(o=e.Pos(o,0));var c=r(t,i,\"minFoldSize\"),u=s(!0);if(r(t,i,\"scanUp\"))for(;!u&&o.line>t.firstLine();)o=e.Pos(o.line-1,0),u=s(!1);if(u&&!u.cleared&&\"unfold\"!==a){var d=n(t,i,u);e.on(d,\"mousedown\",function(t){p.clear(),e.e_preventDefault(t)});var p=t.markText(u.from,u.to,{replacedWith:d,clearOnEnter:r(t,i,\"clearOnEnter\"),__isFold:!0});p.on(\"clear\",function(n,r){e.signal(t,\"unfold\",t,n,r)}),e.signal(t,\"fold\",t,u.from,u.to)}}function n(e,t,n){var o=r(e,t,\"widget\");if(\"function\"==typeof o&&(o=o(n.from,n.to)),\"string\"==typeof o){var i=document.createTextNode(o);o=document.createElement(\"span\"),o.appendChild(i),o.className=\"CodeMirror-foldmarker\"}else o&&(o=o.cloneNode(!0));return o}function r(e,t,n){if(t&&void 0!==t[n])return t[n];var r=e.options.foldOptions;return r&&void 0!==r[n]?r[n]:o[n]}e.newFoldFunction=function(e,n){return function(r,o){t(r,o,{rangeFinder:e,widget:n})}},e.defineExtension(\"foldCode\",function(e,n,r){t(this,e,n,r)}),e.defineExtension(\"isFolded\",function(e){for(var t=this.findMarksAt(e),n=0;n<t.length;++n)if(t[n].__isFold)return!0}),e.commands.toggleFold=function(e){e.foldCode(e.getCursor())},e.commands.fold=function(e){e.foldCode(e.getCursor(),null,\"fold\")},e.commands.unfold=function(e){e.foldCode(e.getCursor(),{scanUp:!1},\"unfold\")},e.commands.foldAll=function(t){t.operation(function(){for(var n=t.firstLine(),r=t.lastLine();r>=n;n++)t.foldCode(e.Pos(n,0),{scanUp:!1},\"fold\")})},e.commands.unfoldAll=function(t){t.operation(function(){for(var n=t.firstLine(),r=t.lastLine();r>=n;n++)t.foldCode(e.Pos(n,0),{scanUp:!1},\"unfold\")})},e.registerHelper(\"fold\",\"combine\",function(){var e=Array.prototype.slice.call(arguments,0);return function(t,n){for(var r=0;r<e.length;++r){var o=e[r](t,n);if(o)return o}}}),e.registerHelper(\"fold\",\"auto\",function(e,t){for(var n=e.getHelpers(t,\"fold\"),r=0;r<n.length;r++){var o=n[r](e,t);if(o)return o}});var o={rangeFinder:e.fold.auto,widget:\"↔\",minFoldSize:0,scanUp:!1,clearOnEnter:!0};e.defineOption(\"foldOptions\",null),e.defineExtension(\"foldOption\",function(e,t){return r(this,e,t)})})},function(e,t,n){!function(e){e(n(268),n(280))}(function(e){\"use strict\";function t(e){this.options=e,this.from=this.to=0}function n(e){return e===!0&&(e={}),null==e.gutter&&(e.gutter=\"CodeMirror-foldgutter\"),null==e.indicatorOpen&&(e.indicatorOpen=\"CodeMirror-foldgutter-open\"),null==e.indicatorFolded&&(e.indicatorFolded=\"CodeMirror-foldgutter-folded\"),e}function r(e,t){for(var n=e.findMarks(h(t,0),h(t+1,0)),r=0;r<n.length;++r)if(n[r].__isFold){var o=n[r].find(-1);if(o&&o.line===t)return n[r]}}function o(e){if(\"string\"==typeof e){var t=document.createElement(\"div\");return t.className=e+\" CodeMirror-guttermarker-subtle\",t}return e.cloneNode(!0)}function i(e,t,n){var i=e.state.foldGutter.options,s=t-1,l=e.foldOption(i,\"minFoldSize\"),c=e.foldOption(i,\"rangeFinder\"),u=\"string\"==typeof i.indicatorFolded&&a(i.indicatorFolded),d=\"string\"==typeof i.indicatorOpen&&a(i.indicatorOpen);e.eachLine(t,n,function(t){++s;var n=null,a=t.gutterMarkers;if(a&&(a=a[i.gutter]),r(e,s)){if(u&&a&&u.test(a.className))return;n=o(i.indicatorFolded)}else{var p=h(s,0),g=c&&c(e,p);if(g&&g.to.line-g.from.line>=l){if(d&&a&&d.test(a.className))return;n=o(i.indicatorOpen)}}(n||a)&&e.setGutterMarker(t,i.gutter,n)})}function a(e){return new RegExp(\"(^|\\\\s)\"+e+\"(?:$|\\\\s)\\\\s*\")}function s(e){var t=e.getViewport(),n=e.state.foldGutter;n&&(e.operation(function(){i(e,t.from,t.to)}),n.from=t.from,n.to=t.to)}function l(e,t,n){var o=e.state.foldGutter;if(o){var i=o.options;if(n==i.gutter){var a=r(e,t);a?a.clear():e.foldCode(h(t,0),i)}}}function c(e,t){\"mode\"==t&&u(e)}function u(e){var t=e.state.foldGutter;if(t){var n=t.options;t.from=t.to=0,clearTimeout(t.changeUpdate),t.changeUpdate=setTimeout(function(){s(e)},n.foldOnChangeTimeSpan||600)}}function d(e){var t=e.state.foldGutter;if(t){var n=t.options;clearTimeout(t.changeUpdate),t.changeUpdate=setTimeout(function(){var n=e.getViewport();t.from==t.to||n.from-t.to>20||t.from-n.to>20?s(e):e.operation(function(){n.from<t.from&&(i(e,n.from,t.from),t.from=n.from),n.to>t.to&&(i(e,t.to,n.to),t.to=n.to)})},n.updateViewportTimeSpan||400)}}function p(e,t){var n=e.state.foldGutter;if(n){var r=t.line;r>=n.from&&r<n.to&&i(e,r,r+1)}}e.defineOption(\"foldGutter\",!1,function(r,o,i){i&&i!=e.Init&&(r.clearGutter(r.state.foldGutter.options.gutter),r.state.foldGutter=null,r.off(\"gutterClick\",l),r.off(\"changes\",u),r.off(\"viewportChange\",d),r.off(\"fold\",p),r.off(\"unfold\",p),r.off(\"swapDoc\",u),r.off(\"optionChange\",c)),o&&(r.state.foldGutter=new t(n(o)),s(r),r.on(\"gutterClick\",l),r.on(\"changes\",u),r.on(\"viewportChange\",d),r.on(\"fold\",p),r.on(\"unfold\",p),r.on(\"swapDoc\",u),r.on(\"optionChange\",c))});var h=e.Pos})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(t){return function(n,r){function o(t){for(var o,i=r.ch,l=0;;){var c=0>=i?-1:s.lastIndexOf(t[0],i-1);if(-1!=c){if(1==l&&c<r.ch)break;if(o=n.getTokenTypeAt(e.Pos(a,c+1)),!/^(comment|string)/.test(o))return{ch:c+1,tokenType:o,pair:t};i=c-1}else{if(1==l)break;l=1,i=s.length}}}function i(t){var r,o,i=1,s=n.lastLine(),l=t.ch;e:for(var c=a;s>=c;++c)for(var u=n.getLine(c),d=c==a?l:0;;){var p=u.indexOf(t.pair[0],d),h=u.indexOf(t.pair[1],d);if(0>p&&(p=u.length),0>h&&(h=u.length),d=Math.min(p,h),d==u.length)break;if(n.getTokenTypeAt(e.Pos(c,d+1))==t.tokenType)if(d==p)++i;else if(!--i){r=c,o=d;break e}++d}return null==r||a==r?null:{from:e.Pos(a,l),to:e.Pos(r,o)}}for(var a=r.line,s=n.getLine(a),l=[],c=0;c<t.length;c++){var u=o(t[c]);u&&l.push(u)}l.sort(function(e,t){return e.ch-t.ch});for(var c=0;c<l.length;c++){var d=i(l[c]);if(d)return d}return null}}e.registerHelper(\"fold\",\"brace\",t([[\"{\",\"}\"],[\"[\",\"]\"]])),e.registerHelper(\"fold\",\"brace-paren\",t([[\"{\",\"}\"],[\"[\",\"]\"],[\"(\",\")\"]])),e.registerHelper(\"fold\",\"import\",function(t,n){function r(n){if(n<t.firstLine()||n>t.lastLine())return null;var r=t.getTokenAt(e.Pos(n,1));if(/\\S/.test(r.string)||(r=t.getTokenAt(e.Pos(n,r.end+1))),\"keyword\"!=r.type||\"import\"!=r.string)return null;for(var o=n,i=Math.min(t.lastLine(),n+10);i>=o;++o){var a=t.getLine(o),s=a.indexOf(\";\");if(-1!=s)return{startCh:r.end,end:e.Pos(o,s)}}}var o,i=n.line,a=r(i);if(!a||r(i-1)||(o=r(i-2))&&o.end.line==i-1)return null;for(var s=a.end;;){var l=r(s.line+1);if(null==l)break;s=l.end}return{from:t.clipPos(e.Pos(i,a.startCh+1)),to:s}}),e.registerHelper(\"fold\",\"include\",function(t,n){function r(n){if(n<t.firstLine()||n>t.lastLine())return null;var r=t.getTokenAt(e.Pos(n,1));return/\\S/.test(r.string)||(r=t.getTokenAt(e.Pos(n,r.end+1))),\"meta\"==r.type&&\"#include\"==r.string.slice(0,8)?r.start+8:void 0}var o=n.line,i=r(o);if(null==i||null!=r(o-1))return null;for(var a=o;;){var s=r(a+1);if(null==s)break;++a}return{from:e.Pos(o,i+1),to:t.clipPos(e.Pos(a))}})})},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";e.registerGlobalHelper(\"fold\",\"comment\",function(e){return e.blockCommentStart&&e.blockCommentEnd},function(t,n){var r=t.getModeAt(n),o=r.blockCommentStart,i=r.blockCommentEnd;if(o&&i){for(var a,s=n.line,l=t.getLine(s),c=n.ch,u=0;;){var d=0>=c?-1:l.lastIndexOf(o,c-1);if(-1!=d){if(1==u&&d<n.ch)return;if(/comment/.test(t.getTokenTypeAt(e.Pos(s,d+1)))&&(0==d||l.slice(d-i.length,d)==i||!/comment/.test(t.getTokenTypeAt(e.Pos(s,d))))){a=d+o.length;break}c=d-1}else{if(1==u)return;u=1,c=l.length}}var p,h,g=1,f=t.lastLine();e:for(var m=s;f>=m;++m)for(var A=t.getLine(m),M=m==s?a:0;;){var w=A.indexOf(o,M),v=A.indexOf(i,M);if(0>w&&(w=A.length),0>v&&(v=A.length),M=Math.min(w,v),M==A.length)break;if(M==w)++g;else if(!--g){p=m,h=M;break e}++M}if(null!=p&&(s!=p||h!=a))return{from:e.Pos(s,a),to:e.Pos(p,h)}}})})},function(e,t,n){\"use strict\";function r(e,t){var n=t&&1===t.length&&t[0];return n&&(n.text||n)===e}function o(){var e=B.rulesModal,t=e&&e.getSelectedList(),n=e&&e.getActive();\nn&&(t?-1===t.indexOf(n)&&t.push(n):t=[n]);var r=t&&t.map(function(e){return e.value}).join(\"\\n\");if(r){if(ce!==r){ce=r;var o={};R.resolveInlineValues(ce,o),le=Object.keys(o),le.length||(le=null)}return le}}function i(e,t){var n=e.moduleName,r=t&&t.hintUrl||e.hintUrl||\"\",o=\"plugin.\"+R.getSimplePluginName(e);return 0!==r.indexOf(n)&&0!==r.indexOf(o)&&(r=o+\"/\"+r),D!==r&&(S&&(S.hasDestroyed=!0),D=r,S=B.createCgi(r,!0)),S}function a(e){if(z)return[];var t=U.getAllRules();if(!e)return t;e=e.toLowerCase();var n=t.filter(function(t){return\"socks://\"!==t&&\"xsocks://\"!==t||-1===\"proxy\".indexOf(e)?(t=t.toLowerCase(),-1!==t.indexOf(e)||\"tlsoptions://\"===t&&-1!==\"cipher://\".indexOf(e)):!0});n.sort(function(t,n){var r=t.toLowerCase().indexOf(e),o=n.toLowerCase().indexOf(e);return r===o?0:0>r||r>o&&o>=0?1:-1}),\"csp\"===e?n.push(\"disable://csp\"):-1!==\"upstream\".indexOf(e)?n.push(\"proxy://\",\"xproxy://\"):-1!==\"xupstream\".indexOf(e)?n.push(\"xproxy://\"):-1!==\"extend\".indexOf(e)&&n.push(\"reqMerge://\",\"resMerge://\");var r=n.indexOf(\"redirect://\"),o=n.indexOf(\"locationHref://\");return-1!==r?-1!==o?r>o?(n.splice(r,1),n.splice(o+1,0,\"redirect://\")):(n.splice(o,1),n.splice(r+1,0,\"locationHref://\")):n.splice(r+1,0,\"locationHref://\"):-1!==o&&n.splice(o+1,0,\"redirect://\"),n}function s(e){var t=e.indexOf(\"<\"),n=-1===t?e:e.substring(0,t);return{text:n.replace(\".header-key\",\"._headerKey_\").replace(\"(keyword|regex)=\",\"_keywordOrRegEx_=\"),displayText:e}}function l(e,t,n){if(e=e.toLowerCase(),/[:=]/.test(e))return[];var r=[],o=[];return K.forEach(function(i,a){(!e||a&&-1!==i.toLowerCase().indexOf(e))&&(r.push(s(t+i)),n&&o.push(s(n+i)))}),r.concat(o)}function c(e,t,n){var r=function(e){return s(t+e)};if(!e)return n.map(r);var o=[];return e=e.toLowerCase(),n.forEach(function(t){-1!==t.toLowerCase().indexOf(e)&&o.push(r(t))}),o}function u(e){e=e.substring(1);try{var t=window.parent.getAtValueListForWhistle;if(\"function\"!=typeof t)return;var n=t(e);if(Array.isArray(n)){var r=[],o=60;return n.forEach(function(e){if(e&&!(1>o)){if(\"string\"==typeof e)return--o,void r.push(e);var t=e.value;if(t&&\"string\"==typeof t){--o;var n=e.label;n&&\"string\"==typeof n?r.push({text:t,displayText:n}):r.push(t)}}}),r}}catch(i){}}function d(e,t){return{text:e,displayText:e+\"(\"+(\"pipe\"===t?\"pipe\":\"sni\")+\"Value)\"}}function p(e,t){var n,r=e;if(t?(e=e.substring(t.length+3),n=U.getAllPluginNameList().map(function(e){return t+\"://\"+e})):(e=e.substring(1),n=U.getPluginVarList()),!e)return t?n.map(function(e){return d(e,t)}):n;if(e=e.toLowerCase(),t){e=t+\"://\"+e;var o=[];return n.filter(function(n){-1!==n.indexOf(e)&&o.push(d(n,t))}),1===o.length&&o[0].text===r?[]:o}return n.filter(function(t){return-1!==t.indexOf(e)})}function h(e,t){try{var n=window.parent.getAtHelpUrlForWhistle;if(\"function\"==typeof n){var r=n(e,t);if(r===!1||\"string\"==typeof r)return r}}catch(o){}return R.getDocUrl(\"rules/@.html\")}function g(e,t){return\"string\"!=typeof t&&(t=\"\"),t||e.homepage||R.getDocUrl(\"extensions/usage.html\")}function f(e,t,n,r){return n?e+(r?\".\":\"=\")+t:e+\"://\"+t}function m(e,t,n,o,i,a,s,l){if(C=[],q={},I=null,E=0,!e||a.hasDestroyed||!Array.isArray(e)&&!Array.isArray(e.list))return void(T=y=null);T=i,y=o;var c=0;Array.isArray(e)||(I=e.position,E=parseInt(e.offset,10)||0,e=e.list);var u=s?F:H;e.forEach(function(e){if(!(c>=60))if(\"string\"==typeof e)e=f(o,e.trim(),s),e.length<u&&!q[e]&&(++c,C.push(e),q[e]=g(n));else if(e){var t=e.label||e.displayText||e.display,r=e.value||e.text;t=\"string\"==typeof t?t.trim():\"\",r=\"string\"==typeof r?r.trim():\"\",r&&(r=f(o,r,s,e.isKey)),r&&r.length<u&&!q[t||r]&&(++c,C.push(t&&t!==r?{displayText:t,text:r}:r),q[t||r]=g(n,e.help))}}),r(l,C)&&(C=[],c=0),L&&c&&(t._byPlugin=!0,t.execCommand(\"autocomplete\"))}function A(e){return\"includeFilter://\"===e||\"excludeFilter://\"===e}function M(e){if(e){if(!e.indexOf(\"pipe://\"))return\"pipe\";if(!e.indexOf(\"sniCallback://\"))return\"sniCallback\";if(-1===e.indexOf(\"://\")){e=e.toLowerCase();var t=-1!==\"pipe://\".indexOf(e);if(t||-1!==\"snicallback://\".indexOf(e)){for(var n=U.getAllRules(),r=t?\"pipe://\":\"sniCallback://\",o=0,i=n.length;i>o;o++){var a=n[o];if(a!==r&&-1!==a.toLowerCase().indexOf(e))return}return t?\"pipe\":\"sniCallback\"}}}}function w(e){var t=e.length,n=1===t?e[0]:\"\";return n?0===n.indexOf(\"headerReplace://\")?{isHeader:!0,protocol:\"headerReplace://\",hints:Z}:0===n.indexOf(\"delete://\")?{isDelete:!0,protocol:\"delete://\",hints:$}:0===n.indexOf(\"lineProps://\")?{isLineProps:!0,protocol:\"lineProps://\",hints:ee}:0===n.indexOf(\"enable://\")?{isEnable:!0,protocol:\"enable://\",hints:te}:0===n.indexOf(\"disable://\")?{isDisable:!0,protocol:\"disable://\",hints:ne}:void 0:void 0}function v(e,t){return(!t||t())&&setTimeout(function(){e.state.completionActive||e.showHint({hint:j.hint.rulesHint,completeSingle:!1})},100),j.Pass}function b(e){Ae=null;var t,n=k(\"li.CodeMirror-hint-active\");if(n.is(\":visible\"))if(t=n.text(),ue)t=\"@\"+t;else if(de)t=\"%\"+t;else{var r=t.indexOf(\":\");Ae=t,-1!==r&&(t=t.substring(0,r))}else{var o=e.getCursor(),i=o.ch,a=e.getLine(o.line).replace(/(#.*|\\s+)$/,\"\"),s=a.length;if(s>=i){for(var l=i;--l>=0&&!/\\s/.test(a[l]););for(++l;++i<=s&&!/\\s/.test(a[i]););a=a.slice(l,i),V.test(a)||P.test(a)?t=a:W.test(a)&&(t=RegExp.$1)}}return t}n(285),n(287);for(var y,x,T,C,N,I,E,D,S,L,k=n(18),j=n(268),U=n(288),B=n(200),R=n(202),z=-1!==window.location.href.indexOf(\"disabledEditor=1\"),Q=/[^:/]/,O=/^((?:whistle\\.)?([a-z\\d_-]+:))(\\/?$|\\/\\/)/,H=512,F=100,V=/^@/,P=/^%/,Y=/^%([a-z\\d_-]+)([=.])/,G=/^([a-z\\d_.-]+:\\/\\/)?(`)?\\{([^\\s]*?)(?:\\}\\1?)?$/i,W=/^([^\\s:]+):\\/\\//,X=/^\\s*```/,_=120,q={},J={\"Alt-/\":\"autocomplete\"},K=[\"<keyword or regex for URL>\",\"m:<keyword or regex for HTTP method>\",\"b:<keyword or regex for request body>\",\"s:<keyword or regex for response status code>\",\"clientIp:<keyword or regex for client IP address>\",\"serverIp:<keyword or regex for server IP address>\",\"chance:<probability between 0 and 1>\",\"reqH.header-key:<keyword or regex for request header value>\",\"resH.header-key:<keyword or regex for response header value>\"],Z=[\"reqH.header-key:(keyword|regex)=<replacement>\",\"resH.header-key:(keyword|regex)=<replacement>\",\"trailer.header-key:(keyword|regex)=<replacement>\"],$=[\"pathname.<index>\",\"urlParams.<param-key>\",\"reqHeaders.<header-key>\",\"resHeaders.<header-key>\",\"reqCookies.<cookie-key>\",\"resCookies.<cookie-key>\",\"reqBody.<key.path>\",\"resBody.<key.path>\",\"pathname\",\"urlParams\",\"reqType\",\"resType\",\"reqCharset\",\"resCharset\",\"reqBody\",\"resBody\"],ee=[\"important\",\"safeHtml\",\"strictHtml\",\"disableAutoCors\",\"disableUserLogin\",\"enableUserLogin\",\"internal\",\"internalOnly\",\"internalProxy\",\"proxyFirst\",\"proxyHost\",\"proxyHostOnly\",\"proxyTunnel\",\"weakRule\",\"enableBigData\"],te=[\"abort\",\"abortReq\",\"abortRes\",\"authCapture\",\"auto2http\",\"bigData\",\"br\",\"gzip\",\"deflate\",\"capture\",\"captureIp\",\"captureStream\",\"clientCert\",\"clientId\",\"clientIp\",\"customParser\",\"flushHeaders\",\"forHttp\",\"forHttps\",\"forceReqWrite\",\"forceResWrite\",\"h2\",\"http2\",\"httpH2\",\"hide\",\"hideComposer\",\"hideCaptureError\",\"showHost\",\"ignoreSend\",\"ignoreReceive\",\"pauseSend\",\"pauseReceive\",\"inspect\",\"interceptConsole\",\"internalProxy\",\"proxyFirst\",\"proxyHost\",\"proxyTunnel\",\"keepCSP\",\"keepAllCSP\",\"keepCache\",\"keepAllCache\",\"keepClientId\",\"safeHtml\",\"strictHtml\",\"multiClient\",\"reqMergeBigData\",\"resMergeBigData\",\"requestWithMatchedRules\",\"responseWithMatchedRules\",\"tunnelHeadersFirst\",\"useLocalHost\",\"useSafePort\",\"userLogin\",\"weakRule\",\"socket\",\"websocket\"],ne=[\"301\",\"abort\",\"abortReq\",\"abortRes\",\"authCapture\",\"auto2http\",\"autoCors\",\"ajax\",\"bigData\",\"capture\",\"captureIp\",\"captureStream\",\"clientCert\",\"clientId\",\"clientIp\",\"customParser\",\"cache\",\"dnsCache\",\"csp\",\"cookies\",\"reqCookies\",\"resCookies\",\"flushHeaders\",\"forHttp\",\"forHttps\",\"forceReqWrite\",\"forceResWrite\",\"gzip\",\"h2\",\"http2\",\"httpH2\",\"hide\",\"hideComposer\",\"hideCaptureError\",\"interceptConsole\",\"internalProxy\",\"proxyFirst\",\"proxyHost\",\"proxyTunnel\",\"keepCSP\",\"keepAllCSP\",\"keepCache\",\"keepAllCache\",\"keepAlive\",\"keepClientId\",\"keepH2Session\",\"safeHtml\",\"strictHtml\",\"multiClient\",\"proxyConnection\",\"ua\",\"proxyUA\",\"referer\",\"rejectUnauthorized\",\"reqMergeBigData\",\"resMergeBigData\",\"requestWithMatchedRules\",\"responseWithMatchedRules\",\"secureOptions\",\"servername\",\"timeout\",\"trailerHeader\",\"trailers\",\"tunnelAuthHeader\",\"tunnelHeadersFirst\",\"useLocalHost\",\"useSafePort\",\"userLogin\",\"weakRule\"],re=[\"-\",'\"_\"',\"Shift-2\",\".\",\",\",\"Shift-,\",\"Shift-.\",\"Shift-;\",\"/\",\"Shift-/\",\"Shift-1\",\"Shift-4\",\"Shift-5\",\"Shift-6\",\"Shift-7\",\"Shift-8\",\"=\",\"Shift-=\",\"'\",\"Shift-'\",\";\",\"Shift-;\",\"\\\\\",\"Shift-\\\\\",\"Shift-`\",\"[\",\"]\",\"Shift-[\",\"Shift-]\",\"Shift-9\",\"Shift-0\"],oe=0;10>oe;oe++)re.push(\"'\"+oe+\"'\");for(var ie=\"a\".charCodeAt(),ae=\"z\".charCodeAt();ae>=ie;ie++){var se=String.fromCharCode(ie);re.push(\"'\"+se.toUpperCase()+\"'\"),re.push(\"'\"+se+\"'\")}k(window).on(\"hashchange\",function(){var e=-1!==window.location.href.indexOf(\"disabledEditor=1\");e!==z&&(z=e)});var le,ce,ue,de,pe,he=/\\S+/,ge=/^https?:\\/?\\/?/,fe=/^(headerReplace|includeFilter|excludeFilter|delete|lineProps|enable|disable):\\/\\//,me=function(e,t){return t+\"{\"+e+\"}\"+t};j.registerHelper(\"hint\",\"rulesHint\",function(e){ue=!1,de=!1,L=!1,x=null;var t=pe;pe=!1;var n=e._byDelete||e._byPlugin,s=e._byEnter;e._byDelete=e._byPlugin=e._byEnter=!1;var d=e.getCursor(),h=e.getLine(d.line);if(!X.test(h)){var v,b=d.ch,D=b,S=h.indexOf(\"#\");if(!(-1!==S&&D>S)){for(;D&&he.test(h.charAt(D-1));)--D;var k,U,R,z,W,J,K=D==b?\"\":h.substring(D,b),Z=V.test(K),$=M(K),ee=P.test(K);if(ee&&Y.test(K)){if(U=RegExp.$1,W=RegExp.$2,k=U&&B.getPlugin(U+\":\"),z=k&&k.pluginVars,!z)return;R=K.substring(U.length+2),ee=\".\"===W,J=ee}if(Z||$||ee){(!s||J||/^(?:pipe|sniCallback):\\/\\/$/.test(K))&&(v=Z?u(K):p(K,$));var te=v&&v.length,ne=r(K,v),re=!te||ne;if(J){if(ne&&/=./.test(R))return}else if(re)return;if(Z?ue=!0:ee&&(de=!0),!re)return{list:v,from:j.Pos(d.line,D),to:j.Pos(d.line,b)};ne&&(s=!1)}if(K){if(G.test(K)){var oe=RegExp.$1.length,ie=RegExp.$2,ae=RegExp.$3,se=B.getValuesModal(),le=se&&se.list,ce=o();if(ce&&(le&&le.forEach(function(e){-1===ce.indexOf(e)&&ce.push(e)}),le=ce),\"}`\"===ae.slice(-2)&&(ae=ae.slice(0,-2)),le&&le.length){if(ae){v=[];var Ae=ae.toLowerCase();le.forEach(function(e){var t=e.toLowerCase();t===Ae?v.unshift(me(e,ie)):-1!==t.indexOf(Ae)&&v.push(me(e,ie))})}else v=le.map(function(e){return me(e,ie)});var Me=v&&v.length;if(Me){if(r(K.substring(oe),v))return;return h=h.substring(D).split(/\\s/,1)[0]||\"\",{list:v,from:j.Pos(d.line,D+oe),to:j.Pos(d.line,D+h.length)}}}}if(k||O.test(K)){k=k||B.getPlugin(RegExp.$2);var we=z||k;if(k&&(\"string\"==typeof we.hintUrl||we.hintList)){if(z){if(R&&(s||R.length>F))return}else if(R=RegExp.$3||\"\",R=2===R.length?K.substring(K.indexOf(\"//\")+2):\"\",R&&(R.length>H||s))return;clearTimeout(N);var ve=z?\"%\"+U:RegExp.$1.slice(0,-1);if(we.hintList){if(R?(R=R.toLowerCase(),C=we.hintList.filter(function(e){return\"string\"==typeof e?-1!==e.toLowerCase().indexOf(R):-1!==e.text.toLowerCase().indexOf(R)?!0:e.displayText&&-1!==e.displayText.toLowerCase().indexOf(R)})):C=we.hintList,!C.length)return;q={},C=C.map(function(e){var t,n;return\"string\"==typeof e?n=f(ve,e,z):(n=f(ve,e.text,z,e.isKey),e.displayText&&(t={text:n,displayText:e.displayText},n=e.displayText)),q[n]=g(k,e.help),t||n}),I=\"\",E=0,y=ve,R=T}if(C&&C.length&&y===ve&&R===T){-1!==S&&(h=h.substring(0,S)),h=h.substring(D).split(/\\s/,1)[0]||\"\",x=ve,b=D+h.trim().length;var be=j.Pos(d.line,D),ye=j.Pos(d.line,b),xe=C,Te=\"cursor\"===I;if((E||Te)&&(xe=xe.map(function(e){var t={from:be,to:ye};return\"string\"==typeof e?(t.text=e,t.displayText=e):(t.text=e.text,t.displayText=e.displayText),t}),Te&&(be=d)),\"tail\"===I){var Ce=be;be=ye,ye=Ce}return E&&(D=Math.max(D,be.ch+E),D>b&&(D=b),be=j.Pos(d.line,D)),{list:xe,from:be,to:ye}}L=!0,N=setTimeout(function(){var t=i(k,we);e._bindedHintEvents||(e._bindedHintEvents=!0,e.on(\"blur\",function(){L=!1}));var n={protocol:ve,value:R};W&&(n.sep=W),t(n,function(n){m(n,e,k,ve,R,t,z,K)})},_)}}var Ne=fe.test(K);if(R||(Ne?s&&t:-1!==K.indexOf(\"//\"))||!Q.test(K))return}else if(n)return;var Ie;if(Ne){var Ee=K.indexOf(\"://\")+3;R=K.substring(Ee),Ie=K.substring(0,Ee)}else R=\"\";v=a(Ie||K);var De=w(v),Se=v.length,Le=2===Se&&(A(v[0])||A(v[1]))||1===Se&&A(v[0]);if(Ne=De||Le,Ne&&(v=De?c(R,De.protocol,De.hints):l(R,v[0],v[1]),Se=v.length),Se){pe=Ne;for(var ke=b,je=h[ke];je&&he.test(je);)je=h[++ke];var Ue=h.substring(D,ke);if(Ne){if(b=ke,1===Se&&r(Ue,v))return}else{var Be=Ue.indexOf(\":\");-1===Be||ge.test(Ue.substring(b))||(b=D+Be+1,\"/\"===h[b]&&(b++,\"/\"===h[b]&&b++))}return{list:v,from:j.Pos(d.line,D),to:j.Pos(d.line,b)}}}}}),j.commands.autocomplete=function(e){e.showHint({hint:j.hint.rulesHint,completeSingle:!1})},re.forEach(function(e){J[e]=v});var Ae;t.getExtraKeys=function(){return J},t.getHelpUrl=function(e,t){var n,r=b(e);if(V.test(r)&&(n=h(r.substring(1),t)))return n;if(n===!1)return!1;if(Ae&&(r===y||x===y)&&(n=q[Ae]))return n;if(Y.test(r)){r=r.substring(1,RegExp.$1.length+1);var o=r&&U.getPlugin(r);return o=o&&o.homepage,o||R.getDocUrl(\"extensions/usage.html\")}return U.getHelpUrl(r)}},function(e,t,n){var r=n(286);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:#fff;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto;box-sizing:border-box}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:#000;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:#fff}\",\"\"])},function(e,t,n){!function(e){e(n(268))}(function(e){\"use strict\";function t(e,t){if(this.cm=e,this.options=t,this.widget=null,this.debounce=0,this.tick=0,this.startPos=this.cm.getCursor(\"start\"),this.startLen=this.cm.getLine(this.startPos.line).length-this.cm.getSelection().length,this.options.updateOnCursorActivity){var n=this;e.on(\"cursorActivity\",this.activityFunc=function(){n.cursorActivity()})}}function n(e,t,n){var r=e.options.hintOptions,o={};for(var i in g)o[i]=g[i];if(r)for(var i in r)void 0!==r[i]&&(o[i]=r[i]);if(n)for(var i in n)void 0!==n[i]&&(o[i]=n[i]);return o.hint.resolve&&(o.hint=o.hint.resolve(e,t)),o}function r(e){return\"string\"==typeof e?e:e.text}function o(e,t){function n(e,n){var o;o=\"string\"!=typeof n?function(e){return n(e,t)}:r.hasOwnProperty(n)?r[n]:n,a[e]=o}var r={Up:function(){t.moveFocus(-1)},Down:function(){t.moveFocus(1)},PageUp:function(){t.moveFocus(-t.menuSize()+1,!0)},PageDown:function(){t.moveFocus(t.menuSize()-1,!0)},Home:function(){t.setFocus(0)},End:function(){t.setFocus(t.length-1)},Enter:t.pick,Tab:t.pick,Esc:t.close},o=/Mac/.test(navigator.platform);o&&(r[\"Ctrl-P\"]=function(){t.moveFocus(-1)},r[\"Ctrl-N\"]=function(){t.moveFocus(1)});var i=e.options.customKeys,a=i?{}:r;if(i)for(var s in i)i.hasOwnProperty(s)&&n(s,i[s]);var l=e.options.extraKeys;if(l)for(var s in l)l.hasOwnProperty(s)&&n(s,l[s]);return a}function i(e,t){for(;t&&t!=e;){if(\"LI\"===t.nodeName.toUpperCase()&&t.parentNode==e)return t;t=t.parentNode}}function a(t,n){this.id=\"cm-complete-\"+Math.floor(Math.random(1e6)),this.completion=t,this.data=n,this.picked=!1;var a=this,s=t.cm,l=s.getInputField().ownerDocument,c=l.defaultView||l.parentWindow,p=this.hints=l.createElement(\"ul\");p.setAttribute(\"role\",\"listbox\"),p.setAttribute(\"aria-expanded\",\"true\"),p.id=this.id;var h=t.cm.options.theme;p.className=\"CodeMirror-hints \"+h,this.selectedHint=n.selectedHint||0;for(var g=n.list,f=0;f<g.length;++f){var m=p.appendChild(l.createElement(\"li\")),A=g[f],M=u+(f!=this.selectedHint?\"\":\" \"+d);null!=A.className&&(M=A.className+\" \"+M),m.className=M,f==this.selectedHint&&m.setAttribute(\"aria-selected\",\"true\"),m.id=this.id+\"-\"+f,m.setAttribute(\"role\",\"option\"),A.render?A.render(m,n,A):m.appendChild(l.createTextNode(A.displayText||r(A))),m.hintId=f}var w=t.options.container||l.body,v=s.cursorCoords(t.options.alignWithWord?n.from:null),b=v.left,y=v.bottom,x=!0,T=0,C=0;if(w!==l.body){var N=-1!==[\"absolute\",\"relative\",\"fixed\"].indexOf(c.getComputedStyle(w).position),I=N?w:w.offsetParent,E=I.getBoundingClientRect(),D=l.body.getBoundingClientRect();T=E.left-D.left-I.scrollLeft,C=E.top-D.top-I.scrollTop}p.style.left=b-T+\"px\",p.style.top=y-C+\"px\";var S=c.innerWidth||Math.max(l.body.offsetWidth,l.documentElement.offsetWidth),L=c.innerHeight||Math.max(l.body.offsetHeight,l.documentElement.offsetHeight);w.appendChild(p),s.getInputField().setAttribute(\"aria-autocomplete\",\"list\"),s.getInputField().setAttribute(\"aria-owns\",this.id),s.getInputField().setAttribute(\"aria-activedescendant\",this.id+\"-\"+this.selectedHint);var k,j=t.options.moveOnOverlap?p.getBoundingClientRect():new DOMRect,U=t.options.paddingForScrollbar?p.scrollHeight>p.clientHeight+1:!1;setTimeout(function(){k=s.getScrollInfo()});var B=j.bottom-L;if(B>0){var R=j.bottom-j.top,z=j.top-(v.bottom-v.top)-2;L-j.top<z?(R>z&&(p.style.height=(R=z)+\"px\"),p.style.top=(y=v.top-R)+C+\"px\",x=!1):p.style.height=L-j.top-2+\"px\"}var Q=j.right-S;if(U&&(Q+=s.display.nativeBarWidth),Q>0&&(j.right-j.left>S&&(p.style.width=S-5+\"px\",Q-=j.right-j.left-S),p.style.left=(b=Math.max(v.left-Q-T,0))+\"px\"),U)for(var O=p.firstChild;O;O=O.nextSibling)O.style.paddingRight=s.display.nativeBarWidth+\"px\";if(s.addKeyMap(this.keyMap=o(t,{moveFocus:function(e,t){a.changeActive(a.selectedHint+e,t)},setFocus:function(e){a.changeActive(e)},menuSize:function(){return a.screenAmount()},length:g.length,close:function(){t.close()},pick:function(){a.pick()},data:n})),t.options.closeOnUnfocus){var H;s.on(\"blur\",this.onBlur=function(){H=setTimeout(function(){t.close()},100)}),s.on(\"focus\",this.onFocus=function(){clearTimeout(H)})}s.on(\"scroll\",this.onScroll=function(){var e=s.getScrollInfo(),n=s.getWrapperElement().getBoundingClientRect();k||(k=s.getScrollInfo());var r=y+k.top-e.top,o=r-(c.pageYOffset||(l.documentElement||l.body).scrollTop);return x||(o+=p.offsetHeight),o<=n.top||o>=n.bottom?t.close():(p.style.top=r+\"px\",void(p.style.left=b+k.left-e.left+\"px\"))}),e.on(p,\"dblclick\",function(e){var t=i(p,e.target||e.srcElement);t&&null!=t.hintId&&(a.changeActive(t.hintId),a.pick())}),e.on(p,\"click\",function(e){var n=i(p,e.target||e.srcElement);n&&null!=n.hintId&&(a.changeActive(n.hintId),t.options.completeOnSingleClick&&a.pick())}),e.on(p,\"mousedown\",function(){setTimeout(function(){s.focus()},20)});var F=this.getSelectedHintRange();return(0!==F.from||0!==F.to)&&this.scrollToActive(),e.signal(n,\"select\",g[this.selectedHint],p.childNodes[this.selectedHint]),!0}function s(e,t){if(!e.somethingSelected())return t;for(var n=[],r=0;r<t.length;r++)t[r].supportsSelection&&n.push(t[r]);return n}function l(e,t,n,r){if(e.async)e(t,r,n);else{var o=e(t,n);o&&o.then?o.then(r):r(o)}}function c(t,n){var r,o=t.getHelpers(n,\"hint\");if(o.length){var i=function(e,t,n){function r(o){return o==i.length?t(null):void l(i[o],e,n,function(e){e&&e.list.length>0?t(e):r(o+1)})}var i=s(e,o);r(0)};return i.async=!0,i.supportsSelection=!0,i}return(r=t.getHelper(t.getCursor(),\"hintWords\"))?function(t){return e.hint.fromList(t,{words:r})}:e.hint.anyword?function(t,n){return e.hint.anyword(t,n)}:function(){}}var u=\"CodeMirror-hint\",d=\"CodeMirror-hint-active\";e.showHint=function(e,t,n){if(!t)return e.showHint(n);n&&n.async&&(t.async=!0);var r={hint:t};if(n)for(var o in n)r[o]=n[o];return e.showHint(r)},e.defineExtension(\"showHint\",function(r){r=n(this,this.getCursor(\"start\"),r);var o=this.listSelections();if(!(o.length>1)){if(this.somethingSelected()){if(!r.hint.supportsSelection)return;for(var i=0;i<o.length;i++)if(o[i].head.line!=o[i].anchor.line)return}this.state.completionActive&&this.state.completionActive.close();var a=this.state.completionActive=new t(this,r);a.options.hint&&(e.signal(this,\"startCompletion\",this),a.update(!0))}}),e.defineExtension(\"closeHint\",function(){this.state.completionActive&&this.state.completionActive.close()});var p=window.requestAnimationFrame||function(e){return setTimeout(e,1e3/60)},h=window.cancelAnimationFrame||clearTimeout;t.prototype={close:function(){this.active()&&(this.cm.state.completionActive=null,this.tick=null,this.options.updateOnCursorActivity&&this.cm.off(\"cursorActivity\",this.activityFunc),this.widget&&this.data&&e.signal(this.data,\"close\"),this.widget&&this.widget.close(),e.signal(this.cm,\"endCompletion\",this.cm))},active:function(){return this.cm.state.completionActive==this},pick:function(t,n){var o=t.list[n],i=this;this.cm.operation(function(){o.hint?o.hint(i.cm,t,o):i.cm.replaceRange(r(o),o.from||t.from,o.to||t.to,\"complete\"),e.signal(t,\"pick\",o),i.cm.scrollIntoView()}),this.options.closeOnPick&&this.close()},cursorActivity:function(){this.debounce&&(h(this.debounce),this.debounce=0);var e=this.startPos;this.data&&(e=this.data.from);var t=this.cm.getCursor(),n=this.cm.getLine(t.line);if(t.line!=this.startPos.line||n.length-t.ch!=this.startLen-this.startPos.ch||t.ch<e.ch||this.cm.somethingSelected()||!t.ch||this.options.closeCharacters.test(n.charAt(t.ch-1)))this.close();else{var r=this;this.debounce=p(function(){r.update()}),this.widget&&this.widget.disable()}},update:function(e){if(null!=this.tick){var t=this,n=++this.tick;l(this.options.hint,this.cm,this.options,function(r){t.tick==n&&t.finishUpdate(r,e)})}},finishUpdate:function(t,n){this.data&&e.signal(this.data,\"update\");var r=this.widget&&this.widget.picked||n&&this.options.completeSingle;this.widget&&this.widget.close(),this.data=t,t&&t.list.length&&(r&&1==t.list.length?this.pick(t,0):(this.widget=new a(this,t),e.signal(t,\"shown\")))}},a.prototype={close:function(){if(this.completion.widget==this){this.completion.widget=null,this.hints.parentNode&&this.hints.parentNode.removeChild(this.hints),this.completion.cm.removeKeyMap(this.keyMap);var e=this.completion.cm.getInputField();e.removeAttribute(\"aria-activedescendant\"),e.removeAttribute(\"aria-owns\");var t=this.completion.cm;this.completion.options.closeOnUnfocus&&(t.off(\"blur\",this.onBlur),t.off(\"focus\",this.onFocus)),t.off(\"scroll\",this.onScroll)}},disable:function(){this.completion.cm.removeKeyMap(this.keyMap);var e=this;this.keyMap={Enter:function(){e.picked=!0}},this.completion.cm.addKeyMap(this.keyMap)},pick:function(){this.completion.pick(this.data,this.selectedHint)},changeActive:function(t,n){if(t>=this.data.list.length?t=n?this.data.list.length-1:0:0>t&&(t=n?0:this.data.list.length-1),this.selectedHint!=t){var r=this.hints.childNodes[this.selectedHint];r&&(r.className=r.className.replace(\" \"+d,\"\"),r.removeAttribute(\"aria-selected\")),r=this.hints.childNodes[this.selectedHint=t],r.className+=\" \"+d,r.setAttribute(\"aria-selected\",\"true\"),this.completion.cm.getInputField().setAttribute(\"aria-activedescendant\",r.id),this.scrollToActive(),e.signal(this.data,\"select\",this.data.list[this.selectedHint],r)}},scrollToActive:function(){var e=this.getSelectedHintRange(),t=this.hints.childNodes[e.from],n=this.hints.childNodes[e.to],r=this.hints.firstChild;t.offsetTop<this.hints.scrollTop?this.hints.scrollTop=t.offsetTop-r.offsetTop:n.offsetTop+n.offsetHeight>this.hints.scrollTop+this.hints.clientHeight&&(this.hints.scrollTop=n.offsetTop+n.offsetHeight-this.hints.clientHeight+r.offsetTop)},screenAmount:function(){return Math.floor(this.hints.clientHeight/this.hints.firstChild.offsetHeight)||1},getSelectedHintRange:function(){var e=this.completion.options.scrollMargin||0;return{from:Math.max(0,this.selectedHint-e),to:Math.min(this.data.list.length-1,this.selectedHint+e)}}},e.registerHelper(\"hint\",\"auto\",{resolve:c}),e.registerHelper(\"hint\",\"fromList\",function(t,n){var r,o=t.getCursor(),i=t.getTokenAt(o),a=e.Pos(o.line,i.start),s=o;i.start<o.ch&&/\\w/.test(i.string.charAt(o.ch-i.start-1))?r=i.string.substr(0,o.ch-i.start):(r=\"\",a=o);for(var l=[],c=0;c<n.words.length;c++){var u=n.words[c];u.slice(0,r.length)==r&&l.push(u)}return l.length?{list:l,from:a,to:s}:void 0}),e.commands.autocomplete=e.showHint;var g={hint:e.hint.auto,completeSingle:!0,alignWithWord:!0,closeCharacters:/[\\s()\\[\\]{};:>,]/,closeOnPick:!0,closeOnUnfocus:!0,updateOnCursorActivity:!0,completeOnSingleClick:!0,container:null,customKeys:null,extraKeys:null,paddingForScrollbar:!0,moveOnOverlap:!0};e.defineOption(\"hintOptions\",null)})},function(e,t,n){\"use strict\";function r(e){e=e.substring(e.indexOf(\".\")+1);for(var t=0,n=m.length;n>t;t++){var r=m[t];if(r.name===e)return r}}var o=n(202),i=n(199),a=[\"rule\",\"style\",\"pipe\",\"plugin\",\"sniCallback\",\"host\",\"xhost\",\"proxy\",\"xproxy\",\"http-proxy\",\"xhttp-proxy\",\"https-proxy\",\"xhttps-proxy\",\"socks\",\"xsocks\",\"pac\",\"log\",\"weinre\",\"filter\",\"ignore\",\"skip\",\"enable\",\"disable\",\"delete\",\"urlParams\",\"pathReplace\",\"method\",\"replaceStatus\",\"reqScript\",\"resScript\",\"frameScript\",\"reqDelay\",\"resDelay\",\"reqSpeed\",\"resSpeed\",\"headerReplace\",\"reqHeaders\",\"resHeaders\",\"reqCharset\",\"resCharset\",\"reqCookies\",\"resCookies\",\"reqCors\",\"resCors\",\"reqType\",\"resType\",\"ua\",\"auth\",\"cache\",\"referer\",\"attachment\",\"forwardedFor\",\"responseFor\",\"reqBody\",\"resBody\",\"reqMerge\",\"resMerge\",\"reqPrepend\",\"resPrepend\",\"reqAppend\",\"resAppend\",\"reqReplace\",\"resReplace\",\"htmlPrepend\",\"htmlBody\",\"htmlAppend\",\"cssPrepend\",\"cssBody\",\"cssAppend\",\"jsPrepend\",\"jsBody\",\"jsAppend\",\"reqWrite\",\"resWrite\",\"reqWriteRaw\",\"resWriteRaw\",\"trailers\",\"tlsOptions\"],s=[\"file\",\"xfile\",\"tpl\",\"xtpl\",\"rawfile\",\"xrawfile\",\"redirect\",\"locationHref\",\"statusCode\"],l=[],c=[],u=[],d=s.slice(),p=[\"http\",\"https\",\"ws\",\"wss\",\"tunnel\"],h=p.concat(s).concat(a.slice(1));h.splice(h.indexOf(\"plugin\"),1),h.splice(h.indexOf(\"reqScript\")+1,0,\"reqRules\"),h.splice(h.indexOf(\"resScript\")+1,0,\"resRules\"),h=h.map(function(e){return e+\"://\"}),h.splice(h.indexOf(\"filter://\"),1,\"excludeFilter://\",\"includeFilter://\"),h.push(\"lineProps://\");var g=h,f=[[\"Map Local\",[\"file://\",\"xfile://\",\"tpl://\",\"xtpl://\",\"rawfile://\",\"xrawfile://\"]],[\"Map Remote\",[\"https://\",\"http://\",\"wss://\",\"ws://\",\"tunnel://\"]],[\"DNS Spoofing\",[\"host://\",\"xhost://\",\"proxy://\",\"xproxy://\",\"http-proxy://\",\"xhttp-proxy://\",\"https-proxy://\",\"xhttps-proxy://\",\"socks://\",\"xsocks://\",\"pac://\"]],[\"Rewrite Request\",[\"urlParams://\",\"pathReplace://\",\"sniCallback://\",\"method://\",\"tlsOptions://\",\"reqHeaders://\",\"forwardedFor://\",\"ua://\",\"auth://\",\"cache://\",\"referer://\",\"reqType://\",\"reqCharset://\",\"reqCookies://\",\"reqCors://\",\"reqBody://\",\"reqMerge://\",\"reqPrepend://\",\"reqAppend://\",\"reqReplace://\",\"reqWrite://\",\"reqWriteRaw://\",\"reqRules://\",\"reqScript://\"]],[\"Rewrite Response\",[\"statusCode://\",\"replaceStatus://\",\"redirect://\",\"locationHref://\",\"resHeaders://\",\"responseFor://\",\"resCharset://\",\"resType://\",\"resCookies://\",\"attachment://\",\"resCors://\",\"resBody://\",\"resMerge://\",\"resPrepend://\",\"resAppend://\",\"resReplace://\",\"htmlPrepend://\",\"htmlBody://\",\"htmlAppend://\",\"cssPrepend://\",\"cssBody://\",\"cssAppend://\",\"jsPrepend://\",\"jsBody://\",\"jsAppend://\",\"trailers://\",\"resWrite://\",\"resWriteRaw://\",\"resRules://\",\"resScript://\",\"frameScript://\"]],[\"General\",[\"pipe://\",\"delete://\",\"headerReplace://\"]],[\"Throttle\",[\"reqDelay://\",\"resDelay://\",\"reqSpeed://\",\"resSpeed://\"]],[\"Tools\",[\"weinre://\",\"log://\"]],[\"Settings\",[\"style://\",\"enable://\",\"disable://\",\"lineProps://\"]],[\"Filters\",[\"ignore://\",\"skip://\",\"excludeFilter://\",\"includeFilter://\"]],[\"Plugins\",[]]],m=[];t.setPlugins=function(e){var t=e.disabledPlugins;m=e.pluginsOptions,l=[],c=[],u=[],d=s.slice(),g=h.slice();var n=[];f[f.length-1][1]=n,e.disabledAllPlugins||m.forEach(function(e,r){if(r){var o=e.name;if(!t[o]){var i=e.pluginVars;if(i){var a=i.hintSuffix;a?a.forEach(function(e){c.push(\"%\"+o+e)}):c.push(\"%\"+o+\"=\")}u.push(o);var s;e.hideShortProtocol||-1!==o.indexOf(\"_\")||(d.push(o),s=o+\"://\",g.push(s),n.push(s)),e.hideLongProtocol||(l.push(\"whistle.\"+o,\"plugin.\"+o),s=\"whistle.\"+o+\"://\",g.push(s),n.push(s))}}}),i.trigger(\"updatePlugins\")},t.PROTOCOLS=a,t.getGroupRules=function(){return f},t.getForwardRules=function(){return d},t.getPluginRules=function(){return l},t.getPluginVarList=function(){return c},t.getAllPluginNameList=function(){return u},t.getAllRules=function(){return g},t.getHelpUrl=function(e){return e&&\"rule\"!==e?\"includeFilter\"===e||\"excludeFilter\"===e?o.getDocUrl(\"rules/filters.html\"):\"skip\"===e?o.getDocUrl(\"rules/skip.html\"):\"lineProps\"===e?o.getDocUrl(\"rules/lineProps.html\"):\"reqRules\"===e?o.getDocUrl(\"rules/reqRules.html\"):\"resRules\"===e?o.getDocUrl(\"rules/resRules.html\"):-1!==s.indexOf(e)||-1!==p.indexOf(e)||-1!==a.indexOf(e)?(\"http-proxy\"===e?e=\"proxy\":\"xhttp-proxy\"===e?e=\"xproxy\":\"tlsOptions\"===e&&(e=\"cipher\"),o.getDocUrl(\"rules/\"+e+\".html\")):(e=r(e),e&&e.homepage?e.homepage:o.getDocUrl(\"rules/protocols.html\")):o.getDocUrl(\"rules/protocols.html\")},t.getPlugin=r},function(e,t,n){\"use strict\";function r(e){return e&&(0==e||e>65535)}var o=n(268),i=n(199),a=n(288),s=a.getForwardRules(),l=a.getPluginRules(),c=/^\\.[\\w-]+(?:[?$]|$)/,u=/^\\.[^./?]+\\.[^/?]/,d=/^(?:::(?:ffff:)?)?(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(?:\\:(\\d+))?$/,p=/^[\\da-f]{1,4}(?::[\\da-f]{1,4}){7}$/,h=/^[\\da-f]{1,4}(?::[\\da-f]{1,4}){0,6}$/,g=/^\\[([:\\da-f.]+)\\](?::(\\d+))?$/i,f=/^%[a-z\\d_\\-]+[=.]/;i.on(\"updatePlugins\",function(){s=a.getForwardRules(),l=a.getPluginRules()}),o.defineMode(\"rules\",function(){function e(e){var t;if(g.test(e)&&(e=RegExp.$1,t=RegExp.$2,r(t)))return!1;if(d.test(e))return!t&&r(RegExp.$1)?!1:!0;var n=e.indexOf(\"::\");return-1!==n?\"::\"===e||-1!==e.indexOf(\"::\",n+1)?!1:(e=e.split(\"::\",2),e=e[0]&&e[1]?e.join(\":\"):e[0]||e[1],h.test(e)):p.test(e)}function t(e){return/^x?hosts?:\\/\\//.test(e)}function n(e){return/^head:\\/\\//.test(e)}function o(e){return/^weinre:\\/\\//.test(e)}function i(e){return/^(?:referer|auth|ua|forwardedFor|reqCookies|reqDelay|reqSpeed|reqCors|reqHeaders|method|reqType|reqCharset|reqBody|reqPrepend|reqAppend|reqReplace|reqWrite|reqWriteRaw):\\/\\//.test(e)}function a(e){return/^(?:resScript|frameScript|resRules|responseFor|resCookies|resHeaders|trailers|replaceStatus|resDelay|resSpeed|resCors|resType|resCharset|cache|attachment|download|resBody|resPrepend|resAppend|css(?:Append|Prepend|Body)?|html(?:Append|Prepend|Body)?|js(?:Append|Prepend|Body)?|resReplace|resMerge|resWrite|resWriteRaw):\\/\\//.test(e)}function m(e){return/^(?:https?|wss?|tunnel):\\/\\//i.test(e)}function A(e){return/^[\\w\\.-]+:\\/\\//i.test(e)}function M(e){return e=e.substring(0,e.indexOf(\":\")),-1==s.indexOf(e)&&\"status\"!==e}function w(e){return e=e.substring(0,e.indexOf(\":\")),-1==l.indexOf(e)}function v(e){return/^\\/[^/](.*)\\/i?$/.test(e)||/^\\$/.test(e)}function b(e){return/^(?:urlParams|params|reqMerge|urlReplace|pathReplace):\\/\\//.test(e)}function y(e){return/^log:\\/\\//.test(e)}function x(e){return/^style:\\/\\//.test(e)}function T(e){return/^(?:excludeFilter|filter):\\/\\//.test(e)}function C(e){return/^lineProps:\\/\\//.test(e)}function N(e){return/^(?:pipe|sniCallback):\\/\\//.test(e)||/^(?:plugin|whistle)\\.[a-z\\d_\\-]+:\\/\\//.test(e)&&!w(e)}function I(e){return/^(?:rules?(?:File|Script)|reqScript|reqRules):\\/\\//.test(e)}function E(e){return/^disable:\\/\\//.test(e)}function D(e){return/^(?:cipher|tlsOptions):\\/\\//.test(e)}function S(e){return/^(?:ignore|skip):\\/\\//.test(e)}function L(e){return/^(?:includeFilter|enable):\\/\\//.test(e)}function k(e){return/^delete:\\/\\//.test(e)}function j(e){return/^headerReplace:\\/\\//.test(e)}function U(e){return/^x?(?:proxy|https?-proxy|http2https-proxy|https2http-proxy|internal-proxy|internal-https?-proxy):\\/\\//.test(e)}function B(e){return/^x?socks:\\/\\//.test(e)}function R(e){return/^pac:\\/\\//.test(e)}function z(e){return/^[a-z]:(?:\\\\|\\/(?!\\/))/i.test(e)||/^\\/[^/]/.test(e)}function Q(e){return/^:\\d{1,5}$/.test(e)}function O(e){if(!/^(?:\\$?(?:https?:|wss?:|tunnel:)?\\/\\/)?([^/?]+)/.test(e))return!1;var t=RegExp.$1;return-1!==t.indexOf(\"*\")||-1!==t.indexOf(\"~\")||u.test(t)}function H(e){return f.test(e)}function F(e){return/^\\^/.test(e)||c.test(e)}return{token:function(r,s){if(r.eatSpace())return null;var l=r.next();if(\"#\"==l)return r.eatWhile(function(e){return!0}),\"comment\";var c,u,d=\"!\"===l,p=d?r.next():l,h=\"\";if(r.eatWhile(function(e){return/\\s/.test(e)||\"#\"==e?!1:\"line\"===p&&\"`\"===e?(h=\"keyword js-keyword\",!1):(p+=e,\nh||\"/\"!=e||\"/\"!=c||(t(p)?h=\"number js-number js-type\":n(p)?h=\"header js-head js-type\":o(p)?h=\"atom js-weinre js-type\":U(p)?h=\"tag js-proxy js-type\":i(p)?h=\"variable-2 js-req js-type\":a(p)?h=\"positive js-res js-type\":b(p)?h=\"meta js-params js-type\":y(p)?h=\"atom js-log js-type\":x(p)?h=\"atom js-style js-type\":N(p)?h=\"variable-2 js-plugin js-type\":j(p)?h=\"variable-2 js-headerReplace js-type\":T(p)?h=\"negative js-filter js-type\":C(p)?h=\"negative js-line-props js-type\":S(p)?h=\"negative js-ignore js-type\":L(p)?h=\"atom js-enable js-type\":E(p)?h=\"negative js-disable js-type\":D(p)?h=\"atom js-cipher js-tls-options js-type\":k(p)?h=\"negative js-delete js-type\":U(p)?h=\"variable-2 js-proxy js-type\":B(p)?h=\"variable-2 js-socks js-type\":R(p)?h=\"variable-2 js-pac js-type\":I(p)?h=\"variable-2 js-rulesFile js-type\":m(p)?(u=!0,h=\"string-2 js-url js-type\"+(\"h\"===p[0]?\" js-http-url\":\"\")):O(p)?h=\"attribute js-attribute\":A(p)&&(h=\"builtin js-rule js-type\"+(M(p)?\" error-rule\":\"\"))),c=e,!0)}),p){if(h){if(u&&O(p))return\"attribute js-attribute\"}else{if(v(p)||F(p)||Q(p))return\"attribute js-attribute\";/^@/.test(p)?h=\"atom js-at js-type\":H(p)?h=\"variable-2 js-plugin-var js-type\":O(p)?h=\"attribute js-attribute\":e(p)?h=\"number js-number\":/^\\{.*\\}$/.test(p)||/^<.*>$/.test(p)||/^\\(.*\\)$/.test(p)?h=\"builtin js-rule js-type\":z(p)&&(h=\"builtin js-rule js-type\")}return d?h+\" error-rule\":h||\"js-http-url\"}}}})},function(e,t,n){\"use strict\";n(291);var r=n(202),o=n(24),i=n(57),a=n(18),s=n(210),l=n(209),c=n(213),u=n(214),d=i.findDOMNode,p=128,h=[\"JSON\",\"HTML\",\"CSS\",\"JS\",\"Font\",\"Img\",\"Media\",\"WS\",\"Tunnel\",\"Wasm\",\"Mock\",\"Rules\",\"Import\",\"Composer\",\"Error\",\"Other\"],g={JS:\"JavaScript\",Img:\"Image\",WS:\"WebSocket\",Wasm:\"WebAssembly\",Mock:\"Show mock requests\",Error:\"Show error requests\",Import:\"Show import sessions\",Composer:\"Show composer requests\",Rules:\"Show requests matched rules\"},f=o.createClass({displayName:\"FilterInput\",getInitialState:function(){var e=this.props.hintKey;if(this.allHintList=[],e)try{var t=JSON.parse(s.get(e));if(Array.isArray(t)){var n={};this.allHintList=t.map(function(e){return\"string\"==typeof e?e.substring(0,p):null}).filter(function(e){return!e||n[e]?!1:(n[e]=1,!0)}).slice(0,p)}}catch(r){}return{hintList:[],filterType:\"\"}},componentDidMount:function(){var e=this;e.hintElem=a(d(this.refs.hints)),a(document.body).on(\"mousedown\",function(t){null===e.state.hintList||a(t.target).closest(\".w-filter-con\").length||e.hideHints()});var t=this.props.onFilterTypeChange;\"function\"==typeof t&&t(e.state.filterType)},focus:function(){var e=d(this.refs.input);e.select(),e.focus()},addHint:function(){var e=this.state.filterText;if(e=e&&e.trim()){var t=this.allHintList,n=t.indexOf(e);-1!==n&&t.splice(n,1),t.length>p&&t.splice(0,1),t.push(e);try{s.set(this.props.hintKey,JSON.stringify(t))}catch(r){}}},filterHints:function(e){e=e?e.trim():\"\";var t=10,n=this.props.addonHints||[];if(!e)return n.concat(this.allHintList.slice(-t));n=n.slice(1),t+=n.length;for(var r=n.concat(this.allHintList),o=[],i=e.toLowerCase(),a=-1===e.indexOf(\":\"),s=r.length-1;s>=0;s--){var l=r[s];if(l!==e&&-1!==l.toLowerCase().indexOf(i)&&(-1===n.indexOf(l)||a)&&(o.unshift(l),o.length>=t))return o}return o},onFilterChange:function(e){this.changeInput(e?e.target.value:\"\")},changeInput:function(e){var t=this;t.props.onChange&&t.props.onChange(e);var n=t.props.hintKey;n&&clearTimeout(t.timer),this.state.filterText=e,t.setState({hintList:this.filterHints(e)},function(){n&&(t.timer=setTimeout(this.addHint,1e4))})},onClick:function(e){this.changeInput(e.target.title),this.hideHints()},hideHints:function(){this.setState({hintList:null}),this.addHint()},showHints:function(e){var t=this;if(t.props.hintKey){t.setState({hintList:this.filterHints(this.state.filterText)},e&&function(){t.hintElem.scrollTop(1e9)});var n=d(t.refs.input).getBoundingClientRect().y-130;d(t.refs.hints).style.setProperty(\"--h-hints\",Math.max(Math.min(n,360),200)+\"px\")}},onFilterKeyDown:function(e){var t;if(27===e.keyCode){var n=this.state.hintList;null===n?this.showHints():this.hideHints()}else if(38===e.keyCode)t=this.hintElem.find(\".w-active\"),null===this.state.hintList&&this.showHints(),t.length&&(t.removeClass(\"w-active\"),t=t.prev(\"li\").addClass(\"w-active\")),t.length||(t=this.hintElem.find(\"li:last\"),t.addClass(\"w-active\")),r.ensureVisible(t,this.hintElem),e.preventDefault();else if(40===e.keyCode)t=this.hintElem.find(\".w-active\"),null===this.state.hintList&&this.showHints(),t.length&&(t.removeClass(\"w-active\"),t=t.next(\"li\").addClass(\"w-active\")),t.length||(t=this.hintElem.find(\"li:first\"),t.addClass(\"w-active\")),r.ensureVisible(t,this.hintElem),e.preventDefault();else if(13===e.keyCode){t=this.hintElem.find(\".w-active\");var o=t.attr(\"title\");o&&(this.changeInput(o),this.hideHints())}else(e.ctrlKey||e.metaKey)&&(68==e.keyCode?(this.clearFilterText(),e.preventDefault(),e.stopPropagation()):88==e.keyCode&&e.stopPropagation());\"function\"==typeof this.props.onKeyDown&&this.props.onKeyDown(e)},clear:function(){var e=this;l.confirm(\"Confirm to clear history?\",function(t){t&&(s.set(e.props.hintKey,\"\"),e.allHintList=[],e.hideHints())})},clearFilterText:function(){this.props.onChange&&this.props.onChange(\"\");var e=null;document.activeElement===d(this.refs.input)&&(e=this.filterHints());var t=this.state.filterText,n=this;this.setState({filterText:\"\",hintList:e},function(){t&&n.onFilterChange()})},handleFilterType:function(e){var t=e.target;if(\"SPAN\"===t.nodeName){var n=t.textContent,r=\"All\"===n?\"\":n;r!==this.state.filterType&&(this.setState({filterType:r}),this.props.onFilterTypeChange(r))}},renderTypes:function(){var e=this.state.filterType;return-1===h.indexOf(e)&&(e=\"\"),o.createElement(\"div\",{className:\"w-filter-type-bar\",onClick:this.handleFilterType,onMouseDown:r.preventBlur},o.createElement(\"span\",{className:e?void 0:\"w-active\"},\"All\"),h.map(function(t){return o.createElement(\"span\",{key:t,title:g[t]||t,className:e===t?\"w-active\":void 0},t)}))},render:function(){var e=this,t=e.props,n=e.state.filterText||\"\",i=t.hintKey,a=e.state.hintList,s=t.addonHints||[],l=\"function\"==typeof t.onFilterTypeChange;return o.createElement(\"div\",{className:\"w-filter-con\"+(l?\" w-filter-show-types\":\"\"),style:t.wStyle},i?o.createElement(\"div\",{className:\"w-filter-hint\",style:{display:a&&a.length?\"\":\"none\"},onMouseDown:r.preventBlur},o.createElement(\"div\",{className:\"w-filter-bar\"},o.createElement(\"a\",{onClick:this.clear},o.createElement(c,{name:\"trash\"}),\"Clear history\"),o.createElement(u,{onClick:e.hideHints})),o.createElement(\"ul\",{ref:\"hints\"},a&&a.map(function(t){var n=t;return-1!==s.indexOf(t)&&(n=t.substring(0,t.indexOf(\":\")+1)),o.createElement(\"li\",{key:t,onClick:e.onClick,title:n},t)}))):void 0,l?e.renderTypes():void 0,o.createElement(\"input\",{type:\"text\",ref:\"input\",value:n,onChange:e.onFilterChange,onKeyDown:e.onFilterKeyDown,onFocus:e.showHints,onDoubleClick:e.showHints,onBlur:e.hideHints,className:\"w-filter-input\",maxLength:p,placeholder:\"Type filter text\"+(t.placeholder||\"\")}),o.createElement(\"button\",{onMouseDown:r.preventBlur,onClick:e.clearFilterText,style:{display:e.state.filterText?\"block\":\"none\"},type:\"button\",className:\"close w-clear-input\",title:\"Ctrl[Command]+D\"},\"×\"))}});e.exports=f},function(e,t,n){var r=n(292);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-filter-con{position:relative}.w-filter-con .w-clear-input{position:absolute;right:5px;bottom:10px;line-height:1pc;display:none}.w-filter-hint,.w-layer{background-color:var(--b-default);border-radius:3px;box-shadow:0 3px 6px -4px var(--s-rgba12),0 6px 1pc 0 var(--s-rgba8),0 9px 28px 8px var(--s-rgba5)}.w-filter-hint{margin:0 3px;bottom:35px;width:99%;padding-bottom:3px;color:var(--c-thin);line-height:1.5;position:absolute;overflow:hidden;font-size:14px}.w-filter-show-types .w-filter-hint{bottom:60px}.w-filter-hint li,.w-filter-hint ul{list-style:none;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.w-filter-hint li{line-height:24px;font-size:9pt;border-radius:3px;padding:0 5px}.w-filter-bar{text-align:right;height:20px;position:relative}.w-filter-bar .close{text-align:center;top:0;right:5px;position:absolute}.w-filter-bar .w-close-params{right:unset;top:-4px;left:5px}.w-filter-bar .w-clear-hints{top:-3px}.w-filter-bar a{left:3px;position:absolute;font-size:9pt}.w-filter-bar a span{width:18px;line-height:18px;text-align:center;display:inline-block;cursor:pointer}.w-filter-hint .w-active,.w-filter-hint li:hover{background-color:var(--b-hover)!important}.w-filter-type-bar{display:flex;height:26px;align-items:center;white-space:nowrap;overflow-x:auto;background:var(--b-bar)}.w-filter-type-bar span{margin-left:10px;border:1px solid var(--c-border);border-radius:3px;padding:0 6px;height:18px;font-size:9pt;box-sizing:border-box;display:flex;align-items:center;justify-content:center;background-color:var(--b-default);min-width:2pc}.w-filter-type-bar span:hover{border-color:var(--b-btn-hover);background:var(--b-btn-hover)}.w-filter-type-bar span.w-active,.w-filter-type-bar span:active{border-color:var(--b-btn-active);background:var(--b-btn-active);color:var(--c-default)}\",\"\"])},function(e,t,n){\"use strict\";n(294);var r=n(18),o=n(24),i=n(57),a=n(202),s=n(197),l=n(213),c=o.createClass({displayName:\"ContextMenu\",getInitialState:function(){return{}},componentDidMount:function(){var e=this;e.container=document.createElement(\"div\"),document.body.appendChild(e.container),e.componentDidUpdate(),r(document).on(\"mousedown click\",function(t){return r(t.target).closest(\".w-ctx-menu\").length?void(\"INPUT\"!==t.target.nodeName&&t.preventDefault()):void e.hide()}).on(\"keydown\",function(t){9===t.keyCode&&e.hide()}),r(window).on(\"resize blur\",function(){e.hide()})},componentDidUpdate:function(){i.unstable_renderSubtreeIntoContainer(this,this.getDialogElement(),this.container)},preventDefault:function(e){e.preventDefault()},isVisible:function(){return this.state.visible},onClick:function(e){var t=r(e.target).closest(\"li\");if(e.preventDefault(),!t.hasClass(\"w-ctx-sub-menu-list\")&&!t.hasClass(\"w-ctx-item-disabled\")){var n=this.state;!t.hasClass(\"w-ctx-item-multi\")&&!n.radio&&this.hide();var o=t.attr(\"data-menu-action\");this.props.onClick&&this.props.onClick(o,e,t.attr(\"data-parent-action\"),t.attr(\"data-name\")),(e.shiftKey||e.ctrlKey||e.metaKey)&&t.attr(\"data-shift-to-edit\")&&this.refs.editorDialog.show({value:t.attr(\"data-clipboard-text\")}),n.radio&&(n.list.forEach(function(e){e.selected=e.action==o}),this.setState({}))}},getDialogElement:function(){var e=this,t=e.state,n=t.list||[],r=t.radio;return o.createElement(\"div\",{onClick:e.onClick,className:\"w-ctx-menu \"+(t.className||\"\"),onContextMenu:e.onClick,style:{left:t.left,top:t.top,display:t.visible?\"\":\"none\"}},o.createElement(\"ul\",{className:\"w-ctx-menu-list\"},n.map(function(e){var t=e.list,n=e.shiftToEdit?1:void 0,i=!t&&e.multiple;return o.createElement(\"li\",{\"data-menu-action\":e.action||e.name,key:e.name,className:\"w-ctx-menu-item \"+(e.sep?\"w-ctx-item-sep\":\"\")+(e.disabled?\" w-ctx-item-disabled\":\"\")+(t?\" w-ctx-sub-menu-list\":\"\")+(e.copyText?\" w-copy-text\":\"\"),\"data-clipboard-text\":e.copyText,style:{display:e.hide?\"none\":void 0},onClick:e.onClick},o.createElement(\"label\",{className:\"w-ctx-item-tt\"+(e.selected?\" w-ctx-selected\":\"\")},r?o.createElement(\"span\",{className:\"w-ctx-checked\"+(e.selected?\" visible\":\"\")},\"✔️\"):void 0,e.icon?o.createElement(l,{name:e.icon}):null,i?o.createElement(\"input\",{type:\"checkbox\",checked:e.checked}):null,e.name),t?o.createElement(l,{name:\"play\"}):void 0,t?o.createElement(\"div\",{className:\"w-ctx-menu-gap\"}):void 0,t?o.createElement(\"ul\",{className:\"w-ctx-menu-list\",style:e.top>0?{top:30*-e.top-1,maxHeight:e.maxHeight}:void 0},t.map(function(t,r){return o.createElement(\"li\",{title:t.title,\"data-parent-action\":e.action,\"data-name\":t.name,\"data-menu-action\":t.action||t.name,key:r,onClick:t.onClick,className:\"w-ctx-menu-item \"+(t.sep?\"w-ctx-item-sep\":\"\")+(t.disabled?\" w-ctx-item-disabled\":\"\")+(t.copyText?\" w-copy-text\":\"\"),style:{display:t.hide?\"none\":void 0},\"data-clipboard-text\":t.copyText,\"data-shift-to-edit\":n},o.createElement(\"label\",{className:\"w-ctx-item-tt\"},t.multiple?o.createElement(\"input\",{type:\"checkbox\",checked:t.checked}):null,t.name))})):void 0)})))},show:function(e){e.visible=!0,this.setState(e)},hide:function(){this.setState({visible:!1})},update:function(){this.state.visible&&this.setState({})},render:function(){return o.createElement(s,{ref:\"editorDialog\"})}});c.util=a,c.$=r,e.exports=c},function(e,t,n){var r=n(295);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-ctx-menu{position:absolute;z-index:var(--z-ctx-menu)}.w-ctx-menu-list{list-style:none outside none;margin:0;padding:0;width:100%;box-sizing:border-box;border:1px solid var(--c-border);box-shadow:2px 2px 2px var(--s-rgba15);background:var(--b-default);min-width:110px;border-radius:4px}.w-ctx-menu-list .w-ctx-item-tt input{margin-right:5px;vertical-align:text-top}.w-ctx-menu-list .w-ctx-item-tt .glyphicon{margin-right:5px}.w-ctx-menu-list .glyphicon-play{line-height:28px;font-size:10px;margin-left:10px;transform:scale(.8)}.w-ctx-menu-list label{margin:0!important;font-weight:400!important;display:block}.w-ctx-menu-list .w-ctx-selected{font-weight:700!important}.w-ctx-menu-list .w-ctx-checked{display:inline-block;width:20px;visibility:hidden}body .w-ctx-menu-list .w-ctx-menu-list{margin-left:3px;display:none;top:-1px;z-index:calc(var(--z-ctx-menu) + 2)}body .w-ctx-menu-list .w-ctx-menu-gap{position:absolute;margin-left:-2px;height:35px;top:-1px;left:100%;width:6px;display:none;z-index:calc(var(--z-ctx-menu) + 1)}.w-ctx-menu-item:hover .w-ctx-menu-gap,.w-ctx-menu-item:hover .w-ctx-menu-list{display:block}.w-ctx-menu-item:hover .w-ctx-menu-list{width:auto}.w-ctx-menu-item{display:flex;line-height:30px;padding:0 15px;justify-content:space-between;position:relative;cursor:default;white-space:nowrap}.w-ctx-menu-item:hover{background:var(--b-title)}.w-ctx-item-sep{border-top:1px solid var(--c-border);margin-top:5px}.w-ctx-item-disabled{color:var(--c-gray)}.w-ctx-item-disabled:hover{background:var(--b-default)!important}.w-ctx-menu-item .w-ctx-menu-list{position:absolute;left:100%;top:0}.w-ctx-sub-menu-left .w-ctx-sub-menu-list .w-ctx-menu-list{left:-99pt;top:-61px}.w-keep-history-data .w-ctx-sub-menu-list .w-ctx-menu-list{top:-61px}.w-ctx-menu-left .w-ctx-menu-item .w-ctx-menu-list{right:102%;left:unset}.w-ctx-menu-left .w-ctx-menu-list .w-ctx-menu-gap{left:-3px}.w-ctx-radio-list{min-width:165px}\",\"\"])},function(e,t,n){\"use strict\";function r(e){if(e!==s)try{document.body.removeChild(e.iframe),e.destroyed=!0,e.dialogs.forEach(function(e){e.hide(!0)}),e.dialogs=[],delete g[e.page]}catch(t){}}function o(e){try{var t=e.location.pathname.substring(1)+e.location.search,n=t.lastIndexOf(\"?\");if(-1!==n){++n;var r=parseInt(t.substring(n),10);r>0&&(t=t.substring(n-r,n))}return g[t]}catch(o){}}function i(e){function t(t){try{\"function\"==typeof e.onWhistleContextMenuClick&&e.onWhistleContextMenuClick(t)}catch(n){}}var n=o(e);if(n&&!n.emit){try{var r=u(e);r.createModal=function(e){var t=c.create(e),r=t.hide;return t.hide=function(e){if(e){var o=n.dialogs.indexOf(t);-1!==o&&n.dialogs.splice(o,1)}r(e)},n.dialogs.push(t),t},e.initWhistleBridge(r)}catch(i){}n.list.forEach(t),n.emit=t,n.list=null}}function a(){var e=Object.keys(g),t=e.length;if(!(d>t)){for(var n=g[e[0]],o=1;t>o;o++){var i=g[e[o]];n.mtime>i.mtime&&(n=i)}r(n)}}var s,l=n(202),c=n(297),u=n(300),d=6,p=18e4,h=6e4,g={},f=function(){f=l.noop,setInterval(function(){var e=Date.now();Object.keys(g).forEach(function(t){var n=g[t];e-n.mtime>p&&r(n)})},h)};t.fork=function(e,t){e+=\"???_WHISTLE_PLUGIN_EXT_CONTEXT_MENU_\"+t.port+\"???\";var n=s=g[e];if(n)return n.mtime=Date.now(),void(n.emit?n.emit(t):(n.list.push(t),n.list.length>10&&(n.list=n.list.slice(-10))));a(),f(),window.onPluginContextMenuReady=i;var o=document.createElement(\"iframe\");o.style.display=\"none\",g[e]=s=n={page:e,list:[t],mtime:Date.now(),iframe:o,dialogs:[]},document.body.appendChild(o),o.src=e+e.length,setTimeout(function(){!n.emit&&r(n)},h)}},function(e,t,n){\"use strict\";function r(){return++g>100&&(g=0),g}function o(e){try{delete window[e]}catch(t){window[e]=void 0}}function i(e,t,n){var r=document.createElement(\"div\");document.body.appendChild(r),e.methods&&(window[n]=e.methods),u.render(c.createElement(d,{width:e.width,height:e.height,fullCustom:e.fullCustom,wclassName:\"w-dialog-for-plguin\",customRef:function(o){document.body.removeChild(r),l(o,e,n),t(o)},onClose:e.onClose,onShow:e.onShow},e.fullCustom?c.createElement(p,null):c.createElement(\"div\",{className:\"modal-header\"},c.createElement(\"h4\",null),c.createElement(p,null)),c.createElement(\"div\",{className:\"modal-body\"}),e.fullCustom?void 0:c.createElement(\"div\",{className:\"modal-footer\"},c.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"))),r)}function a(e,t){return e&&\"string\"==typeof e?e.replace(/\\s(on[a-z]+=)\"([^\"]+)\"/g,function(e,n,r){var o,i=r.indexOf(\"(\");return-1===i?o=\"(event)\":(o=r.substring(i),r=r.substring(0,i)),r=t+\"['\"+r+\"']\"+o,\" \"+n+'\"'+r+'\"'}):e}function s(e,t,n){var r=e.find(\".modal-content>.modal-\"+n+\":first\");null!=t?r.html(t):(t===!1||null===t)&&r.hide()}function l(e,t,n){t=t||\"\";var r=t.title,o=a(t.body,n),i=a(t.footer,n),l=e.container,c=l.find(\".modal-content>.modal-header:first\");r&&\"string\"==typeof r?c.show().find(\"h4\").html(r):(r===!1||\"\"===r)&&c.hide(),s(l,o,\"body\"),s(l,i,\"footer\")}var c=n(24),u=n(57),d=n(198),p=n(214);n(298);var h=\"__WHISTLE_MODAL_\"+Date.now()+\"_\"+Math.floor(1e3*Math.random())+\"_\",g=0;t.show=function(e){var t,n,a=e.onClose,s=h+r();return e.onClose=function(){o(s),n&&n.destroy(),n=null,\"function\"==typeof a&&a.call(e)},i(e,function(e){n=e,t?e.hide():e.show()},s),function(){!t&&n&&(t=!0,n.hide())}},t.create=function(e){var t,n,a=e.onClose,s=h+r();return e.onClose=function(){o(s),t&&(n&&n.destroy(),n=null),\"function\"==typeof a&&a.call(e)},i(e,function(e){n=e,t&&e.destroy()},s),{show:function(e){n&&(e&&e.methods&&(window[s]=e.methods),n.show(),l(n,e,s))},hide:function(e){t=e,n&&(n.container.is(\":visible\")?n.hide():t&&n.destroy())}}}},function(e,t,n){var r=n(299);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-dialog-for-plguin .modal-header{display:none}\",\"\"])},function(e,t,n){\"use strict\";function r(e){return\"string\"!=typeof e&&(e.type=e.type||e.method),e}function o(e){if(e)try{for(var t=e.location.pathname.split(\"/\"),n=t.length-1;n>=0;n--){var r=t[n];if(/^plugin\\.([a-z\\d_\\-]+)$/.test(r)){var o=u.getPlugin(RegExp.$1+\":\");if(o)return o}}}catch(i){}}function i(e,t){var n=o(e),i={updateUI:function(){h.trigger(\"updateUIThrottle\")},pageId:u.getPageId(),getWhistleId:function(){return u.whistleId},hasWhistleToken:function(){return u.hasWhistleToken},escapeHtml:d.escape,compose:u.compose,createComposeInterrupt:u.createComposeInterrupt,importSessions:u.importAnySessions,exportSessions:u.exportSessions,msgBox:l,qrCode:a,qrcode:a,decodeBase64:d.decodeBase64,joinBase64:d.joinBase64,getReqId:u.getReqId,onComposeData:u.onComposeData,offComposeData:u.offComposeData,alert:g.alert,confirm:g.confirm,showNetwork:function(){h.trigger(\"showNetwork\")},showRules:function(e){h.trigger(\"showRules\",e)},showValues:function(){h.trigger(\"showValues\")},showPlugins:function(){h.trigger(\"showPlugins\")},getActiveSession:function(){return m.getActive()},getSelectedSessionList:function(){return m.getSelectedList()},importMockData:function(e){return d.handleImportData(e)},download:function(e){h.trigger(\"download\",[e])},showOption:function(){h.trigger(\"showPluginOption\",n)},hideOption:function(){h.trigger(\"hidePluginOption\",n)},setNetworkSettings:function(e){h.trigger(\"setNetworkSettings\",e)},setRulesSettings:function(e){h.trigger(\"setRulesSettings\",e)},setValuesSettings:function(e){h.trigger(\"setValuesSettings\",e)},setComposerData:function(e){h.trigger(\"setComposerData\",e)},readFileAsText:d.readFileAsText,readFileAsBase64:d.readFileAsBase64,showHttpsSettings:function(){h.trigger(\"showHttpsSettingsDialog\")},showCustomCerts:function(){h.trigger(\"showCustomCerts\")},uploadCustomCerts:function(e,t){return u.uploadCerts(e,t)},showService:d.showService,hideService:d.hideService,getInstalledPlugins:function(){return u.getInstalledPlugins()},showInstallPlugins:function(e,t){h.trigger(\"showInstallPlugins\",[e,t])},showUpdatePlugins:function(e,t){h.trigger(\"showUpdatePlugins\",[e,t])},getVersion:function(){return u.version},copyText:d.copyText,syncData:function(e){n&&u.syncData(n,e)},syncRules:function(){n&&u.syncRules(n)},syncValues:function(){n&&u.syncValues(n)},request:function A(e,t){var A=c(r(e));return A(e.data,t)},createRequest:function(e){return c(r(e))},parseRules:f,showModal:p.show,getServerInfo:function(){var e=u.getServerInfo();return e&&s.extend(!0,{},e)},importRules:function(e){h.trigger(\"handleImportRules\",e)},importValues:function(e){h.trigger(\"handleImportValues\",e)}};return t&&Object.keys(t).forEach(function(e){i[e]=t[e]}),i}var a=n(301),s=n(18),l=n(206),c=n(201).createCgi,u=n(200),d=n(202),p=n(297),h=n(199),g=n(209),f=n(334),m=u.networkModal;e.exports=i,i.getServiceBridge=function(e){var t=i(null,{login:function(e,t){\"string\"!=typeof e&&(e=JSON.stringify(e)),u.login(e,t)},logout:function(e){u.logout(e)}});return t.closeDialog=e,t.installPlugins=u.installPluginsFromService,t}},function(e,t,n){function r(e,t,n,r,a){var s=[].slice.call(arguments,1),l=s.length,c=\"function\"==typeof s[l-1];if(!c&&!o())throw new Error(\"Callback required as last argument\");if(!c){if(1>l)throw new Error(\"Too few arguments provided\");return 1===l?(n=t,t=r=void 0):2!==l||t.getContext||(r=n,n=t,t=void 0),new Promise(function(o,a){try{var s=i.create(n,r);o(e(s,t,r))}catch(l){a(l)}})}if(2>l)throw new Error(\"Too few arguments provided\");2===l?(a=n,n=t,t=r=void 0):3===l&&(t.getContext&&\"undefined\"==typeof a?(a=r,r=void 0):(a=r,r=n,n=t,t=void 0));try{var u=i.create(n,r);a(null,e(u,t,r))}catch(d){a(d)}}var o=n(302),i=n(304),a=n(331),s=n(333);t.create=i.create,t.toCanvas=r.bind(null,a.render),t.toDataURL=r.bind(null,a.renderToDataURL),t.toString=r.bind(null,function(e,t,n){return s.render(e,n)})},function(e,t,n){\"use strict\";var r=n(303);e.exports=function(){return\"function\"==typeof r.Promise&&\"function\"==typeof r.Promise.prototype.then}},function(e,t){(function(t){\"use strict\";e.exports=\"object\"==typeof self&&self.self===self&&self||\"object\"==typeof t&&t.global===t&&t||this}).call(t,function(){return this}())},function(e,t,n){function r(e,t){for(var n=e.size,r=M.getPositions(t),o=0;o<r.length;o++)for(var i=r[o][0],a=r[o][1],s=-1;7>=s;s++)if(!(-1>=i+s||i+s>=n))for(var l=-1;7>=l;l++)-1>=a+l||a+l>=n||(s>=0&&6>=s&&(0===l||6===l)||l>=0&&6>=l&&(0===s||6===s)||s>=2&&4>=s&&l>=2&&4>=l?e.set(i+s,a+l,!0,!0):e.set(i+s,a+l,!1,!0))}function o(e){for(var t=e.size,n=8;t-8>n;n++){var r=n%2===0;e.set(n,6,r,!0),e.set(6,n,r,!0)}}function i(e,t){for(var n=A.getPositions(t),r=0;r<n.length;r++)for(var o=n[r][0],i=n[r][1],a=-2;2>=a;a++)for(var s=-2;2>=s;s++)-2===a||2===a||-2===s||2===s||0===a&&0===s?e.set(o+a,i+s,!0,!0):e.set(o+a,i+s,!1,!0)}function a(e,t){for(var n,r,o,i=e.size,a=y.getEncodedBits(t),s=0;18>s;s++)n=Math.floor(s/3),r=s%3+i-8-3,o=1===(a>>s&1),e.set(n,r,o,!0),e.set(r,n,o,!0)}function s(e,t,n){var r,o,i=e.size,a=x.getEncodedBits(t,n);for(r=0;15>r;r++)o=1===(a>>r&1),6>r?e.set(r,8,o,!0):8>r?e.set(r+1,8,o,!0):e.set(i-15+r,8,o,!0),8>r?e.set(8,i-r-1,o,!0):9>r?e.set(8,15-r-1+1,o,!0):e.set(8,15-r-1,o,!0);e.set(i-8,8,1,!0)}function l(e,t){for(var n=e.size,r=-1,o=n-1,i=7,a=0,s=n-1;s>0;s-=2)for(6===s&&s--;;){for(var l=0;2>l;l++)if(!e.isReserved(o,s-l)){var c=!1;a<t.length&&(c=1===(t[a]>>>i&1)),e.set(o,s-l,c),i--,-1===i&&(a++,i=7)}if(o+=r,0>o||o>=n){o-=r,r=-r;break}}}function c(e,t,n){var r=new f;n.forEach(function(t){r.put(t.mode.bit,4),r.put(t.getLength(),T.getCharCountIndicator(t.mode,e)),t.write(r)});var o=h.getSymbolTotalCodewords(e),i=v.getTotalCodewordsCount(e,t),a=8*(o-i);for(r.getLengthInBits()+4<=a&&r.put(0,4);r.getLengthInBits()%8!==0;)r.putBit(0);for(var s=(a-r.getLengthInBits())/8,l=0;s>l;l++)r.put(l%2?17:236,8);return u(r,e,t)}function u(e,t,n){for(var r=h.getSymbolTotalCodewords(t),o=v.getTotalCodewordsCount(t,n),i=r-o,a=v.getBlocksCount(t,n),s=r%a,l=a-s,c=Math.floor(r/a),u=Math.floor(i/a),d=u+1,g=c-u,f=new b(g),m=0,A=new Array(a),M=new Array(a),w=0,y=new p(e.buffer),x=0;a>x;x++){var T=l>x?u:d;A[x]=y.slice(m,m+T),M[x]=f.encode(A[x]),m+=T,w=Math.max(w,T)}var C,N,I=new p(r),E=0;for(C=0;w>C;C++)for(N=0;a>N;N++)C<A[N].length&&(I[E++]=A[N][C]);for(C=0;g>C;C++)for(N=0;a>N;N++)I[E++]=M[N][C];return I}function d(e,t,n,u){var d;if(N(e))d=C.fromArray(e);else{if(\"string\"!=typeof e)throw new Error(\"Invalid data\");var p=t;if(!p){var g=C.rawSplit(e);p=y.getBestVersionForData(g,n)}d=C.fromString(e,p||40)}var f=y.getBestVersionForData(d,n);if(!f)throw new Error(\"The amount of data is too big to be stored in a QR Code\");if(t){if(f>t)throw new Error(\"\\nThe chosen QR Code version cannot contain this amount of data.\\nMinimum version required to store current data is: \"+f+\".\\n\")}else t=f;var A=c(t,n,d),M=h.getSymbolSize(t),v=new m(M);return r(v,t),o(v),i(v,t),s(v,n,0),t>=7&&a(v,t),l(v,A),u||(u=w.getBestMask(v,s.bind(null,v,n))),w.applyMask(u,v),s(v,n,u),{modules:v,version:t,errorCorrectionLevel:n,maskPattern:u,segments:d}}var p=n(305),h=n(310),g=n(311),f=n(312),m=n(313),A=n(314),M=n(315),w=n(316),v=n(317),b=n(318),y=n(321),x=n(324),T=n(322),C=n(325),N=n(309);t.create=function(e,t){if(\"undefined\"==typeof e||\"\"===e)throw new Error(\"No input text\");var n,r,o=g.M;return\"undefined\"!=typeof t&&(o=g.from(t.errorCorrectionLevel,g.M),n=y.from(t.version),r=w.from(t.maskPattern),t.toSJISFunc&&h.setToSJISFunction(t.toSJISFunc)),d(e,n,o,r)}},function(e,t,n){(function(t){\"use strict\";function r(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()}catch(t){return!1}}function t(e,n,r){return t.TYPED_ARRAY_SUPPORT||this instanceof t?\"number\"==typeof e?s(this,e):m(this,e,n,r):new t(e,n,r)}function o(e){if(e>=M)throw new RangeError(\"Attempt to allocate Buffer larger than maximum size: 0x\"+M.toString(16)+\" bytes\");return 0|e}function i(e){return e!==e}function a(e,n){var r;return t.TYPED_ARRAY_SUPPORT?(r=new Uint8Array(n),r.__proto__=t.prototype):(r=e,null===r&&(r=new t(n)),r.length=n),r}function s(e,n){var r=a(e,0>n?0:0|o(n));if(!t.TYPED_ARRAY_SUPPORT)for(var i=0;n>i;++i)r[i]=0;return r}function l(e,t){var n=0|h(t),r=a(e,n),o=r.write(t);return o!==n&&(r=r.slice(0,o)),r}function c(e,t){for(var n=t.length<0?0:0|o(t.length),r=a(e,n),i=0;n>i;i+=1)r[i]=255&t[i];return r}function u(e,n,r,o){if(0>r||n.byteLength<r)throw new RangeError(\"'offset' is out of bounds\");if(n.byteLength<r+(o||0))throw new RangeError(\"'length' is out of bounds\");var i;return i=void 0===r&&void 0===o?new Uint8Array(n):void 0===o?new Uint8Array(n,r):new Uint8Array(n,r,o),t.TYPED_ARRAY_SUPPORT?i.__proto__=t.prototype:i=c(e,i),i}function d(e,n){if(t.isBuffer(n)){var r=0|o(n.length),s=a(e,r);return 0===s.length?s:(n.copy(s,0,0,r),s)}if(n){if(\"undefined\"!=typeof ArrayBuffer&&n.buffer instanceof ArrayBuffer||\"length\"in n)return\"number\"!=typeof n.length||i(n.length)?a(e,0):c(e,n);if(\"Buffer\"===n.type&&Array.isArray(n.data))return c(e,n.data)}throw new TypeError(\"First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.\")}function p(e,t){t=t||1/0;for(var n,r=e.length,o=null,i=[],a=0;r>a;++a){if(n=e.charCodeAt(a),n>55295&&57344>n){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(56320>n){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=(o-55296<<10|n-56320)+65536}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,128>n){if((t-=1)<0)break;i.push(n)}else if(2048>n){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(65536>n){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(1114112>n))throw new Error(\"Invalid code point\");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function h(e){if(t.isBuffer(e))return e.length;if(\"undefined\"!=typeof ArrayBuffer&&\"function\"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;\"string\"!=typeof e&&(e=\"\"+e);var n=e.length;return 0===n?0:p(e).length}function g(e,t,n,r){for(var o=0;r>o&&!(o+n>=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function f(e,t,n,r){return g(p(t,e.length-n),e,n,r)}function m(e,t,n,r){if(\"number\"==typeof t)throw new TypeError('\"value\" argument must not be a number');return\"undefined\"!=typeof ArrayBuffer&&t instanceof ArrayBuffer?u(e,t,n,r):\"string\"==typeof t?l(e,t,n):d(e,t)}var A=n(309);t.TYPED_ARRAY_SUPPORT=r();var M=t.TYPED_ARRAY_SUPPORT?2147483647:1073741823;t.TYPED_ARRAY_SUPPORT&&(t.prototype.__proto__=Uint8Array.prototype,t.__proto__=Uint8Array,\"undefined\"!=typeof Symbol&&Symbol.species&&t[Symbol.species]===t&&Object.defineProperty(t,Symbol.species,{value:null,configurable:!0,enumerable:!1,writable:!1})),t.prototype.write=function(e,t,n){void 0===t?(n=this.length,t=0):void 0===n&&\"string\"==typeof t?(n=this.length,t=0):isFinite(t)&&(t=0|t,n=isFinite(n)?0|n:void 0);var r=this.length-t;if((void 0===n||n>r)&&(n=r),e.length>0&&(0>n||0>t)||t>this.length)throw new RangeError(\"Attempt to write outside buffer bounds\");return f(this,e,t,n)},t.prototype.slice=function(e,n){var r=this.length;e=~~e,n=void 0===n?r:~~n,0>e?(e+=r,0>e&&(e=0)):e>r&&(e=r),0>n?(n+=r,0>n&&(n=0)):n>r&&(n=r),e>n&&(n=e);var o;if(t.TYPED_ARRAY_SUPPORT)o=this.subarray(e,n),o.__proto__=t.prototype;else{var i=n-e;o=new t(i,void 0);for(var a=0;i>a;++a)o[a]=this[a+e]}return o},t.prototype.copy=function(e,n,r,o){if(r||(r=0),o||0===o||(o=this.length),n>=e.length&&(n=e.length),n||(n=0),o>0&&r>o&&(o=r),o===r)return 0;if(0===e.length||0===this.length)return 0;if(0>n)throw new RangeError(\"targetStart out of bounds\");if(0>r||r>=this.length)throw new RangeError(\"sourceStart out of bounds\");if(0>o)throw new RangeError(\"sourceEnd out of bounds\");o>this.length&&(o=this.length),e.length-n<o-r&&(o=e.length-n+r);var i,a=o-r;if(this===e&&n>r&&o>n)for(i=a-1;i>=0;--i)e[i+n]=this[i+r];else if(1e3>a||!t.TYPED_ARRAY_SUPPORT)for(i=0;a>i;++i)e[i+n]=this[i+r];else Uint8Array.prototype.set.call(e,this.subarray(r,r+a),n);return a},t.prototype.fill=function(e,n,r){if(\"string\"==typeof e){if(\"string\"==typeof n?(n=0,r=this.length):\"string\"==typeof r&&(r=this.length),1===e.length){var o=e.charCodeAt(0);256>o&&(e=o)}}else\"number\"==typeof e&&(e=255&e);if(0>n||this.length<n||this.length<r)throw new RangeError(\"Out of range index\");if(n>=r)return this;n>>>=0,r=void 0===r?this.length:r>>>0,e||(e=0);var i;if(\"number\"==typeof e)for(i=n;r>i;++i)this[i]=e;else{var a=t.isBuffer(e)?e:new t(e),s=a.length;for(i=0;r-n>i;++i)this[i+n]=a[i%s]}return this},t.concat=function(e,n){if(!A(e))throw new TypeError('\"list\" argument must be an Array of Buffers');if(0===e.length)return a(null,0);var r;if(void 0===n)for(n=0,r=0;r<e.length;++r)n+=e[r].length;var o=s(null,n),i=0;for(r=0;r<e.length;++r){var l=e[r];if(!t.isBuffer(l))throw new TypeError('\"list\" argument must be an Array of Buffers');l.copy(o,i),i+=l.length}return o},t.byteLength=h,t.prototype._isBuffer=!0,t.isBuffer=function(e){return!(null==e||!e._isBuffer)},e.exports=t}).call(t,n(306).Buffer)},function(e,t,n){(function(e,r){/*!\n\t * The buffer module from node.js, for the browser.\n\t *\n\t * @author   Feross Aboukhadijeh <http://feross.org>\n\t * @license  MIT\n\t */\n\"use strict\";function o(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()&&\"function\"==typeof e.subarray&&0===e.subarray(1,1).byteLength}catch(t){return!1}}function i(){return e.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(t,n){if(i()<n)throw new RangeError(\"Invalid typed array length\");return e.TYPED_ARRAY_SUPPORT?(t=new Uint8Array(n),t.__proto__=e.prototype):(null===t&&(t=new e(n)),t.length=n),t}function e(t,n,r){if(!(e.TYPED_ARRAY_SUPPORT||this instanceof e))return new e(t,n,r);if(\"number\"==typeof t){if(\"string\"==typeof n)throw new Error(\"If encoding is specified then the first argument must be a string\");return u(this,t)}return s(this,t,n,r)}function s(e,t,n,r){if(\"number\"==typeof t)throw new TypeError('\"value\" argument must not be a number');return\"undefined\"!=typeof ArrayBuffer&&t instanceof ArrayBuffer?h(e,t,n,r):\"string\"==typeof t?d(e,t,n):g(e,t)}function l(e){if(\"number\"!=typeof e)throw new TypeError('\"size\" argument must be a number');if(0>e)throw new RangeError('\"size\" argument must not be negative')}function c(e,t,n,r){return l(t),0>=t?a(e,t):void 0!==n?\"string\"==typeof r?a(e,t).fill(n,r):a(e,t).fill(n):a(e,t)}function u(t,n){if(l(n),t=a(t,0>n?0:0|f(n)),!e.TYPED_ARRAY_SUPPORT)for(var r=0;n>r;++r)t[r]=0;return t}function d(t,n,r){if((\"string\"!=typeof r||\"\"===r)&&(r=\"utf8\"),!e.isEncoding(r))throw new TypeError('\"encoding\" must be a valid string encoding');var o=0|A(n,r);t=a(t,o);var i=t.write(n,r);return i!==o&&(t=t.slice(0,i)),t}function p(e,t){var n=t.length<0?0:0|f(t.length);e=a(e,n);for(var r=0;n>r;r+=1)e[r]=255&t[r];return e}function h(t,n,r,o){if(n.byteLength,0>r||n.byteLength<r)throw new RangeError(\"'offset' is out of bounds\");if(n.byteLength<r+(o||0))throw new RangeError(\"'length' is out of bounds\");return n=void 0===r&&void 0===o?new Uint8Array(n):void 0===o?new Uint8Array(n,r):new Uint8Array(n,r,o),e.TYPED_ARRAY_SUPPORT?(t=n,t.__proto__=e.prototype):t=p(t,n),t}function g(t,n){if(e.isBuffer(n)){var r=0|f(n.length);return t=a(t,r),0===t.length?t:(n.copy(t,0,0,r),t)}if(n){if(\"undefined\"!=typeof ArrayBuffer&&n.buffer instanceof ArrayBuffer||\"length\"in n)return\"number\"!=typeof n.length||J(n.length)?a(t,0):p(t,n);if(\"Buffer\"===n.type&&$(n.data))return p(t,n.data)}throw new TypeError(\"First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.\")}function f(e){if(e>=i())throw new RangeError(\"Attempt to allocate Buffer larger than maximum size: 0x\"+i().toString(16)+\" bytes\");return 0|e}function m(t){return+t!=t&&(t=0),e.alloc(+t)}function A(t,n){if(e.isBuffer(t))return t.length;if(\"undefined\"!=typeof ArrayBuffer&&\"function\"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer))return t.byteLength;\"string\"!=typeof t&&(t=\"\"+t);var r=t.length;if(0===r)return 0;for(var o=!1;;)switch(n){case\"ascii\":case\"latin1\":case\"binary\":return r;case\"utf8\":case\"utf-8\":case void 0:return G(t).length;case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return 2*r;case\"hex\":return r>>>1;case\"base64\":return _(t).length;default:if(o)return G(t).length;n=(\"\"+n).toLowerCase(),o=!0}}function M(e,t,n){var r=!1;if((void 0===t||0>t)&&(t=0),t>this.length)return\"\";if((void 0===n||n>this.length)&&(n=this.length),0>=n)return\"\";if(n>>>=0,t>>>=0,t>=n)return\"\";for(e||(e=\"utf8\");;)switch(e){case\"hex\":return j(this,t,n);case\"utf8\":case\"utf-8\":return D(this,t,n);case\"ascii\":return L(this,t,n);case\"latin1\":case\"binary\":return k(this,t,n);case\"base64\":return E(this,t,n);case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return U(this,t,n);default:if(r)throw new TypeError(\"Unknown encoding: \"+e);e=(e+\"\").toLowerCase(),r=!0}}function w(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function v(t,n,r,o,i){if(0===t.length)return-1;if(\"string\"==typeof r?(o=r,r=0):r>2147483647?r=2147483647:-2147483648>r&&(r=-2147483648),r=+r,isNaN(r)&&(r=i?0:t.length-1),0>r&&(r=t.length+r),r>=t.length){if(i)return-1;r=t.length-1}else if(0>r){if(!i)return-1;r=0}if(\"string\"==typeof n&&(n=e.from(n,o)),e.isBuffer(n))return 0===n.length?-1:b(t,n,r,o,i);if(\"number\"==typeof n)return n=255&n,e.TYPED_ARRAY_SUPPORT&&\"function\"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(t,n,r):Uint8Array.prototype.lastIndexOf.call(t,n,r):b(t,[n],r,o,i);throw new TypeError(\"val must be string, number or Buffer\")}function b(e,t,n,r,o){function i(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}var a=1,s=e.length,l=t.length;if(void 0!==r&&(r=String(r).toLowerCase(),\"ucs2\"===r||\"ucs-2\"===r||\"utf16le\"===r||\"utf-16le\"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,l/=2,n/=2}var c;if(o){var u=-1;for(c=n;s>c;c++)if(i(e,c)===i(t,-1===u?0:c-u)){if(-1===u&&(u=c),c-u+1===l)return u*a}else-1!==u&&(c-=c-u),u=-1}else for(n+l>s&&(n=s-l),c=n;c>=0;c--){for(var d=!0,p=0;l>p;p++)if(i(e,c+p)!==i(t,p)){d=!1;break}if(d)return c}return-1}function y(e,t,n,r){n=Number(n)||0;var o=e.length-n;r?(r=Number(r),r>o&&(r=o)):r=o;var i=t.length;if(i%2!==0)throw new TypeError(\"Invalid hex string\");r>i/2&&(r=i/2);for(var a=0;r>a;++a){var s=parseInt(t.substr(2*a,2),16);if(isNaN(s))return a;e[n+a]=s}return a}function x(e,t,n,r){return q(G(t,e.length-n),e,n,r)}function T(e,t,n,r){return q(W(t),e,n,r)}function C(e,t,n,r){return T(e,t,n,r)}function N(e,t,n,r){return q(_(t),e,n,r)}function I(e,t,n,r){return q(X(t,e.length-n),e,n,r)}function E(e,t,n){return 0===t&&n===e.length?K.fromByteArray(e):K.fromByteArray(e.slice(t,n))}function D(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;n>o;){var i=e[o],a=null,s=i>239?4:i>223?3:i>191?2:1;if(n>=o+s){var l,c,u,d;switch(s){case 1:128>i&&(a=i);break;case 2:l=e[o+1],128===(192&l)&&(d=(31&i)<<6|63&l,d>127&&(a=d));break;case 3:l=e[o+1],c=e[o+2],128===(192&l)&&128===(192&c)&&(d=(15&i)<<12|(63&l)<<6|63&c,d>2047&&(55296>d||d>57343)&&(a=d));break;case 4:l=e[o+1],c=e[o+2],u=e[o+3],128===(192&l)&&128===(192&c)&&128===(192&u)&&(d=(15&i)<<18|(63&l)<<12|(63&c)<<6|63&u,d>65535&&1114112>d&&(a=d))}}null===a?(a=65533,s=1):a>65535&&(a-=65536,r.push(a>>>10&1023|55296),a=56320|1023&a),r.push(a),o+=s}return S(r)}function S(e){var t=e.length;if(ee>=t)return String.fromCharCode.apply(String,e);for(var n=\"\",r=0;t>r;)n+=String.fromCharCode.apply(String,e.slice(r,r+=ee));return n}function L(e,t,n){var r=\"\";n=Math.min(e.length,n);for(var o=t;n>o;++o)r+=String.fromCharCode(127&e[o]);return r}function k(e,t,n){var r=\"\";n=Math.min(e.length,n);for(var o=t;n>o;++o)r+=String.fromCharCode(e[o]);return r}function j(e,t,n){var r=e.length;(!t||0>t)&&(t=0),(!n||0>n||n>r)&&(n=r);for(var o=\"\",i=t;n>i;++i)o+=Y(e[i]);return o}function U(e,t,n){for(var r=e.slice(t,n),o=\"\",i=0;i<r.length;i+=2)o+=String.fromCharCode(r[i]+256*r[i+1]);return o}function B(e,t,n){if(e%1!==0||0>e)throw new RangeError(\"offset is not uint\");if(e+t>n)throw new RangeError(\"Trying to access beyond buffer length\")}function R(t,n,r,o,i,a){if(!e.isBuffer(t))throw new TypeError('\"buffer\" argument must be a Buffer instance');if(n>i||a>n)throw new RangeError('\"value\" argument is out of bounds');if(r+o>t.length)throw new RangeError(\"Index out of range\")}function z(e,t,n,r){0>t&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);i>o;++o)e[n+o]=(t&255<<8*(r?o:1-o))>>>8*(r?o:1-o)}function Q(e,t,n,r){0>t&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);i>o;++o)e[n+o]=t>>>8*(r?o:3-o)&255}function O(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError(\"Index out of range\");if(0>n)throw new RangeError(\"Index out of range\")}function H(e,t,n,r,o){return o||O(e,t,n,4,3.4028234663852886e38,-3.4028234663852886e38),Z.write(e,t,n,r,23,4),n+4}function F(e,t,n,r,o){return o||O(e,t,n,8,1.7976931348623157e308,-1.7976931348623157e308),Z.write(e,t,n,r,52,8),n+8}function V(e){if(e=P(e).replace(te,\"\"),e.length<2)return\"\";for(;e.length%4!==0;)e+=\"=\";return e}function P(e){return e.trim?e.trim():e.replace(/^\\s+|\\s+$/g,\"\")}function Y(e){return 16>e?\"0\"+e.toString(16):e.toString(16)}function G(e,t){t=t||1/0;for(var n,r=e.length,o=null,i=[],a=0;r>a;++a){if(n=e.charCodeAt(a),n>55295&&57344>n){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(56320>n){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=(o-55296<<10|n-56320)+65536}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,128>n){if((t-=1)<0)break;i.push(n)}else if(2048>n){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(65536>n){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(1114112>n))throw new Error(\"Invalid code point\");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function W(e){for(var t=[],n=0;n<e.length;++n)t.push(255&e.charCodeAt(n));return t}function X(e,t){for(var n,r,o,i=[],a=0;a<e.length&&!((t-=2)<0);++a)n=e.charCodeAt(a),r=n>>8,o=n%256,i.push(o),i.push(r);return i}function _(e){return K.toByteArray(V(e))}function q(e,t,n,r){for(var o=0;r>o&&!(o+n>=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function J(e){return e!==e}var K=n(1),Z=n(307),$=n(308);t.Buffer=e,t.SlowBuffer=m,t.INSPECT_MAX_BYTES=50,e.TYPED_ARRAY_SUPPORT=void 0!==r.TYPED_ARRAY_SUPPORT?r.TYPED_ARRAY_SUPPORT:o(),t.kMaxLength=i(),e.poolSize=8192,e._augment=function(t){return t.__proto__=e.prototype,t},e.from=function(e,t,n){return s(null,e,t,n)},e.TYPED_ARRAY_SUPPORT&&(e.prototype.__proto__=Uint8Array.prototype,e.__proto__=Uint8Array,\"undefined\"!=typeof Symbol&&Symbol.species&&e[Symbol.species]===e&&Object.defineProperty(e,Symbol.species,{value:null,configurable:!0})),e.alloc=function(e,t,n){return c(null,e,t,n)},e.allocUnsafe=function(e){return u(null,e)},e.allocUnsafeSlow=function(e){return u(null,e)},e.isBuffer=function(e){return!(null==e||!e._isBuffer)},e.compare=function(t,n){if(!e.isBuffer(t)||!e.isBuffer(n))throw new TypeError(\"Arguments must be Buffers\");if(t===n)return 0;for(var r=t.length,o=n.length,i=0,a=Math.min(r,o);a>i;++i)if(t[i]!==n[i]){r=t[i],o=n[i];break}return o>r?-1:r>o?1:0},e.isEncoding=function(e){switch(String(e).toLowerCase()){case\"hex\":case\"utf8\":case\"utf-8\":case\"ascii\":case\"latin1\":case\"binary\":case\"base64\":case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return!0;default:return!1}},e.concat=function(t,n){if(!$(t))throw new TypeError('\"list\" argument must be an Array of Buffers');if(0===t.length)return e.alloc(0);var r;if(void 0===n)for(n=0,r=0;r<t.length;++r)n+=t[r].length;var o=e.allocUnsafe(n),i=0;for(r=0;r<t.length;++r){var a=t[r];if(!e.isBuffer(a))throw new TypeError('\"list\" argument must be an Array of Buffers');a.copy(o,i),i+=a.length}return o},e.byteLength=A,e.prototype._isBuffer=!0,e.prototype.swap16=function(){var e=this.length;if(e%2!==0)throw new RangeError(\"Buffer size must be a multiple of 16-bits\");for(var t=0;e>t;t+=2)w(this,t,t+1);return this},e.prototype.swap32=function(){var e=this.length;if(e%4!==0)throw new RangeError(\"Buffer size must be a multiple of 32-bits\");for(var t=0;e>t;t+=4)w(this,t,t+3),w(this,t+1,t+2);return this},e.prototype.swap64=function(){var e=this.length;if(e%8!==0)throw new RangeError(\"Buffer size must be a multiple of 64-bits\");for(var t=0;e>t;t+=8)w(this,t,t+7),w(this,t+1,t+6),w(this,t+2,t+5),w(this,t+3,t+4);return this},e.prototype.toString=function(){var e=0|this.length;return 0===e?\"\":0===arguments.length?D(this,0,e):M.apply(this,arguments)},e.prototype.equals=function(t){if(!e.isBuffer(t))throw new TypeError(\"Argument must be a Buffer\");return this===t?!0:0===e.compare(this,t)},e.prototype.inspect=function(){var e=\"\",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString(\"hex\",0,n).match(/.{2}/g).join(\" \"),this.length>n&&(e+=\" ... \")),\"<Buffer \"+e+\">\"},e.prototype.compare=function(t,n,r,o,i){if(!e.isBuffer(t))throw new TypeError(\"Argument must be a Buffer\");if(void 0===n&&(n=0),void 0===r&&(r=t?t.length:0),void 0===o&&(o=0),void 0===i&&(i=this.length),0>n||r>t.length||0>o||i>this.length)throw new RangeError(\"out of range index\");if(o>=i&&n>=r)return 0;if(o>=i)return-1;if(n>=r)return 1;if(n>>>=0,r>>>=0,o>>>=0,i>>>=0,this===t)return 0;for(var a=i-o,s=r-n,l=Math.min(a,s),c=this.slice(o,i),u=t.slice(n,r),d=0;l>d;++d)if(c[d]!==u[d]){a=c[d],s=u[d];break}return s>a?-1:a>s?1:0},e.prototype.includes=function(e,t,n){return-1!==this.indexOf(e,t,n)},e.prototype.indexOf=function(e,t,n){return v(this,e,t,n,!0)},e.prototype.lastIndexOf=function(e,t,n){return v(this,e,t,n,!1)},e.prototype.write=function(e,t,n,r){if(void 0===t)r=\"utf8\",n=this.length,t=0;else if(void 0===n&&\"string\"==typeof t)r=t,n=this.length,t=0;else{if(!isFinite(t))throw new Error(\"Buffer.write(string, encoding, offset[, length]) is no longer supported\");t=0|t,isFinite(n)?(n=0|n,void 0===r&&(r=\"utf8\")):(r=n,n=void 0)}var o=this.length-t;if((void 0===n||n>o)&&(n=o),e.length>0&&(0>n||0>t)||t>this.length)throw new RangeError(\"Attempt to write outside buffer bounds\");r||(r=\"utf8\");for(var i=!1;;)switch(r){case\"hex\":return y(this,e,t,n);case\"utf8\":case\"utf-8\":return x(this,e,t,n);case\"ascii\":return T(this,e,t,n);case\"latin1\":case\"binary\":return C(this,e,t,n);case\"base64\":return N(this,e,t,n);case\"ucs2\":case\"ucs-2\":case\"utf16le\":case\"utf-16le\":return I(this,e,t,n);default:if(i)throw new TypeError(\"Unknown encoding: \"+r);r=(\"\"+r).toLowerCase(),i=!0}},e.prototype.toJSON=function(){return{type:\"Buffer\",data:Array.prototype.slice.call(this._arr||this,0)}};var ee=4096;e.prototype.slice=function(t,n){var r=this.length;t=~~t,n=void 0===n?r:~~n,0>t?(t+=r,0>t&&(t=0)):t>r&&(t=r),0>n?(n+=r,0>n&&(n=0)):n>r&&(n=r),t>n&&(n=t);var o;if(e.TYPED_ARRAY_SUPPORT)o=this.subarray(t,n),o.__proto__=e.prototype;else{var i=n-t;o=new e(i,void 0);for(var a=0;i>a;++a)o[a]=this[a+t]}return o},e.prototype.readUIntLE=function(e,t,n){e=0|e,t=0|t,n||B(e,t,this.length);for(var r=this[e],o=1,i=0;++i<t&&(o*=256);)r+=this[e+i]*o;return r},e.prototype.readUIntBE=function(e,t,n){e=0|e,t=0|t,n||B(e,t,this.length);for(var r=this[e+--t],o=1;t>0&&(o*=256);)r+=this[e+--t]*o;return r},e.prototype.readUInt8=function(e,t){return t||B(e,1,this.length),this[e]},e.prototype.readUInt16LE=function(e,t){return t||B(e,2,this.length),this[e]|this[e+1]<<8},e.prototype.readUInt16BE=function(e,t){return t||B(e,2,this.length),this[e]<<8|this[e+1]},e.prototype.readUInt32LE=function(e,t){return t||B(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},e.prototype.readUInt32BE=function(e,t){return t||B(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},e.prototype.readIntLE=function(e,t,n){e=0|e,t=0|t,n||B(e,t,this.length);for(var r=this[e],o=1,i=0;++i<t&&(o*=256);)r+=this[e+i]*o;return o*=128,r>=o&&(r-=Math.pow(2,8*t)),r},e.prototype.readIntBE=function(e,t,n){e=0|e,t=0|t,n||B(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return o*=128,i>=o&&(i-=Math.pow(2,8*t)),i},e.prototype.readInt8=function(e,t){return t||B(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},e.prototype.readInt16LE=function(e,t){t||B(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},e.prototype.readInt16BE=function(e,t){t||B(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},e.prototype.readInt32LE=function(e,t){return t||B(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},e.prototype.readInt32BE=function(e,t){return t||B(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},e.prototype.readFloatLE=function(e,t){return t||B(e,4,this.length),Z.read(this,e,!0,23,4)},e.prototype.readFloatBE=function(e,t){return t||B(e,4,this.length),Z.read(this,e,!1,23,4)},e.prototype.readDoubleLE=function(e,t){return t||B(e,8,this.length),Z.read(this,e,!0,52,8)},e.prototype.readDoubleBE=function(e,t){return t||B(e,8,this.length),Z.read(this,e,!1,52,8)},e.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t=0|t,n=0|n,!r){var o=Math.pow(2,8*n)-1;R(this,e,t,n,o,0)}var i=1,a=0;for(this[t]=255&e;++a<n&&(i*=256);)this[t+a]=e/i&255;return t+n},e.prototype.writeUIntBE=function(e,t,n,r){if(e=+e,t=0|t,n=0|n,!r){var o=Math.pow(2,8*n)-1;R(this,e,t,n,o,0)}var i=n-1,a=1;for(this[t+i]=255&e;--i>=0&&(a*=256);)this[t+i]=e/a&255;return t+n},e.prototype.writeUInt8=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,1,255,0),e.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),this[n]=255&t,n+1},e.prototype.writeUInt16LE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,2,65535,0),e.TYPED_ARRAY_SUPPORT?(this[n]=255&t,this[n+1]=t>>>8):z(this,t,n,!0),n+2},e.prototype.writeUInt16BE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,2,65535,0),e.TYPED_ARRAY_SUPPORT?(this[n]=t>>>8,this[n+1]=255&t):z(this,t,n,!1),n+2},e.prototype.writeUInt32LE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,4,4294967295,0),e.TYPED_ARRAY_SUPPORT?(this[n+3]=t>>>24,this[n+2]=t>>>16,this[n+1]=t>>>8,this[n]=255&t):Q(this,t,n,!0),n+4},e.prototype.writeUInt32BE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,4,4294967295,0),e.TYPED_ARRAY_SUPPORT?(this[n]=t>>>24,this[n+1]=t>>>16,this[n+2]=t>>>8,this[n+3]=255&t):Q(this,t,n,!1),n+4},e.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t=0|t,!r){var o=Math.pow(2,8*n-1);R(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i<n&&(a*=256);)0>e&&0===s&&0!==this[t+i-1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},e.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t=0|t,!r){var o=Math.pow(2,8*n-1);R(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)0>e&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},e.prototype.writeInt8=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,1,127,-128),e.TYPED_ARRAY_SUPPORT||(t=Math.floor(t)),0>t&&(t=255+t+1),this[n]=255&t,n+1},e.prototype.writeInt16LE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,2,32767,-32768),e.TYPED_ARRAY_SUPPORT?(this[n]=255&t,this[n+1]=t>>>8):z(this,t,n,!0),n+2},e.prototype.writeInt16BE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,2,32767,-32768),e.TYPED_ARRAY_SUPPORT?(this[n]=t>>>8,this[n+1]=255&t):z(this,t,n,!1),n+2},e.prototype.writeInt32LE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,4,2147483647,-2147483648),e.TYPED_ARRAY_SUPPORT?(this[n]=255&t,this[n+1]=t>>>8,this[n+2]=t>>>16,this[n+3]=t>>>24):Q(this,t,n,!0),n+4},e.prototype.writeInt32BE=function(t,n,r){return t=+t,n=0|n,r||R(this,t,n,4,2147483647,-2147483648),0>t&&(t=4294967295+t+1),e.TYPED_ARRAY_SUPPORT?(this[n]=t>>>24,this[n+1]=t>>>16,this[n+2]=t>>>8,this[n+3]=255&t):Q(this,t,n,!1),n+4},e.prototype.writeFloatLE=function(e,t,n){return H(this,e,t,!0,n)},e.prototype.writeFloatBE=function(e,t,n){return H(this,e,t,!1,n)},e.prototype.writeDoubleLE=function(e,t,n){return F(this,e,t,!0,n)},e.prototype.writeDoubleBE=function(e,t,n){return F(this,e,t,!1,n)},e.prototype.copy=function(t,n,r,o){if(r||(r=0),o||0===o||(o=this.length),n>=t.length&&(n=t.length),n||(n=0),o>0&&r>o&&(o=r),o===r)return 0;if(0===t.length||0===this.length)return 0;if(0>n)throw new RangeError(\"targetStart out of bounds\");if(0>r||r>=this.length)throw new RangeError(\"sourceStart out of bounds\");if(0>o)throw new RangeError(\"sourceEnd out of bounds\");o>this.length&&(o=this.length),t.length-n<o-r&&(o=t.length-n+r);var i,a=o-r;if(this===t&&n>r&&o>n)for(i=a-1;i>=0;--i)t[i+n]=this[i+r];else if(1e3>a||!e.TYPED_ARRAY_SUPPORT)for(i=0;a>i;++i)t[i+n]=this[i+r];else Uint8Array.prototype.set.call(t,this.subarray(r,r+a),n);return a},e.prototype.fill=function(t,n,r,o){if(\"string\"==typeof t){if(\"string\"==typeof n?(o=n,n=0,r=this.length):\"string\"==typeof r&&(o=r,r=this.length),1===t.length){var i=t.charCodeAt(0);256>i&&(t=i)}if(void 0!==o&&\"string\"!=typeof o)throw new TypeError(\"encoding must be a string\");if(\"string\"==typeof o&&!e.isEncoding(o))throw new TypeError(\"Unknown encoding: \"+o)}else\"number\"==typeof t&&(t=255&t);if(0>n||this.length<n||this.length<r)throw new RangeError(\"Out of range index\");if(n>=r)return this;n>>>=0,r=void 0===r?this.length:r>>>0,t||(t=0);var a;if(\"number\"==typeof t)for(a=n;r>a;++a)this[a]=t;else{var s=e.isBuffer(t)?t:G(new e(t,o).toString()),l=s.length;for(a=0;r-n>a;++a)this[a+n]=s[a%l]}return this};var te=/[^+\\/0-9A-Za-z-_]/g}).call(t,n(306).Buffer,function(){return this}())},function(e,t){/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */\nt.read=function(e,t,n,r,o){var i,a,s=8*o-r-1,l=(1<<s)-1,c=l>>1,u=-7,d=n?o-1:0,p=n?-1:1,h=e[t+d];for(d+=p,i=h&(1<<-u)-1,h>>=-u,u+=s;u>0;i=256*i+e[t+d],d+=p,u-=8);for(a=i&(1<<-u)-1,i>>=-u,u+=r;u>0;a=256*a+e[t+d],d+=p,u-=8);if(0===i)i=1-c;else{if(i===l)return a?NaN:(h?-1:1)*(1/0);a+=Math.pow(2,r),i-=c}return(h?-1:1)*a*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var a,s,l,c=8*i-o-1,u=(1<<c)-1,d=u>>1,p=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,h=r?0:i-1,g=r?1:-1,f=0>t||0===t&&0>1/t?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=u):(a=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-a))<1&&(a--,l*=2),t+=a+d>=1?p/l:p*Math.pow(2,1-d),t*l>=2&&(a++,l/=2),a+d>=u?(s=0,a=u):a+d>=1?(s=(t*l-1)*Math.pow(2,o),a+=d):(s=t*Math.pow(2,d-1)*Math.pow(2,o),a=0));o>=8;e[n+h]=255&s,h+=g,s/=256,o-=8);for(a=a<<o|s,c+=o;c>0;e[n+h]=255&a,h+=g,a/=256,c-=8);e[n+h-g]|=128*f}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return\"[object Array]\"==n.call(e)}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return\"[object Array]\"==n.call(e)}},function(e,t){var n,r=[0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706];t.getSymbolSize=function(e){if(!e)throw new Error('\"version\" cannot be null or undefined');if(1>e||e>40)throw new Error('\"version\" should be in range from 1 to 40');return 4*e+17},t.getSymbolTotalCodewords=function(e){return r[e]},t.getBCHDigit=function(e){for(var t=0;0!==e;)t++,e>>>=1;return t},t.setToSJISFunction=function(e){if(\"function\"!=typeof e)throw new Error('\"toSJISFunc\" is not a valid function.');n=e},t.isKanjiModeEnabled=function(){return\"undefined\"!=typeof n},t.toSJIS=function(e){return n(e)}},function(e,t){function n(e){if(\"string\"!=typeof e)throw new Error(\"Param is not a string\");var n=e.toLowerCase();switch(n){case\"l\":case\"low\":return t.L;case\"m\":case\"medium\":return t.M;case\"q\":case\"quartile\":return t.Q;case\"h\":case\"high\":return t.H;default:throw new Error(\"Unknown EC Level: \"+e)}}t.L={bit:1},t.M={bit:0},t.Q={bit:3},t.H={bit:2},t.isValid=function(e){return e&&\"undefined\"!=typeof e.bit&&e.bit>=0&&e.bit<4},t.from=function(e,r){if(t.isValid(e))return e;try{return n(e)}catch(o){return r}}},function(e,t){function n(){this.buffer=[],this.length=0}n.prototype={get:function(e){var t=Math.floor(e/8);return 1===(this.buffer[t]>>>7-e%8&1)},put:function(e,t){for(var n=0;t>n;n++)this.putBit(1===(e>>>t-n-1&1))},getLengthInBits:function(){return this.length},putBit:function(e){var t=Math.floor(this.length/8);this.buffer.length<=t&&this.buffer.push(0),e&&(this.buffer[t]|=128>>>this.length%8),this.length++}},e.exports=n},function(e,t,n){function r(e){if(!e||1>e)throw new Error(\"BitMatrix size must be defined and greater than 0\");this.size=e,this.data=new o(e*e),this.data.fill(0),this.reservedBit=new o(e*e),this.reservedBit.fill(0)}var o=n(305);r.prototype.set=function(e,t,n,r){var o=e*this.size+t;this.data[o]=n,r&&(this.reservedBit[o]=!0)},r.prototype.get=function(e,t){return this.data[e*this.size+t]},r.prototype.xor=function(e,t,n){this.data[e*this.size+t]^=n},r.prototype.isReserved=function(e,t){return this.reservedBit[e*this.size+t]},e.exports=r},function(e,t,n){var r=n(310).getSymbolSize;t.getRowColCoords=function(e){if(1===e)return[];for(var t=Math.floor(e/7)+2,n=r(e),o=145===n?26:2*Math.ceil((n-13)/(2*t-2)),i=[n-7],a=1;t-1>a;a++)i[a]=i[a-1]-o;return i.push(6),i.reverse()},t.getPositions=function(e){for(var n=[],r=t.getRowColCoords(e),o=r.length,i=0;o>i;i++)for(var a=0;o>a;a++)0===i&&0===a||0===i&&a===o-1||i===o-1&&0===a||n.push([r[i],r[a]]);return n}},function(e,t,n){var r=n(310).getSymbolSize,o=7;t.getPositions=function(e){var t=r(e);return[[0,0],[t-o,0],[0,t-o]]}},function(e,t){function n(e,n,r){switch(e){case t.Patterns.PATTERN000:return(n+r)%2===0;case t.Patterns.PATTERN001:return n%2===0;case t.Patterns.PATTERN010:return r%3===0;case t.Patterns.PATTERN011:return(n+r)%3===0;case t.Patterns.PATTERN100:return(Math.floor(n/2)+Math.floor(r/3))%2===0;case t.Patterns.PATTERN101:return n*r%2+n*r%3===0;case t.Patterns.PATTERN110:return(n*r%2+n*r%3)%2===0;case t.Patterns.PATTERN111:return(n*r%3+(n+r)%2)%2===0;default:throw new Error(\"bad maskPattern:\"+e)}}t.Patterns={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var r={N1:3,N2:3,N3:40,N4:10};t.isValid=function(e){return e&&\"\"!==e&&!isNaN(e)&&e>=0&&7>=e},t.from=function(e){return t.isValid(e)?parseInt(e,10):void 0},t.getPenaltyN1=function(e){for(var t=e.size,n=0,o=0,i=0,a=null,s=null,l=0;t>l;l++){o=i=0,a=s=null;for(var c=0;t>c;c++){var u=e.get(l,c);u===a?o++:(o>=5&&(n+=r.N1+(o-5)),a=u,o=1),u=e.get(c,l),u===s?i++:(i>=5&&(n+=r.N1+(i-5)),s=u,i=1)}o>=5&&(n+=r.N1+(o-5)),i>=5&&(n+=r.N1+(i-5))}return n},t.getPenaltyN2=function(e){for(var t=e.size,n=0,o=0;t-1>o;o++)for(var i=0;t-1>i;i++){var a=e.get(o,i)+e.get(o,i+1)+e.get(o+1,i)+e.get(o+1,i+1);(4===a||0===a)&&n++}return n*r.N2},t.getPenaltyN3=function(e){for(var t=e.size,n=0,o=0,i=0,a=0;t>a;a++){o=i=0;for(var s=0;t>s;s++)o=o<<1&2047|e.get(a,s),s>=10&&(1488===o||93===o)&&n++,i=i<<1&2047|e.get(s,a),s>=10&&(1488===i||93===i)&&n++}return n*r.N3},t.getPenaltyN4=function(e){for(var t=0,n=e.data.length,o=0;n>o;o++)t+=e.data[o];var i=Math.abs(Math.ceil(100*t/n/5)-10);return i*r.N4},t.applyMask=function(e,t){for(var r=t.size,o=0;r>o;o++)for(var i=0;r>i;i++)t.isReserved(i,o)||t.xor(i,o,n(e,i,o))},t.getBestMask=function(e,n){for(var r=Object.keys(t.Patterns).length,o=0,i=1/0,a=0;r>a;a++){n(a),t.applyMask(a,e);var s=t.getPenaltyN1(e)+t.getPenaltyN2(e)+t.getPenaltyN3(e)+t.getPenaltyN4(e);t.applyMask(a,e),i>s&&(i=s,o=a)}return o}},function(e,t,n){var r=n(311),o=[1,1,1,1,1,1,1,1,1,1,2,2,1,2,2,4,1,2,4,4,2,4,4,4,2,4,6,5,2,4,6,6,2,5,8,8,4,5,8,8,4,5,8,11,4,8,10,11,4,9,12,16,4,9,16,16,6,10,12,18,6,10,17,16,6,11,16,19,6,13,18,21,7,14,21,25,8,16,20,25,8,17,23,25,9,17,23,34,9,18,25,30,10,20,27,32,12,21,29,35,12,23,34,37,12,25,34,40,13,26,35,42,14,28,38,45,15,29,40,48,16,31,43,51,17,33,45,54,18,35,48,57,19,37,51,60,19,38,53,63,20,40,56,66,21,43,59,70,22,45,62,74,24,47,65,77,25,49,68,81],i=[7,10,13,17,10,16,22,28,15,26,36,44,20,36,52,64,26,48,72,88,36,64,96,112,40,72,108,130,48,88,132,156,60,110,160,192,72,130,192,224,80,150,224,264,96,176,260,308,104,198,288,352,120,216,320,384,132,240,360,432,144,280,408,480,168,308,448,532,180,338,504,588,196,364,546,650,224,416,600,700,224,442,644,750,252,476,690,816,270,504,750,900,300,560,810,960,312,588,870,1050,336,644,952,1110,360,700,1020,1200,390,728,1050,1260,420,784,1140,1350,450,812,1200,1440,480,868,1290,1530,510,924,1350,1620,540,980,1440,1710,570,1036,1530,1800,570,1064,1590,1890,600,1120,1680,1980,630,1204,1770,2100,660,1260,1860,2220,720,1316,1950,2310,750,1372,2040,2430];t.getBlocksCount=function(e,t){switch(t){case r.L:return o[4*(e-1)+0];case r.M:return o[4*(e-1)+1];case r.Q:return o[4*(e-1)+2];case r.H:return o[4*(e-1)+3];default:return void 0}},t.getTotalCodewordsCount=function(e,t){switch(t){case r.L:return i[4*(e-1)+0];case r.M:return i[4*(e-1)+1];case r.Q:return i[4*(e-1)+2];case r.H:return i[4*(e-1)+3];default:return void 0}}},function(e,t,n){function r(e){this.genPoly=void 0,this.degree=e,this.degree&&this.initialize(this.degree)}var o=n(305),i=n(319);r.prototype.initialize=function(e){this.degree=e,this.genPoly=i.generateECPolynomial(this.degree)},r.prototype.encode=function(e){if(!this.genPoly)throw new Error(\"Encoder not initialized\");var t=new o(this.degree);t.fill(0);var n=o.concat([e,t],e.length+this.degree),r=i.mod(n,this.genPoly),a=this.degree-r.length;if(a>0){var s=new o(this.degree);return s.fill(0),r.copy(s,a),s}return r},e.exports=r},function(e,t,n){var r=n(305),o=n(320);t.mul=function(e,t){var n=new r(e.length+t.length-1);n.fill(0);for(var i=0;i<e.length;i++)for(var a=0;a<t.length;a++)n[i+a]^=o.mul(e[i],t[a]);return n},t.mod=function(e,t){for(var n=new r(e);n.length-t.length>=0;){for(var i=n[0],a=0;a<t.length;a++)n[a]^=o.mul(t[a],i);for(var s=0;s<n.length&&0===n[s];)s++;n=n.slice(s)}return n},t.generateECPolynomial=function(e){for(var n=new r([1]),i=0;e>i;i++)n=t.mul(n,[1,o.exp(i)]);return n}},function(e,t,n){var r=n(305),o=new r(512),i=new r(256);!function(){for(var e=1,t=0;255>t;t++)o[t]=e,i[e]=t,e<<=1,256&e&&(e^=285);for(t=255;512>t;t++)o[t]=o[t-255]}(),t.log=function(e){if(1>e)throw new Error(\"log(\"+e+\")\");return i[e]},t.exp=function(e){return o[e]},t.mul=function(e,t){return 0===e||0===t?0:o[i[e]+i[t]]}},function(e,t,n){function r(e,n,r){for(var o=1;40>=o;o++)if(n<=t.getCapacity(o,r,e))return o;return void 0}function o(e,t){return u.getCharCountIndicator(e,t)+4}function i(e,t){var n=0;return e.forEach(function(e){var r=o(e.mode,t);n+=r+e.getBitsLength()}),n}function a(e,n){for(var r=1;40>=r;r++){var o=i(e,r);if(o<=t.getCapacity(r,n,u.MIXED))return r}return void 0}var s=n(310),l=n(317),c=n(311),u=n(322),d=n(309),p=7973,h=s.getBCHDigit(p);t.isValid=function(e){return!isNaN(e)&&e>=1&&40>=e},t.from=function(e,n){return t.isValid(e)?parseInt(e,10):n},t.getCapacity=function(e,n,r){if(!t.isValid(e))throw new Error(\"Invalid QR Code version\");\"undefined\"==typeof r&&(r=u.BYTE);var i=s.getSymbolTotalCodewords(e),a=l.getTotalCodewordsCount(e,n),c=8*(i-a);if(r===u.MIXED)return c;var d=c-o(r,e);switch(r){case u.NUMERIC:return Math.floor(d/10*3);case u.ALPHANUMERIC:return Math.floor(d/11*2);case u.KANJI:return Math.floor(d/13);case u.BYTE:default:return Math.floor(d/8)}},t.getBestVersionForData=function(e,t){var n,o=c.from(t,c.M);if(d(e)){if(e.length>1)return a(e,o);if(0===e.length)return 1;n=e[0]}else n=e;return r(n.mode,n.getLength(),o)},t.getEncodedBits=function(e){if(!t.isValid(e)||7>e)throw new Error(\"Invalid QR Code version\");for(var n=e<<12;s.getBCHDigit(n)-h>=0;)n^=p<<s.getBCHDigit(n)-h;return e<<12|n}},function(e,t,n){function r(e){if(\"string\"!=typeof e)throw new Error(\"Param is not a string\");var n=e.toLowerCase();switch(n){case\"numeric\":return t.NUMERIC;case\"alphanumeric\":return t.ALPHANUMERIC;case\"kanji\":return t.KANJI;case\"byte\":return t.BYTE;default:throw new Error(\"Unknown mode: \"+e)}}var o=n(321),i=n(323);t.NUMERIC={id:\"Numeric\",bit:1,ccBits:[10,12,14]},t.ALPHANUMERIC={id:\"Alphanumeric\",bit:2,ccBits:[9,11,13]},t.BYTE={id:\"Byte\",bit:4,ccBits:[8,16,16]},t.KANJI={id:\"Kanji\",bit:8,ccBits:[8,10,12]},t.MIXED={bit:-1},t.getCharCountIndicator=function(e,t){if(!e.ccBits)throw new Error(\"Invalid mode: \"+e);if(!o.isValid(t))throw new Error(\"Invalid version: \"+t);return t>=1&&10>t?e.ccBits[0]:27>t?e.ccBits[1]:e.ccBits[2]},t.getBestModeForData=function(e){return i.testNumeric(e)?t.NUMERIC:i.testAlphanumeric(e)?t.ALPHANUMERIC:i.testKanji(e)?t.KANJI:t.BYTE},t.toString=function(e){if(e&&e.id)return e.id;throw new Error(\"Invalid mode\")},t.isValid=function(e){return e&&e.bit&&e.ccBits},t.from=function(e,n){if(t.isValid(e))return e;try{return r(e)}catch(o){return n}}},function(e,t){var n=\"[0-9]+\",r=\"[A-Z $%*+\\\\-./:]+\",o=\"(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+\";o=o.replace(/u/g,\"\\\\u\");var i=\"(?:(?![A-Z0-9 $%*+\\\\-./:]|\"+o+\").)+\";t.KANJI=new RegExp(o,\"g\"),t.BYTE_KANJI=new RegExp(\"[^A-Z0-9 $%*+\\\\-./:]+\",\"g\"),t.BYTE=new RegExp(i,\"g\"),t.NUMERIC=new RegExp(n,\"g\"),t.ALPHANUMERIC=new RegExp(r,\"g\");var a=new RegExp(\"^\"+o+\"$\"),s=new RegExp(\"^\"+n+\"$\"),l=new RegExp(\"^[A-Z0-9 $%*+\\\\-./:]+$\");t.testKanji=function(e){return a.test(e)},t.testNumeric=function(e){return s.test(e)},t.testAlphanumeric=function(e){return l.test(e)}},function(e,t,n){var r=n(310),o=1335,i=21522,a=r.getBCHDigit(o);t.getEncodedBits=function(e,t){for(var n=e.bit<<3|t,s=n<<10;r.getBCHDigit(s)-a>=0;)s^=o<<r.getBCHDigit(s)-a;return(n<<10|s)^i}},function(e,t,n){function r(e){return unescape(encodeURIComponent(e)).length}function o(e,t,n){for(var r,o=[];null!==(r=e.exec(n));)o.push({data:r[0],index:r.index,mode:t,length:r[0].length});return o}function i(e){var t,n,r=o(m.NUMERIC,d.NUMERIC,e),i=o(m.ALPHANUMERIC,d.ALPHANUMERIC,e);A.isKanjiModeEnabled()?(t=o(m.BYTE,d.BYTE,e),n=o(m.KANJI,d.KANJI,e)):(t=o(m.BYTE_KANJI,d.BYTE,e),n=[]);var a=r.concat(i,t,n);return a.sort(function(e,t){return e.index-t.index}).map(function(e){return{data:e.data,mode:e.mode,length:e.length}})}function a(e,t){switch(t){case d.NUMERIC:return p.getBitsLength(e);case d.ALPHANUMERIC:return h.getBitsLength(e);case d.KANJI:return f.getBitsLength(e);case d.BYTE:return g.getBitsLength(e)}}function s(e){return e.reduce(function(e,t){var n=e.length-1>=0?e[e.length-1]:null;return n&&n.mode===t.mode?(e[e.length-1].data+=t.data,e):(e.push(t),e)},[])}function l(e){for(var t=[],n=0;n<e.length;n++){var o=e[n];switch(o.mode){case d.NUMERIC:t.push([o,{data:o.data,mode:d.ALPHANUMERIC,length:o.length},{data:o.data,mode:d.BYTE,length:o.length}]);break;case d.ALPHANUMERIC:t.push([o,{data:o.data,mode:d.BYTE,length:o.length}]);break;case d.KANJI:t.push([o,{data:o.data,mode:d.BYTE,length:r(o.data)}]);break;case d.BYTE:t.push([{data:o.data,mode:d.BYTE,length:r(o.data)}])}}return t}function c(e,t){for(var n={},r={start:{}},o=[\"start\"],i=0;i<e.length;i++){for(var s=e[i],l=[],c=0;c<s.length;c++){var u=s[c],p=\"\"+i+c;l.push(p),n[p]={node:u,lastCount:0},r[p]={};for(var h=0;h<o.length;h++){var g=o[h];n[g]&&n[g].node.mode===u.mode?(r[g][p]=a(n[g].lastCount+u.length,u.mode)-a(n[g].lastCount,u.mode),n[g].lastCount+=u.length):(n[g]&&(n[g].lastCount=u.length),r[g][p]=a(u.length,u.mode)+4+d.getCharCountIndicator(u.mode,t))}}o=l}for(h=0;h<o.length;h++)r[o[h]].end=0;return{map:r,table:n}}function u(e,t){var n,r=d.getBestModeForData(e);if(n=d.from(t,r),n!==d.BYTE&&n.bit<r.bit)throw new Error('\"'+e+'\" cannot be encoded with mode '+d.toString(n)+\".\\n Suggested mode is: \"+d.toString(r));switch(n!==d.KANJI||A.isKanjiModeEnabled()||(n=d.BYTE),n){case d.NUMERIC:return new p(e);case d.ALPHANUMERIC:return new h(e);case d.KANJI:return new f(e);case d.BYTE:return new g(e)}}var d=n(322),p=n(326),h=n(327),g=n(328),f=n(329),m=n(323),A=n(310),M=n(330);t.fromArray=function(e){return e.reduce(function(e,t){return\"string\"==typeof t?e.push(u(t,null)):t.data&&e.push(u(t.data,t.mode)),e},[])},t.fromString=function(e,n){for(var r=i(e,A.isKanjiModeEnabled()),o=l(r),a=c(o,n),u=M.find_path(a.map,\"start\",\"end\"),d=[],p=1;p<u.length-1;p++)d.push(a.table[u[p]].node);return t.fromArray(s(d))},t.rawSplit=function(e){return t.fromArray(i(e,A.isKanjiModeEnabled()))}},function(e,t,n){function r(e){this.mode=o.NUMERIC,this.data=e.toString()}var o=n(322);r.getBitsLength=function(e){return 10*Math.floor(e/3)+(e%3?e%3*3+1:0)},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(e){var t,n,r;for(t=0;t+3<=this.data.length;t+=3)n=this.data.substr(t,3),r=parseInt(n,10),e.put(r,10);var o=this.data.length-t;o>0&&(n=this.data.substr(t),r=parseInt(n,10),e.put(r,3*o+1))},e.exports=r},function(e,t,n){function r(e){this.mode=o.ALPHANUMERIC,this.data=e}var o=n(322),i=[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\",\"Q\",\"R\",\"S\",\"T\",\"U\",\"V\",\"W\",\"X\",\"Y\",\"Z\",\" \",\"$\",\"%\",\"*\",\"+\",\"-\",\".\",\"/\",\":\"];r.getBitsLength=function(e){return 11*Math.floor(e/2)+6*(e%2)},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(e){var t;for(t=0;t+2<=this.data.length;t+=2){var n=45*i.indexOf(this.data[t]);n+=i.indexOf(this.data[t+1]),e.put(n,11)}this.data.length%2&&e.put(i.indexOf(this.data[t]),6)},e.exports=r},function(e,t,n){function r(e){this.mode=i.BYTE,this.data=new o(e)}var o=n(305),i=n(322);r.getBitsLength=function(e){return 8*e},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(e){for(var t=0,n=this.data.length;n>t;t++)e.put(this.data[t],8)},e.exports=r},function(e,t,n){function r(e){this.mode=o.KANJI,this.data=e}var o=n(322),i=n(310);r.getBitsLength=function(e){return 13*e},r.prototype.getLength=function(){return this.data.length},r.prototype.getBitsLength=function(){return r.getBitsLength(this.data.length)},r.prototype.write=function(e){var t;for(t=0;t<this.data.length;t++){var n=i.toSJIS(this.data[t]);if(n>=33088&&40956>=n)n-=33088;else{if(!(n>=57408&&60351>=n))throw new Error(\"Invalid SJIS character: \"+this.data[t]+\"\\nMake sure your charset is UTF-8\");n-=49472}n=192*(n>>>8&255)+(255&n),e.put(n,13)}},e.exports=r},function(e,t,n){\"use strict\";var r={single_source_shortest_paths:function(e,t,n){var o={},i={};i[t]=0;var a=r.PriorityQueue.make();a.push(t,0);for(var s,l,c,u,d,p,h,g,f;!a.empty();){s=a.pop(),l=s.value,u=s.cost,d=e[l]||{};for(c in d)d.hasOwnProperty(c)&&(p=d[c],h=u+p,g=i[c],f=\"undefined\"==typeof i[c],(f||g>h)&&(i[c]=h,a.push(c,h),o[c]=l))}if(\"undefined\"!=typeof n&&\"undefined\"==typeof i[n]){var m=[\"Could not find a path from \",t,\" to \",n,\".\"].join(\"\");throw new Error(m)}return o},extract_shortest_path_from_predecessor_list:function(e,t){for(var n,r=[],o=t;o;)r.push(o),n=e[o],o=e[o];return r.reverse(),r},find_path:function(e,t,n){var o=r.single_source_shortest_paths(e,t,n);return r.extract_shortest_path_from_predecessor_list(o,n)},PriorityQueue:{make:function(e){var t,n=r.PriorityQueue,o={};e=e||{};for(t in n)n.hasOwnProperty(t)&&(o[t]=n[t]);return o.queue=[],o.sorter=e.sorter||n.default_sorter,o},default_sorter:function(e,t){return e.cost-t.cost},push:function(e,t){var n={value:e,cost:t};this.queue.push(n),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return 0===this.queue.length}}};e.exports=r},function(e,t,n){function r(e,t,n){e.clearRect(0,0,t.width,t.height),t.style||(t.style={}),t.height=n,t.width=n,t.style.height=n+\"px\",t.style.width=n+\"px\"}function o(){try{return document.createElement(\"canvas\")}catch(e){throw new Error(\"You need to specify a canvas element\")}}var i=n(332);t.render=function(e,t,n){var a=n,s=t;\"undefined\"!=typeof a||t&&t.getContext||(a=t,t=void 0),t||(s=o()),a=i.getOptions(a);var l=i.getImageWidth(e.modules.size,a),c=s.getContext(\"2d\"),u=c.createImageData(l,l);return i.qrToImageData(u.data,e,a),r(c,s,l),c.putImageData(u,0,0),s},t.renderToDataURL=function(e,n,r){var o=r;\"undefined\"!=typeof o||n&&n.getContext||(o=n,n=void 0),o||(o={});var i=t.render(e,n,o),a=o.type||\"image/png\",s=o.rendererOpts||{};return i.toDataURL(a,s.quality)}},function(e,t){function n(e){if(\"string\"!=typeof e)throw new Error(\"Color should be defined as hex string\");var t=e.slice().replace(\"#\",\"\").split(\"\");if(t.length<3||5===t.length||t.length>8)throw new Error(\"Invalid hex color: \"+e);(3===t.length||4===t.length)&&(t=Array.prototype.concat.apply([],t.map(function(e){return[e,e]}))),6===t.length&&t.push(\"F\",\"F\");var n=parseInt(t.join(\"\"),16);return{r:n>>24&255,g:n>>16&255,b:n>>8&255,a:255&n,hex:\"#\"+t.slice(0,6).join(\"\")}}t.getOptions=function(e){e||(e={}),e.color||(e.color={});var t=\"undefined\"==typeof e.margin||null===e.margin||e.margin<0?4:e.margin,r=e.width&&e.width>=21?e.width:void 0,o=e.scale||4;return{width:r,scale:r?4:o,margin:t,color:{dark:n(e.color.dark||\"#000000ff\"),light:n(e.color.light||\"#ffffffff\")},type:e.type,rendererOpts:e.rendererOpts||{}}},t.getScale=function(e,t){return t.width&&t.width>=e+2*t.margin?t.width/(e+2*t.margin):t.scale},t.getImageWidth=function(e,n){var r=t.getScale(e,n);return Math.floor((e+2*n.margin)*r)},t.qrToImageData=function(e,n,r){for(var o=n.modules.size,i=n.modules.data,a=t.getScale(o,r),s=Math.floor((o+2*r.margin)*a),l=r.margin*a,c=[r.color.light,r.color.dark],u=0;s>u;u++)for(var d=0;s>d;d++){var p=4*(u*s+d),h=r.color.light;if(u>=l&&d>=l&&s-l>u&&s-l>d){var g=Math.floor((u-l)/a),f=Math.floor((d-l)/a);h=c[i[g*o+f]?1:0]}e[p++]=h.r,e[p++]=h.g,e[p++]=h.b,e[p]=h.a}}},function(e,t,n){function r(e,t){var n=e.a/255,r=t+'=\"'+e.hex+'\"';return 1>n?r+\" \"+t+'-opacity=\"'+n.toFixed(2).slice(1)+'\"':r}function o(e,t,n){var r=e+t;return\"undefined\"!=typeof n&&(r+=\" \"+n),r}function i(e,t,n){for(var r=\"\",i=0,a=!1,s=0,l=0;l<e.length;l++){var c=Math.floor(l%t),u=Math.floor(l/t);c||a||(a=!0),e[l]?(s++,l>0&&c>0&&e[l-1]||(r+=a?o(\"M\",c+n,.5+u+n):o(\"m\",i,0),i=0,a=!1),t>c+1&&e[l+1]||(r+=o(\"h\",s),s=0)):i++}return r}var a=n(332);t.render=function(e,t,n){var o=a.getOptions(t),s=e.modules.size,l=e.modules.data,c=s+2*o.margin,u=o.color.light.a?\"<path \"+r(o.color.light,\"fill\")+' d=\"M0 0h'+c+\"v\"+c+'H0z\"/>':\"\",d=\"<path \"+r(o.color.dark,\"stroke\")+' d=\"'+i(l,s,o.margin)+'\"/>',p='viewBox=\"0 0 '+c+\" \"+c+'\"',h=o.width?'width=\"'+o.width+'\" height=\"'+o.width+'\" ':\"\",g='<svg xmlns=\"http://www.w3.org/2000/svg\" '+h+p+\">\"+u+d+\"</svg>\";return\"function\"==typeof n&&n(null,g),g}},function(e,t,n){\"use strict\";function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e){return p.test(e)||u.test(e)||d.test(e)||h.test(e)}function i(e){for(var t=-1,n=0,r=e.length;r>n;n++){var i=e[n];if(o(i))return n;if(g.test(i)){if(!c.test(i))return n;-1===t&&(t=n)}}return t}function a(e){var t=[];return e=e.filter(function(e){return m.test(e)||f.test(e)?(t.push(e),!1):!0}),{list:e,filters:t}}var s=n(202),l=/\\s+/g,c=/^(?:\\[([:\\da-f.]+)\\]|(?:\\d{1,3}\\.){3}\\d{1,3})(?::(\\d+))?$/i,u=/^\\/\\//,d=/^\\/(.+)\\/(i?u?|ui)$/,p=/^[\\^!$.:*~]/,h=/^(?:https?|wss?|tunnel):\\/\\//,g=/^[^@%\\\\/\\{\\}\\(\\)<>]*[^@%\\\\/\\{\\}\\(\\)<>:](?:\\/|$)/,f=/^filter:\\/\\/\\w+:.+$/,m=/^(lineProps|excludeFilter|includeFilter):\\/\\/.*$/;e.exports=function(e){if(!e)return\"\";var t={},n={},c=[],u={},d=function(e){var t=e.join(\" \");u[t]||(u[t]=1,c.push(e))};return s.formatRules(e,t,n).forEach(function(e){if(e=e.trim()){var t=e.split(l);if(t.length<2)return void((\"@\"===e[0]||\"%\"===e[0])&&d([e]));var n=a(t);t=n.list;var s=i(t);if(-1===s)return d([e]);if(0===s){var c=t.shift();t.forEach(function(e){d([c,e].concat(r(n.filters)))})}else{var u=[],p=t.filter(function(e){return o(e)||g.test(e)?!0:void u.push(e)});u.forEach(function(e){p.forEach(function(t){d([t,e].concat(r(n.filters)))})})}}}),{rules:c,values:t,rawValues:n}}},function(e,t,n){\"use strict\";function r(e){try{return decodeURIComponent(e)}catch(t){}return e}var o=n(24),i=n(57),a=n(18),s=n(198),l=n(200),c=n(202),u=n(206),d=n(199),p=n(209),h=n(213),g=n(214),f=/^(\\d+)\\.([\\s\\S]+)$/,m=o.createClass({displayName:\"RecycleBinDialog\",getInitialState:function(){return{}},componentDidMount:function(){var e=this;d.on(\"rulesRecycleList\",function(t,n){\"Rules\"===e.state.name&&e.show(n,!0)}),d.on(\"valuesRecycleList\",function(t,n){\"Values\"===e.state.name&&e.show(n,!0)})},show:function(e,t){var n=this;e.list&&(e.list=e.list.map(function(e){return f.test(e)?{filename:r(RegExp.$2),date:c.toLocaleString(new Date(parseInt(RegExp.$1,10))),name:e}:void 0}).filter(c.noop)),n.setState(e,function(){!t&&n.refs.recycleBinDialog.show()})},hide:function(){this.refs.recycleBinDialog.hide()},checkFile:function(e,t){if(!e)return void c.showSystemError(t);if(3===e.ec){var n=this;return u.error(\"File not found\"),void l[this.state.name.toLowerCase()].recycleList(function(e,t){return e?void n.show(e):void c.showSystemError(t)})}return!0},view:function(e){var t=this,n=e.target.getAttribute(\"data-name\");l[this.state.name.toLowerCase()].recycleView({name:n},function(e,n){return t.checkFile(e,n)?e.data?void c.openEditor(e.data):u.info(\"Empty content\"):void 0})},recover:function(e){var t=this;l[t.state.name.toLowerCase()].recycleView({name:e.name},function(n,r){t.checkFile(n,r)&&(e.data=n.data,d.trigger(\"recover\"+t.state.name,e))})},remove:function(e){var t=e.target.getAttribute(\"data-name\"),n=r(t.substring(t.indexOf(\".\")+1)),o=this;p.confirm(\"Do you confirm the deletion of '\"+n+\"' completely?\",function(e){e&&l[o.state.name.toLowerCase()].recycleRemove({name:t},function(e,t){return e?void o.show(e):void c.showSystemError(t)})})},isVisible:function(){return a(i.findDOMNode(this.refs.recycleBinBody)).is(\":visible\")},render:function(){var e=this,t=e.state,n=t.list||[];return o.createElement(s,{ref:\"recycleBinDialog\",wstyle:\"w-files-dialog\"},o.createElement(\"div\",{className:\"modal-header\"},o.createElement(\"h4\",null,t.name,\" Trash\"),o.createElement(g,null)),o.createElement(\"div\",{className:\"modal-body\",ref:\"recycleBinBody\"},o.createElement(\"table\",{className:\"table\"},o.createElement(\"thead\",null,o.createElement(\"th\",{className:\"w-files-order\"},\"#\"),o.createElement(\"th\",{className:\"w-files-date\"},\"Date\"),o.createElement(\"th\",{className:\"w-files-path\"},\"Filename\"),o.createElement(\"th\",{className:\"w-files-operation\"},\"Operation\")),o.createElement(\"tbody\",{className:\"w-hover-body\"},n.length?n.map(function(t,n){return o.createElement(\"tr\",{key:t.name},o.createElement(\"th\",{className:\"w-files-order\"},n+1),o.createElement(\"td\",{className:\"w-files-date\"},t.date),o.createElement(\"td\",{className:\"w-files-path\",title:t.filename},c.isGroup(t.filename)?o.createElement(h,{name:\"triangle-right\",className:\"w-list-group-icon\"}):null,t.filename),o.createElement(\"td\",{className:\"w-files-operation\"},o.createElement(\"a\",{\"data-name\":t.name,onClick:e.view},\"View\"),o.createElement(\"a\",{onClick:function(){e.recover(t)}},\"Restore\"),o.createElement(\"a\",{\"data-name\":t.name,onClick:e.remove},\"Delete\")))}):o.createElement(\"tr\",null,o.createElement(\"td\",{colSpan:\"4\",style:{textAlign:\"center\"}},\"Empty\"))))),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))}}),A=o.createClass({displayName:\"RecycleBinDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e){this.refs.recycleBinDialog.show(e)},hide:function(){this.refs.recycleBinDialog.hide()},isVisible:function(){return this.refs.recycleBinDialog.isVisible()},render:function(){return o.createElement(m,{ref:\"recycleBinDialog\"})}});e.exports=A},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=n(337),a=n(512),s=n(198),l=n(214),c=r.createClass({displayName:\"EnabledRulesDialog\",getInitialState:function(){return{list:[]}},shouldComponentUpdate:function(){return this.refs.enabledRules.isVisible()},handleClickLocate:function(e){this.hide(),o.handleClickLocate(e)},show:function(e){this.refs.enabledRules.show(),this.setState({list:e})},hide:function(){this.refs.enabledRules.hide()},render:function(){var e=this.state.list||[];return r.createElement(s,{ref:\"enabledRules\",wstyle:\"w-enabled-rules-dialog\"},r.createElement(\"div\",{className:\"modal-header\"},r.createElement(\"h4\",null,\"Enabled Rules\"),r.createElement(l,null)),e.length?r.createElement(i,{name:\"Rules\",wrapClass:\"box fill w-enabled-rules\",onClickLocate:this.handleClickLocate,modal:e}):r.createElement(a,null))}});e.exports=c},function(e,t,n){\"use strict\";function r(e,t){var n=t?e.indexOf(t):-1,r=\"\";return-1!==n&&(r=e.substring(n),e=e.substring(0,n)),{value:e,btnText:r}}n(338);var o=n(24),i=n(57),a=n(18),s=n(202),l=n(340),c=n(341),u=n(293),d=n(342)[\"default\"],p=n(510),h=n(200),g=n(511),f=/^tunnel:\\/\\//,m=o.createClass({displayName:\"Properties\",getInitialState:function(){return{viewSource:!1}},onLocate:function(e){var t=this.props.onClickLocate;t&&t(e)},componentDidMount:function(){var e=this;\"Rules\"===this.props.name&&a(i.findDOMNode(e.refs.properties)).on(\"mouseenter\",\"td pre\",function(e){if(e.ctrlKey||e.metaKey){var t=e.target,n=t.innerText,r=n&&n.indexOf(s.SOURCE_SEP);r>0&&-1!==n.substring(r).indexOf(\":\")&&t.setAttribute(\"data-rule-source\",\"1\")}}).on(\"mouseleave\",\"td pre\",function(e){e.target.removeAttribute(\"data-rule-source\")}).on(\"click\",\"td pre\",function(t){(t.ctrlKey||t.metaKey)&&e.onLocate(t.target.innerText)})},toggle:function(){this.setState({viewSource:!this.state.viewSource})},renderValue:function(e){return e&&e.length>=2100?o.createElement(c,{text:e}):e},renderKey:function(e,t){if(this.props.hideKeys)return null;var n=this.props.onHelp,r=this.props.showEnableBtn&&\"URL\"===e&&f.test(t),i=this.props.richKey?e.indexOf(\"\\r\\x00(\"):-1;return o.createElement(\"th\",null,n?o.createElement(g,{\"data-name\":e,onClick:n}):void 0,r?o.createElement(p,null):null,this.renderValue(-1===i?e:e.substring(0,i)),-1===i?null:o.createElement(\"span\",{className:\"w-gray\"},e.substring(i+2)))},onContextMenu:function(e){s.handlePropsContextMenu(e,this.refs.contextMenu)},getElemRef:function(){return this.refs.properties},handleClick:function(e){this.onLocate(e.target.dataset.text)},render:function(){var e=this,t=e.props,n=\"Rules\"===t.name,i=t.showEnableBtn,a=t.enableViewSource,c=t.enableCopyValue,p=t.hasPluginRule,f=e.state.viewSource,m=t.rawName,A=t.showJsonView,M=t.cssMap,w=t.rawValue,v=t.modal||{},b=t.title||{},y=t.wrapClass||\"\",x=n?s.SOURCE_SEP:null,T=Array.isArray(v),C=Object.keys(v);if(a||c){var N=[];C.forEach(function(e){if(!p||\"rule\"!==e){var t=v[e];e=a?e+\": \":\"\",N.push(Array.isArray(t)?t.map(function(t){return e+s.toString(t)}).join(\"\\n\"):e+s.toString(t))}}),a=a&&N.join(\"\\n\"),c&&(c=N.filter(s.noop).join(\"\\n\").trim(),n&&(c=s.removeRulesComments(c)))}if(e.textStr!==a){e.textStr=a;try{e.jsonStr=JSON.stringify(v,null,\"  \")}catch(I){e.jsonStr=void 0}}return o.createElement(\"div\",{ref:\"properties\",className:\"w-props-wrap \"+(f?\"w-props-view-source \":\"w-props-view-parsed \")+(t.hide?\"hide\":\"\")+(y?\" \"+y:\"\")},a?o.createElement(\"div\",{className:\"w-textarea-bar\"},o.createElement(l,{value:a,name:\"AsText\"}),e.jsonStr?o.createElement(l,{value:e.jsonStr,name:\"AsJSON\"}):void 0,o.createElement(\"a\",{onClick:e.toggle},f?\"Form\":\"Text\")):void 0,c?o.createElement(\"div\",{className:\"w-textarea-bar\"},o.createElement(l,{value:c,name:t.name})):void 0,a?o.createElement(\"pre\",{className:\"w-props-source\"},e.renderValue(a)):void 0,o.createElement(\"table\",{className:\"table w-props w-props-parsed \"+(t.className||\"\"),onContextMenu:e.onContextMenu},o.createElement(\"tbody\",null,w?o.createElement(\"tr\",{key:\"raw\",className:w?void 0:\"w-no-value\",\"data-name\":m,\"data-value\":w},o.createElement(\"th\",null,m),o.createElement(\"td\",{className:\"w-props-raw-data w-user-select-none\",title:w},o.createElement(\"pre\",null,e.renderValue(w)))):null,C.map(function(t,a){var l=v[t];if(Array.isArray(l))return l.map(function(n,r){n=s.toString(n);var i=A&&s.likeJson(n)&&s.parseJSON(n);return o.createElement(\"tr\",{key:r,className:n?void 0:\"w-no-value\",\"data-name\":t,\"data-value\":n},e.renderKey(t,n),o.createElement(\"td\",{className:i?\"w-props-json\":\"w-user-select-none\",onContextMenu:i?s.stopPropagation:null},i?o.createElement(d,{data:i,onSearch:function(){s.showJSONDialog(i)}}):o.createElement(\"pre\",null,e.renderValue(n))))});l=s.toString(l);var c=A&&s.likeJson(l)&&s.parseJSON(l),u=M&&M[t],p=u&&u.style,f=u&&u.className,m=!c&&i&&\"Status Code\"===t&&\"captureError\"===l,w=n?l.split(s.CRLF_RE):null;return o.createElement(\"tr\",{key:t,title:b[t],className:l?void 0:\"w-no-value\",\"data-name\":t,\"data-value\":l},e.renderKey(T?a+1+\"\":t,l),o.createElement(\"td\",{className:(c?\"w-props-json \":\"w-user-select-none \")+(f||\"\"),style:p,onContextMenu:c?s.stopPropagation:null},c?o.createElement(d,{data:c,onSearch:function(){s.showJSONDialog(c)}}):w?w.map(function(t){var n=r(t,x),i=n.btnText,a=-1===i.indexOf(\":\")&&(!h.whistleId||\"# (From Mock Rules)\"!==i&&\"# (From Service Rules)\"!==i);return o.createElement(\"pre\",null,e.renderValue(n.value),a?o.createElement(\"span\",{className:\"w-src-info\"},i):o.createElement(\"a\",{className:\"w-src-info\",onClick:e.handleClick,\"data-text\":t},i))}):o.createElement(\"pre\",{className:m?\"w-align-items\":null},m?o.createElement(g,{className:\"w-props-icon\",docsUrl:\"faq.html#capture-error\"}):null,e.renderValue(l))))}))),o.createElement(u,{ref:\"contextMenu\"}))}});e.exports=m},function(e,t,n){var r=n(339);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-props-wrap{position:relative}.w-props-source{padding:5px 10px}.w-props-view-parsed .w-props-source,.w-props-view-source .w-props-parsed{display:none}.w-props-view-parsed .w-textarea-bar,.w-props-view-source .w-textarea-bar{right:5px!important}.w-props td,.w-props th{border:none!important;font-size:9pt;padding-top:3px!important;padding-bottom:3px!important}.w-props tr{border-bottom:1px solid var(--c-border)}.w-props th{width:93pt;text-align:right;font-weight:400;background:var(--b-prop);padding-right:3px;word-break:break-word}.w-enabled-rules{overflow-y:auto}.w-enabled-rules .w-props th{width:36px;padding-left:5px;vertical-align:middle}.w-props td{padding-left:5px;vertical-align:top;overflow:hidden;word-break:break-word}.w-props th .glyphicon-question-sign{display:none}.w-props th .w-enable-https-help,.w-props th:hover .w-help-icon{display:inline-block;vertical-align:top}.w-props-raw-data{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.w-user-select-none{user-select:none}.w-user-select-none>pre,.w-user-select-none>span{user-select:text;display:block}.w-props .w-props-json{padding-top:0!important;padding-bottom:5px!important}.w-props .w-props-json>ul{background:var(--b-default)!important;margin:0!important}.w-props-icon{cursor:pointer!important;text-decoration:none!important;color:var(--c-default)}.w-src-info{margin-left:30px}span.w-src-info{color:var(--c-gray)}.w-com-dialog .form-control{padding:6px}\",\"\"]);\n},function(e,t,n){\"use strict\";var r=n(24),o=r.createClass({displayName:\"CopyBtn\",getInitialState:function(){return{}},handleLeave:function(){this.setState({copied:!1})},handleCopy:function(){this.setState({copied:!0})},render:function(){var e=this.state.copied;return r.createElement(\"a\",{onMouseLeave:this.handleLeave,onClick:this.handleCopy,className:\"w-copy-btn\"+(e?\" w-copied-text\":\" w-copy-text\"),draggable:\"false\",\"data-clipboard-text\":this.props.value||\"\"},(e?\"Copied\":\"Copy\")+(this.props.name||\"\"))}});e.exports=o},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=1024,a=32768,s=3*a,l=r.createClass({displayName:\"ExpandCollapse\",getInitialState:function(){return{expandLength:i}},componentWillReceiveProps:function(e){(e.text!==this.props.text||this.props.wStyle!==e.wStyle)&&(this.state.expandLength=i)},onCollapse:function(){this.setState({expandLength:i})},onExpand:function(){this.setState({expandLength:this.state.expandLength+a})},viewAll:function(){o.openEditor(this.props.text)},render:function(){var e=this.props.text,t=e&&e.length||0,n=this.props.wStyle;if(2100>t)return r.createElement(\"span\",{style:n},e);var o=this.state.expandLength,a=o>=t,l=!a&&o>s;return r.createElement(\"span\",{style:n},a?e:e.substring(0,o)+\"...\",l?r.createElement(\"button\",{onClick:this.viewAll,className:\"w-expand-collapse\"},\"ViewAll\"):a?void 0:r.createElement(\"button\",{onClick:this.onExpand,className:\"w-expand-collapse\"},\"Expand\"),o>i?r.createElement(\"button\",{onClick:this.onCollapse,className:\"w-expand-collapse\"},\"Collapse\"):void 0)}});e.exports=l},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e,t){var n={getArrowStyle:\"arrow\",getListStyle:\"nestedNodeChildren\",getItemStringStyle:\"nestedNodeItemString\",getLabelStyle:\"label\",getValueStyle:\"valueText\"},r=y[\"default\"](n).filter(function(e){return t[e]});return r.length>0&&(e=\"string\"==typeof e?{extend:e}:v[\"default\"]({},e),r.forEach(function(r){console.error('Styling method \"'+r+'\" is deprecated, use \"theme\" property instead'),e[n[r]]=function(e){for(var n=arguments.length,o=Array(n>1?n-1:0),i=1;n>i;i++)o[i-1]=arguments[i];var a=e.style;return{style:v[\"default\"]({},a,t[r].apply(t,o))}}})),e}function i(e){var t=o(e.theme,e);return e.invertTheme&&(\"string\"==typeof t?t+=\":inverted\":t&&t.extend?t=\"string\"==typeof t?v[\"default\"]({},t,{extend:t.extend+\":inverted\"}):v[\"default\"]({},t,{extend:D.invertTheme(t.extend)}):t&&(t=D.invertTheme(t))),{styling:E[\"default\"](t)}}var a=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};t.__esModule=!0;var s=n(343),l=r(s),c=n(344),u=r(c),d=n(347),p=r(d),h=n(413),g=r(h),f=n(414),m=r(f),A=n(415),M=r(A),w=n(423),v=r(w),b=n(428),y=r(b),x=n(24),T=r(x),C=n(432),N=r(C),I=n(454),E=r(I),D=n(455),S=n(293),L=r(S),k=function(e){return e},j=function(e,t,n){return 0===n},U=function(e,t,n,r){return T[\"default\"].createElement(\"span\",null,n,\" \",r)},B=function(e){var t=e[0];return T[\"default\"].createElement(\"span\",null,t,\":\")},R=function(){return!1},z=function(e){function t(n){g[\"default\"](this,t);var r,o,s,l=[{name:\"Copy\"},{name:\"Copy Key\"},{name:\"Copy Value\"},{name:\"Copy Object\"},{name:\"Collapse Parent\"}];n.expandAll&&(r={name:\"Expand All\",onClick:function(){n.expandAll()}},l.push(r)),n.collapseAll&&(o={name:\"Collapse All\",onClick:function(){n.collapseAll()}},l.push(o)),n.onSearch&&(s={name:\"Search Object\",onClick:function(){n.onSearch()}},l.push(s));var c={name:\"Inspect Value\"};l.push(c);var d=m[\"default\"](this,e.call(this,n));return d.onContextMenu=function(e){var t=S.$(e.target),i=t.closest(\"li\").find(\">label[data-key-path]:first\"),h=S.util.parseJSON(i.attr(\"data-key-path\"));if(Array.isArray(h)){var g=d.props.data,f=h.length,m=f>=2||n.onSearch;if(l[3].copyText=g?JSON.stringify(g,null,\"  \"):\"\",g)for(var A=f-2;A>=0;A--)g=g&&g[h[A]];var M=b?90:120;n.onSearch&&!s&&(s=r,r=null),r&&(M+=30),o&&(M+=30),s&&(M+=30);var w=S.util.getSelectedText(e.clientX,e.clientY),v=g&&m&&(\"object\"===(\"undefined\"==typeof g?\"undefined\":a(g))?g:S.util.parseJSON(g));c.hide=!v,c.onClick=v?function(){S.util.showJSONDialog(v,[h[0]])}:null,M+=v?30:0,M+=w?30:0;var b=1===f,y=S.util.getMenuPosition(e,110,M);if(y.className=\"w-inspectors-ctx-menu\",l[0].copyText=w,l[0].hide=!w,l[1].copyText=h[0],g&&\"object\"===(\"undefined\"==typeof g?\"undefined\":p[\"default\"](g))&&!(g instanceof String))try{g=u[\"default\"](g,null,\"  \")}catch(e){}l[2].copyText=g+\"\",l[4].onClick=function(){i.closest(\"li\").parent().closest(\"li\").find(\"div:first\").click()},l[4].hide=b;var x=l;t.closest(\"label\").length||(x=x.map(S.util.noop),x[1]=l[2],x[2]=l[1]),y.list=x,d.refs.contextMenu.show(y),e.preventDefault(),e.stopPropagation()}},d.state=i(n),d}return M[\"default\"](t,e),t.prototype.componentWillReceiveProps=function(e){var t=this;[\"theme\",\"invertTheme\"].find(function(n){return e[n]!==t.props[n]})&&this.setState(i(e))},t.prototype.shouldComponentUpdate=function(e){var t=this;return!!y[\"default\"](e).find(function(n){return\"keyPath\"===n?e[n].join(\"/\")!==t.props[n].join(\"/\"):e[n]!==t.props[n]})},t.prototype.render=function(){var e=this.props,t=e.data,n=e.keyPath,r=e.postprocessValue,o=e.hideRoot,i=(e.theme,e.invertTheme,l[\"default\"](e,[\"data\",\"keyPath\",\"postprocessValue\",\"hideRoot\",\"theme\",\"invertTheme\"])),a=this.state.styling;return T[\"default\"].createElement(\"ul\",v[\"default\"]({},a(\"tree\"),{onContextMenu:this.onContextMenu,className:\"w-json-tree\"}),T[\"default\"].createElement(N[\"default\"],v[\"default\"]({},v[\"default\"]({postprocessValue:r,hideRoot:o,styling:a},i),{keyPath:o?[]:n,value:r(t)})),T[\"default\"].createElement(L[\"default\"],{ref:\"contextMenu\"}))},t}(T[\"default\"].Component);z.defaultProps={shouldExpandNode:j,hideRoot:!1,keyPath:[\"root\"],getItemString:U,labelRenderer:B,valueRenderer:k,postprocessValue:k,isCustomNode:R,collectionLimit:50,invertTheme:!0},t[\"default\"]=z},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]=function(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}},function(e,t,n){e.exports={\"default\":n(345),__esModule:!0}},function(e,t,n){var r=n(346),o=r.JSON||(r.JSON={stringify:JSON.stringify});e.exports=function(e){return o.stringify.apply(o,arguments)}},function(e,t){var n=e.exports={version:\"2.6.12\"};\"number\"==typeof __e&&(__e=n)},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(348),i=r(o),a=n(398),s=r(a),l=\"function\"==typeof s[\"default\"]&&\"symbol\"==typeof i[\"default\"]?function(e){return typeof e}:function(e){return e&&\"function\"==typeof s[\"default\"]&&e.constructor===s[\"default\"]&&e!==s[\"default\"].prototype?\"symbol\":typeof e};t[\"default\"]=\"function\"==typeof s[\"default\"]&&\"symbol\"===l(i[\"default\"])?function(e){return\"undefined\"==typeof e?\"undefined\":l(e)}:function(e){return e&&\"function\"==typeof s[\"default\"]&&e.constructor===s[\"default\"]&&e!==s[\"default\"].prototype?\"symbol\":\"undefined\"==typeof e?\"undefined\":l(e)}},function(e,t,n){e.exports={\"default\":n(349),__esModule:!0}},function(e,t,n){n(350),n(393),e.exports=n(397).f(\"iterator\")},function(e,t,n){\"use strict\";var r=n(351)(!0);n(354)(String,\"String\",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,n=this._i;return n>=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var r=n(352),o=n(353);e.exports=function(e){return function(t,n){var i,a,s=String(o(t)),l=r(n),c=s.length;return 0>l||l>=c?e?\"\":void 0:(i=s.charCodeAt(l),55296>i||i>56319||l+1===c||(a=s.charCodeAt(l+1))<56320||a>57343?e?s.charAt(l):i:e?s.slice(l,l+2):(i-55296<<10)+(a-56320)+65536)}}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError(\"Can't call method on  \"+e);return e}},function(e,t,n){\"use strict\";var r=n(355),o=n(356),i=n(371),a=n(360),s=n(372),l=n(373),c=n(389),u=n(391),d=n(390)(\"iterator\"),p=!([].keys&&\"next\"in[].keys()),h=\"@@iterator\",g=\"keys\",f=\"values\",m=function(){return this};e.exports=function(e,t,n,A,M,w,v){l(n,t,A);var b,y,x,T=function(e){if(!p&&e in E)return E[e];switch(e){case g:return function(){return new n(this,e)};case f:return function(){return new n(this,e)}}return function(){return new n(this,e)}},C=t+\" Iterator\",N=M==f,I=!1,E=e.prototype,D=E[d]||E[h]||M&&E[M],S=D||T(M),L=M?N?T(\"entries\"):S:void 0,k=\"Array\"==t?E.entries||D:D;if(k&&(x=u(k.call(new e)),x!==Object.prototype&&x.next&&(c(x,C,!0),r||\"function\"==typeof x[d]||a(x,d,m))),N&&D&&D.name!==f&&(I=!0,S=function(){return D.call(this)}),r&&!v||!p&&!I&&E[d]||a(E,d,S),s[t]=S,s[C]=m,M)if(b={values:N?S:T(f),keys:w?S:T(g),entries:L},v)for(y in b)y in E||i(E,y,b[y]);else o(o.P+o.F*(p||I),t,b);return b}},function(e,t){e.exports=!0},function(e,t,n){var r=n(357),o=n(346),i=n(358),a=n(360),s=n(370),l=\"prototype\",c=function(e,t,n){var u,d,p,h=e&c.F,g=e&c.G,f=e&c.S,m=e&c.P,A=e&c.B,M=e&c.W,w=g?o:o[t]||(o[t]={}),v=w[l],b=g?r:f?r[t]:(r[t]||{})[l];g&&(n=t);for(u in n)d=!h&&b&&void 0!==b[u],d&&s(w,u)||(p=d?b[u]:n[u],w[u]=g&&\"function\"!=typeof b[u]?n[u]:A&&d?i(p,r):M&&b[u]==p?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t[l]=e[l],t}(p):m&&\"function\"==typeof p?i(Function.call,p):p,m&&((w.virtual||(w.virtual={}))[u]=p,e&c.R&&v&&!v[u]&&a(v,u,p)))};c.F=1,c.G=2,c.S=4,c.P=8,c.B=16,c.W=32,c.U=64,c.R=128,e.exports=c},function(e,t){var n=e.exports=\"undefined\"!=typeof window&&window.Math==Math?window:\"undefined\"!=typeof self&&self.Math==Math?self:Function(\"return this\")();\"number\"==typeof __g&&(__g=n)},function(e,t,n){var r=n(359);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,o){return e.call(t,n,r,o)}}return function(){return e.apply(t,arguments)}}},function(e,t){e.exports=function(e){if(\"function\"!=typeof e)throw TypeError(e+\" is not a function!\");return e}},function(e,t,n){var r=n(361),o=n(369);e.exports=n(365)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var r=n(362),o=n(364),i=n(368),a=Object.defineProperty;t.f=n(365)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(s){}if(\"get\"in n||\"set\"in n)throw TypeError(\"Accessors not supported!\");return\"value\"in n&&(e[t]=n.value),e}},function(e,t,n){var r=n(363);e.exports=function(e){if(!r(e))throw TypeError(e+\" is not an object!\");return e}},function(e,t){e.exports=function(e){return\"object\"==typeof e?null!==e:\"function\"==typeof e}},function(e,t,n){e.exports=!n(365)&&!n(366)(function(){return 7!=Object.defineProperty(n(367)(\"div\"),\"a\",{get:function(){return 7}}).a})},function(e,t,n){e.exports=!n(366)(function(){return 7!=Object.defineProperty({},\"a\",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e){try{return!!e()}catch(t){return!0}}},function(e,t,n){var r=n(363),o=n(357).document,i=r(o)&&r(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t,n){var r=n(363);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&\"function\"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if(\"function\"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&\"function\"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError(\"Can't convert object to primitive value\")}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){e.exports=n(360)},function(e,t){e.exports={}},function(e,t,n){\"use strict\";var r=n(374),o=n(369),i=n(389),a={};n(360)(a,n(390)(\"iterator\"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+\" Iterator\")}},function(e,t,n){var r=n(362),o=n(375),i=n(387),a=n(384)(\"IE_PROTO\"),s=function(){},l=\"prototype\",c=function(){var e,t=n(367)(\"iframe\"),r=i.length,o=\"<\",a=\">\";for(t.style.display=\"none\",n(388).appendChild(t),t.src=\"javascript:\",e=t.contentWindow.document,e.open(),e.write(o+\"script\"+a+\"document.F=Object\"+o+\"/script\"+a),e.close(),c=e.F;r--;)delete c[l][i[r]];return c()};e.exports=Object.create||function(e,t){var n;return null!==e?(s[l]=r(e),n=new s,s[l]=null,n[a]=e):n=c(),void 0===t?n:o(n,t)}},function(e,t,n){var r=n(361),o=n(362),i=n(376);e.exports=n(365)?Object.defineProperties:function(e,t){o(e);for(var n,a=i(t),s=a.length,l=0;s>l;)r.f(e,n=a[l++],t[n]);return e}},function(e,t,n){var r=n(377),o=n(387);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t,n){var r=n(370),o=n(378),i=n(381)(!1),a=n(384)(\"IE_PROTO\");e.exports=function(e,t){var n,s=o(e),l=0,c=[];for(n in s)n!=a&&r(s,n)&&c.push(n);for(;t.length>l;)r(s,n=t[l++])&&(~i(c,n)||c.push(n));return c}},function(e,t,n){var r=n(379),o=n(353);e.exports=function(e){return r(o(e))}},function(e,t,n){var r=n(380);e.exports=Object(\"z\").propertyIsEnumerable(0)?Object:function(e){return\"String\"==r(e)?e.split(\"\"):Object(e)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var r=n(378),o=n(382),i=n(383);e.exports=function(e){return function(t,n,a){var s,l=r(t),c=o(l.length),u=i(a,c);if(e&&n!=n){for(;c>u;)if(s=l[u++],s!=s)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===n)return e||u||0;return!e&&-1}}},function(e,t,n){var r=n(352),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t,n){var r=n(352),o=Math.max,i=Math.min;e.exports=function(e,t){return e=r(e),0>e?o(e+t,0):i(e,t)}},function(e,t,n){var r=n(385)(\"keys\"),o=n(386);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(346),o=n(357),i=\"__core-js_shared__\",a=o[i]||(o[i]={});(e.exports=function(e,t){return a[e]||(a[e]=void 0!==t?t:{})})(\"versions\",[]).push({version:r.version,mode:n(355)?\"pure\":\"global\",copyright:\"© 2020 Denis Pushkarev (zloirock.ru)\"})},function(e,t){var n=0,r=Math.random();e.exports=function(e){return\"Symbol(\".concat(void 0===e?\"\":e,\")_\",(++n+r).toString(36))}},function(e,t){e.exports=\"constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf\".split(\",\")},function(e,t,n){var r=n(357).document;e.exports=r&&r.documentElement},function(e,t,n){var r=n(361).f,o=n(370),i=n(390)(\"toStringTag\");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){var r=n(385)(\"wks\"),o=n(386),i=n(357).Symbol,a=\"function\"==typeof i,s=e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)(\"Symbol.\"+e))};s.store=r},function(e,t,n){var r=n(370),o=n(392),i=n(384)(\"IE_PROTO\"),a=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=o(e),r(e,i)?e[i]:\"function\"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,n){var r=n(353);e.exports=function(e){return Object(r(e))}},function(e,t,n){n(394);for(var r=n(357),o=n(360),i=n(372),a=n(390)(\"toStringTag\"),s=\"CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList\".split(\",\"),l=0;l<s.length;l++){var c=s[l],u=r[c],d=u&&u.prototype;d&&!d[a]&&o(d,a,c),i[c]=i.Array}},function(e,t,n){\"use strict\";var r=n(395),o=n(396),i=n(372),a=n(378);e.exports=n(354)(Array,\"Array\",function(e,t){this._t=a(e),this._i=0,this._k=t},function(){var e=this._t,t=this._k,n=this._i++;return!e||n>=e.length?(this._t=void 0,o(1)):\"keys\"==t?o(0,n):\"values\"==t?o(0,e[n]):o(0,[n,e[n]])},\"values\"),i.Arguments=i.Array,r(\"keys\"),r(\"values\"),r(\"entries\")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){t.f=n(390)},function(e,t,n){e.exports={\"default\":n(399),__esModule:!0}},function(e,t,n){n(400),n(410),n(411),n(412),e.exports=n(346).Symbol},function(e,t,n){\"use strict\";var r=n(357),o=n(370),i=n(365),a=n(356),s=n(371),l=n(401).KEY,c=n(366),u=n(385),d=n(389),p=n(386),h=n(390),g=n(397),f=n(402),m=n(403),A=n(406),M=n(362),w=n(363),v=n(392),b=n(378),y=n(368),x=n(369),T=n(374),C=n(407),N=n(409),I=n(404),E=n(361),D=n(376),S=N.f,L=E.f,k=C.f,j=r.Symbol,U=r.JSON,B=U&&U.stringify,R=\"prototype\",z=h(\"_hidden\"),Q=h(\"toPrimitive\"),O={}.propertyIsEnumerable,H=u(\"symbol-registry\"),F=u(\"symbols\"),V=u(\"op-symbols\"),P=Object[R],Y=\"function\"==typeof j&&!!I.f,G=r.QObject,W=!G||!G[R]||!G[R].findChild,X=i&&c(function(){return 7!=T(L({},\"a\",{get:function(){return L(this,\"a\",{value:7}).a}})).a})?function(e,t,n){var r=S(P,t);r&&delete P[t],L(e,t,n),r&&e!==P&&L(P,t,r)}:L,_=function(e){var t=F[e]=T(j[R]);return t._k=e,t},q=Y&&\"symbol\"==typeof j.iterator?function(e){return\"symbol\"==typeof e}:function(e){return e instanceof j},J=function(e,t,n){return e===P&&J(V,t,n),M(e),t=y(t,!0),M(n),o(F,t)?(n.enumerable?(o(e,z)&&e[z][t]&&(e[z][t]=!1),n=T(n,{enumerable:x(0,!1)})):(o(e,z)||L(e,z,x(1,{})),e[z][t]=!0),X(e,t,n)):L(e,t,n)},K=function(e,t){M(e);for(var n,r=m(t=b(t)),o=0,i=r.length;i>o;)J(e,n=r[o++],t[n]);return e},Z=function(e,t){return void 0===t?T(e):K(T(e),t)},$=function(e){var t=O.call(this,e=y(e,!0));return this===P&&o(F,e)&&!o(V,e)?!1:t||!o(this,e)||!o(F,e)||o(this,z)&&this[z][e]?t:!0},ee=function(e,t){if(e=b(e),t=y(t,!0),e!==P||!o(F,t)||o(V,t)){var n=S(e,t);return!n||!o(F,t)||o(e,z)&&e[z][t]||(n.enumerable=!0),n}},te=function(e){for(var t,n=k(b(e)),r=[],i=0;n.length>i;)o(F,t=n[i++])||t==z||t==l||r.push(t);return r},ne=function(e){for(var t,n=e===P,r=k(n?V:b(e)),i=[],a=0;r.length>a;)o(F,t=r[a++])&&(n?o(P,t):!0)&&i.push(F[t]);return i};Y||(j=function(){if(this instanceof j)throw TypeError(\"Symbol is not a constructor!\");var e=p(arguments.length>0?arguments[0]:void 0),t=function(n){this===P&&t.call(V,n),o(this,z)&&o(this[z],e)&&(this[z][e]=!1),X(this,e,x(1,n))};return i&&W&&X(P,e,{configurable:!0,set:t}),_(e)},s(j[R],\"toString\",function(){return this._k}),N.f=ee,E.f=J,n(408).f=C.f=te,n(405).f=$,I.f=ne,i&&!n(355)&&s(P,\"propertyIsEnumerable\",$,!0),g.f=function(e){return _(h(e))}),a(a.G+a.W+a.F*!Y,{Symbol:j});for(var re=\"hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables\".split(\",\"),oe=0;re.length>oe;)h(re[oe++]);for(var ie=D(h.store),ae=0;ie.length>ae;)f(ie[ae++]);a(a.S+a.F*!Y,\"Symbol\",{\"for\":function(e){return o(H,e+=\"\")?H[e]:H[e]=j(e)},keyFor:function(e){if(!q(e))throw TypeError(e+\" is not a symbol!\");for(var t in H)if(H[t]===e)return t},useSetter:function(){W=!0},useSimple:function(){W=!1}}),a(a.S+a.F*!Y,\"Object\",{create:Z,defineProperty:J,defineProperties:K,getOwnPropertyDescriptor:ee,getOwnPropertyNames:te,getOwnPropertySymbols:ne});var se=c(function(){I.f(1)});a(a.S+a.F*se,\"Object\",{getOwnPropertySymbols:function(e){return I.f(v(e))}}),U&&a(a.S+a.F*(!Y||c(function(){var e=j();return\"[null]\"!=B([e])||\"{}\"!=B({a:e})||\"{}\"!=B(Object(e))})),\"JSON\",{stringify:function(e){for(var t,n,r=[e],o=1;arguments.length>o;)r.push(arguments[o++]);return n=t=r[1],!w(t)&&void 0===e||q(e)?void 0:(A(t)||(t=function(e,t){return\"function\"==typeof n&&(t=n.call(this,e,t)),q(t)?void 0:t}),r[1]=t,B.apply(U,r))}}),j[R][Q]||n(360)(j[R],Q,j[R].valueOf),d(j,\"Symbol\"),d(Math,\"Math\",!0),d(r.JSON,\"JSON\",!0)},function(e,t,n){var r=n(386)(\"meta\"),o=n(363),i=n(370),a=n(361).f,s=0,l=Object.isExtensible||function(){return!0},c=!n(366)(function(){return l(Object.preventExtensions({}))}),u=function(e){a(e,r,{value:{i:\"O\"+ ++s,w:{}}})},d=function(e,t){if(!o(e))return\"symbol\"==typeof e?e:(\"string\"==typeof e?\"S\":\"P\")+e;if(!i(e,r)){if(!l(e))return\"F\";if(!t)return\"E\";u(e)}return e[r].i},p=function(e,t){if(!i(e,r)){if(!l(e))return!0;if(!t)return!1;u(e)}return e[r].w},h=function(e){return c&&g.NEED&&l(e)&&!i(e,r)&&u(e),e},g=e.exports={KEY:r,NEED:!1,fastKey:d,getWeak:p,onFreeze:h}},function(e,t,n){var r=n(357),o=n(346),i=n(355),a=n(397),s=n(361).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=i?{}:r.Symbol||{});\"_\"==e.charAt(0)||e in t||s(t,e,{value:a.f(e)})}},function(e,t,n){var r=n(376),o=n(404),i=n(405);e.exports=function(e){var t=r(e),n=o.f;if(n)for(var a,s=n(e),l=i.f,c=0;s.length>c;)l.call(e,a=s[c++])&&t.push(a);return t}},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var r=n(380);e.exports=Array.isArray||function(e){return\"Array\"==r(e)}},function(e,t,n){var r=n(378),o=n(408).f,i={}.toString,a=\"object\"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(e){try{return o(e)}catch(t){return a.slice()}};e.exports.f=function(e){return a&&\"[object Window]\"==i.call(e)?s(e):o(r(e))}},function(e,t,n){var r=n(377),o=n(387).concat(\"length\",\"prototype\");t.f=Object.getOwnPropertyNames||function(e){return r(e,o)}},function(e,t,n){var r=n(405),o=n(369),i=n(378),a=n(368),s=n(370),l=n(364),c=Object.getOwnPropertyDescriptor;t.f=n(365)?c:function(e,t){if(e=i(e),t=a(t,!0),l)try{return c(e,t)}catch(n){}return s(e,t)?o(!r.f.call(e,t),e[t]):void 0}},function(e,t){},function(e,t,n){n(402)(\"asyncIterator\")},function(e,t,n){n(402)(\"observable\")},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]=function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(347),i=r(o);t[\"default\"]=function(e,t){if(!e)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!t||\"object\"!==(\"undefined\"==typeof t?\"undefined\":i[\"default\"](t))&&\"function\"!=typeof t?e:t}},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(416),i=r(o),a=n(420),s=r(a),l=n(347),c=r(l);t[\"default\"]=function(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Super expression must either be null or a function, not \"+(\"undefined\"==typeof t?\"undefined\":c[\"default\"](t)));e.prototype=s[\"default\"](t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(i[\"default\"]?i[\"default\"](e,t):e.__proto__=t)}},function(e,t,n){e.exports={\"default\":n(417),__esModule:!0}},function(e,t,n){n(418),e.exports=n(346).Object.setPrototypeOf},function(e,t,n){var r=n(356);r(r.S,\"Object\",{setPrototypeOf:n(419).set})},function(e,t,n){var r=n(363),o=n(362),i=function(e,t){if(o(e),!r(t)&&null!==t)throw TypeError(t+\": can't set as prototype!\")};e.exports={set:Object.setPrototypeOf||(\"__proto__\"in{}?function(e,t,r){try{r=n(358)(Function.call,n(409).f(Object.prototype,\"__proto__\").set,2),r(e,[]),t=!(e instanceof Array)}catch(o){t=!0}return function(e,n){return i(e,n),t?e.__proto__=n:r(e,n),e}}({},!1):void 0),check:i}},function(e,t,n){e.exports={\"default\":n(421),__esModule:!0}},function(e,t,n){n(422);var r=n(346).Object;e.exports=function(e,t){return r.create(e,t)}},function(e,t,n){var r=n(356);r(r.S,\"Object\",{create:n(374)})},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(424),i=r(o);t[\"default\"]=i[\"default\"]||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}},function(e,t,n){e.exports={\"default\":n(425),__esModule:!0}},function(e,t,n){n(426),e.exports=n(346).Object.assign},function(e,t,n){var r=n(356);r(r.S+r.F,\"Object\",{assign:n(427)})},function(e,t,n){\"use strict\";var r=n(365),o=n(376),i=n(404),a=n(405),s=n(392),l=n(379),c=Object.assign;e.exports=!c||n(366)(function(){var e={},t={},n=Symbol(),r=\"abcdefghijklmnopqrst\";return e[n]=7,r.split(\"\").forEach(function(e){t[e]=e}),7!=c({},e)[n]||Object.keys(c({},t)).join(\"\")!=r})?function(e,t){for(var n=s(e),c=arguments.length,u=1,d=i.f,p=a.f;c>u;)for(var h,g=l(arguments[u++]),f=d?o(g).concat(d(g)):o(g),m=f.length,A=0;m>A;)h=f[A++],(!r||p.call(g,h))&&(n[h]=g[h]);return n}:c},function(e,t,n){e.exports={\"default\":n(429),__esModule:!0}},function(e,t,n){n(430),e.exports=n(346).Object.keys},function(e,t,n){var r=n(392),o=n(376);n(431)(\"keys\",function(){return function(e){return o(r(e))}})},function(e,t,n){var r=n(356),o=n(346),i=n(366);e.exports=function(e,t){var n=(o.Object||{})[e]||Object[e],a={};a[e]=t(n),r(r.S+r.F*i(function(){n(1)}),\"Object\",a)}},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(423),i=r(o),a=n(343),s=r(a),l=n(24),c=r(l),u=n(433),d=r(u),p=n(434),h=r(p),g=n(447),f=r(g),m=n(448),A=r(m),M=n(453),w=r(M),v=n(341),b=r(v),y=function(e){var t=e.getItemString,n=e.keyPath,r=e.labelRenderer,o=e.styling,a=e.value,l=e.valueRenderer,u=e.isCustomNode,p=s[\"default\"](e,[\"getItemString\",\"keyPath\",\"labelRenderer\",\"styling\",\"value\",\"valueRenderer\",\"isCustomNode\"]),g=u(a)?\"Custom\":d[\"default\"](a);\"BigNumber\"===g&&(g=\"Number\",a=a.toString());var m={getItemString:t,key:n[0],keyPath:n,labelRenderer:r,nodeType:g,styling:o,value:a,valueRenderer:l},M=i[\"default\"]({},p,m,{data:a,isCustomNode:u});switch(g){case\"Object\":case\"Error\":case\"WeakMap\":case\"WeakSet\":return c[\"default\"].createElement(h[\"default\"],M);case\"Array\":return c[\"default\"].createElement(f[\"default\"],M);case\"Iterable\":case\"Map\":case\"Set\":return c[\"default\"].createElement(A[\"default\"],M);case\"String\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(e){return e='\"'+e+'\"',e.length>1024?c[\"default\"].createElement(b[\"default\"],{text:e}):e}}));case\"Number\":return c[\"default\"].createElement(w[\"default\"],m);case\"Boolean\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(e){return e?\"true\":\"false\"}}));case\"Date\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(e){return e.toISOString()}}));case\"Null\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(){return\"null\"}}));case\"Undefined\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(){return\"undefined\"}}));case\"Function\":case\"Symbol\":return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(e){return e.toString()}}));case\"Custom\":return c[\"default\"].createElement(w[\"default\"],m);default:return c[\"default\"].createElement(w[\"default\"],i[\"default\"]({},m,{valueGetter:function(e){return\"<\"+g+\">\"}}))}};t[\"default\"]=y},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e){var t=Object.prototype.toString.call(e).slice(8,-1);return\"Object\"===t&&\"function\"==typeof e[a[\"default\"]]?\"Iterable\":\"String\"===t&&e._$isNumber?\"BigNumber\":\"Custom\"===t&&e.constructor!==Object&&e instanceof Object?\"Object\":t}t.__esModule=!0;var i=n(348),a=r(i);t[\"default\"]=o},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e){var t=u[\"default\"](e).length;return t+\" \"+(1!==t?\"keys\":\"key\")}t.__esModule=!0;var i=n(423),a=r(i),s=n(343),l=r(s),c=n(435),u=r(c),d=n(24),p=r(d),h=n(438),g=r(h),f=function(e){var t=e.data,n=l[\"default\"](e,[\"data\"]);return p[\"default\"].createElement(g[\"default\"],a[\"default\"]({},n,{data:t,nodeType:\"Object\",nodeTypeIndicator:\"Error\"===n.nodeType?\"Error()\":\"{}\",createItemString:o,expandable:u[\"default\"](t).length>0}))};t[\"default\"]=f},function(e,t,n){e.exports={\"default\":n(436),__esModule:!0}},function(e,t,n){n(437);var r=n(346).Object;e.exports=function(e){return r.getOwnPropertyNames(e)}},function(e,t,n){n(431)(\"getOwnPropertyNames\",function(){return n(407).f})},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e,t,n){var r=e.nodeType,i=e.data,a=e.collectionLimit,s=e.circularCache,l=e.keyPath,c=e.postprocessValue,u=e.sortObjectKeys,d=[];return x[\"default\"](r,i,u,a,t,n).forEach(function(t){if(t.to)d.push(w[\"default\"].createElement(I[\"default\"],A[\"default\"]({},e,{key:\"ItemRange--\"+t.from+\"-\"+t.to,from:t.from,to:t.to,renderChildNodes:o})));else{var n=t.key,r=t.value,i=-1!==s.indexOf(r),u=w[\"default\"].createElement(C[\"default\"],A[\"default\"]({},e,{postprocessValue:c,collectionLimit:a},{key:\"Node--\"+n,keyPath:[n].concat(l),value:c(r),circularCache:[].concat(s,[r]),isCircular:i,hideRoot:!1}));u!==!1&&d.push(u)}}),d}function i(e){var t=e.shouldExpandNode&&!e.isCircular?e.shouldExpandNode(e.keyPath,e.data,e.level):!1;return{expanded:t}}t.__esModule=!0;var a=n(344),s=r(a),l=n(428),c=r(l),u=n(413),d=r(u),p=n(414),h=r(p),g=n(415),f=r(g),m=n(423),A=r(m),M=n(24),w=r(M),v=n(439),b=r(v),y=n(440),x=r(y),T=n(432),C=r(T),N=n(446),I=r(N),E=function(e){function t(n){d[\"default\"](this,t);var r=h[\"default\"](this,e.call(this,n));return r.handleClick=function(){r.props.expandable&&r.setState({expanded:!r.state.expanded})},r.state=i(n),r}return f[\"default\"](t,e),t.prototype.componentWillReceiveProps=function(e){var t=i(e);i(this.props).expanded!==t.expanded&&this.setState(t)},t.prototype.shouldComponentUpdate=function(e,t){var n=this;return!!c[\"default\"](e).find(function(t){return\"circularCache\"!==t&&(\"keyPath\"===t?e[t].join(\"/\")!==n.props[t].join(\"/\"):e[t]!==n.props[t])})||t.expanded!==this.state.expanded},t.prototype.render=function(){var e=this.props,t=e.getItemString,n=e.nodeTypeIndicator,r=e.nodeType,i=e.data,a=e.hideRoot,l=e.createItemString,c=e.styling,u=e.collectionLimit,d=e.keyPath,p=e.labelRenderer,h=e.expandable,g=this.state.expanded,f=g||a&&0===this.props.level?o(A[\"default\"]({},this.props,{level:this.props.level+1})):null,m=w[\"default\"].createElement(\"span\",c(\"nestedNodeItemType\",g),n),M=t(r,i,m,l(i,u)),v=[d,r,g,h];return a?w[\"default\"].createElement(\"li\",c.apply(void 0,[\"rootNode\"].concat(v)),w[\"default\"].createElement(\"ul\",c.apply(void 0,[\"rootNodeChildren\"].concat(v)),f)):w[\"default\"].createElement(\"li\",c.apply(void 0,[\"nestedNode\"].concat(v)),h&&w[\"default\"].createElement(b[\"default\"],{styling:c,nodeType:r,expanded:g,onClick:this.handleClick}),w[\"default\"].createElement(\"label\",A[\"default\"]({},c.apply(void 0,[[\"label\",\"nestedNodeLabel\"]].concat(v)),{onClick:this.handleClick,\"data-key-path\":s[\"default\"](d)}),p.apply(void 0,v)),w[\"default\"].createElement(\"span\",A[\"default\"]({},c.apply(void 0,[\"nestedNodeItemString\"].concat(v)),{onClick:this.handleClick}),M),w[\"default\"].createElement(\"ul\",c.apply(void 0,[\"nestedNodeChildren\"].concat(v)),f))},t}(w[\"default\"].Component);E.defaultProps={data:[],circularCache:[],level:0,expandable:!0},t[\"default\"]=E},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(423),i=r(o),a=n(24),s=r(a),l=function(e){var t=e.styling,n=e.arrowStyle,r=e.expanded,o=e.nodeType,a=e.onClick;return s[\"default\"].createElement(\"div\",i[\"default\"]({},t(\"arrowContainer\",n),{onClick:a}),s[\"default\"].createElement(\"div\",t([\"arrow\",\"arrowSign\"],o,r,n),\"▶\",\"double\"===n&&s[\"default\"].createElement(\"div\",t([\"arrowSign\",\"arrowSignInner\"]),\"▶\")))};l.defaultProps={arrowStyle:\"single\"},t[\"default\"]=l},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e,t){return\"Object\"===e?g[\"default\"](t).length:\"Array\"===e?t.length:1/0}function i(e){return\"function\"==typeof e.set}function a(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:1/0,a=void 0;if(\"Object\"===e){var s=p[\"default\"](t);n&&s.sort(n===!0?void 0:n),s=s.slice(r,o+1),a={entries:s.map(function(e){return{key:e,value:t[e]}})}}else if(\"Array\"===e){var l=t._idx;a={entries:t.slice(r,o+1).map(function(e,t){t+=r;var n=l&&l[t];return{key:n>=0?n:t,value:e}})}}else{for(var c=0,d=[],h=!0,g=i(t),f=t,m=Array.isArray(f),A=0,f=m?f:u[\"default\"](f);;){\nvar M;if(m){if(A>=f.length)break;M=f[A++]}else{if(A=f.next(),A.done)break;M=A.value}var w=M;if(c>o){h=!1;break}c>=r&&(g&&Array.isArray(w)?\"string\"==typeof w[0]||\"number\"==typeof w[0]?d.push({key:w[0],value:w[1]}):d.push({key:\"[entry \"+c+\"]\",value:{\"[key]\":w[0],\"[value]\":w[1]}}):d.push({key:c,value:w})),c++}a={hasMore:!h,entries:d}}return a}function s(e,t,n){for(var r=[];t-e>n*n;)n*=n;for(var o=e;t>=o;o+=n)r.push({from:o,to:Math.min(t,o+n-1)});return r}function l(e,t,n,r){var i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,l=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1/0,c=a.bind(null,e,t,n);if(!r)return c().entries;var u=1/0>l,d=Math.min(l-i,o(e,t));if(\"Iterable\"!==e){if(r>=d||7>r)return c(i,l).entries}else if(r>=d&&!u)return c(i,l).entries;var p=void 0;if(\"Iterable\"===e){var h=c(i,i+r-1),g=h.hasMore,f=h.entries;p=g?[].concat(f,s(i+r,i+2*r-1,r)):f}else p=u?s(i,l,r):[].concat(c(0,r-5).entries,s(r-4,d-5,r),c(d-4,d-1).entries);return p}t.__esModule=!0;var c=n(441),u=r(c),d=n(435),p=r(d),h=n(428),g=r(h);t[\"default\"]=l},function(e,t,n){e.exports={\"default\":n(442),__esModule:!0}},function(e,t,n){n(393),n(350),e.exports=n(443)},function(e,t,n){var r=n(362),o=n(444);e.exports=n(346).getIterator=function(e){var t=o(e);if(\"function\"!=typeof t)throw TypeError(e+\" is not iterable!\");return r(t.call(e))}},function(e,t,n){var r=n(445),o=n(390)(\"iterator\"),i=n(372);e.exports=n(346).getIteratorMethod=function(e){return void 0!=e?e[o]||e[\"@@iterator\"]||i[r(e)]:void 0}},function(e,t,n){var r=n(380),o=n(390)(\"toStringTag\"),i=\"Arguments\"==r(function(){return arguments}()),a=function(e,t){try{return e[t]}catch(n){}};e.exports=function(e){var t,n,s;return void 0===e?\"Undefined\":null===e?\"Null\":\"string\"==typeof(n=a(t=Object(e),o))?n:i?r(t):\"Object\"==(s=r(t))&&\"function\"==typeof t.callee?\"Arguments\":s}},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(423),i=r(o),a=n(413),s=r(a),l=n(414),c=r(l),u=n(415),d=r(u),p=n(24),h=r(p),g=n(439),f=r(g),m=function(e){function t(n){s[\"default\"](this,t);var r=c[\"default\"](this,e.call(this,n));return r.state={expanded:!1},r.handleClick=r.handleClick.bind(r),r}return d[\"default\"](t,e),t.prototype.render=function(){var e=this.props,t=e.styling,n=e.from,r=e.to,o=e.renderChildNodes,a=e.nodeType;return this.state.expanded?h[\"default\"].createElement(\"div\",t(\"itemRange\",this.state.expanded),o(this.props,n,r)):h[\"default\"].createElement(\"div\",i[\"default\"]({},t(\"itemRange\",this.state.expanded),{onClick:this.handleClick}),h[\"default\"].createElement(f[\"default\"],{nodeType:a,styling:t,expanded:!1,onClick:this.handleClick,arrowStyle:\"double\"}),n+\" ... \"+r)},t.prototype.handleClick=function(){this.setState({expanded:!this.state.expanded})},t}(h[\"default\"].Component);t[\"default\"]=m},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e){return e.length+\" \"+(1!==e.length?\"items\":\"item\")}t.__esModule=!0;var i=n(423),a=r(i),s=n(343),l=r(s),c=n(24),u=r(c),d=n(438),p=r(d),h=function(e){var t=e.data,n=l[\"default\"](e,[\"data\"]);return u[\"default\"].createElement(p[\"default\"],a[\"default\"]({},n,{data:t,nodeType:\"Array\",nodeTypeIndicator:\"[]\",createItemString:o,expandable:t.length>0}))};t[\"default\"]=h},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}function o(e,t){var n=0,r=!1;if(h[\"default\"](e.size))n=e.size;else for(var o=e,i=Array.isArray(o),a=0,o=i?o:d[\"default\"](o);;){var s;if(i){if(a>=o.length)break;s=o[a++]}else{if(a=o.next(),a.done)break;s=a.value}if(t&&n+1>t){r=!0;break}n+=1}return\"\"+(r?\">\":\"\")+n+\" \"+(1!==n?\"entries\":\"entry\")}function i(e){var t=c[\"default\"](e,[]);return f[\"default\"].createElement(A[\"default\"],s[\"default\"]({},t,{nodeType:\"Iterable\",nodeTypeIndicator:\"()\",createItemString:o}))}t.__esModule=!0;var a=n(423),s=r(a),l=n(343),c=r(l),u=n(441),d=r(u),p=n(449),h=r(p);t[\"default\"]=i;var g=n(24),f=r(g),m=n(438),A=r(m)},function(e,t,n){e.exports={\"default\":n(450),__esModule:!0}},function(e,t,n){n(451),e.exports=n(346).Number.isSafeInteger},function(e,t,n){var r=n(356),o=n(452),i=Math.abs;r(r.S,\"Number\",{isSafeInteger:function(e){return o(e)&&i(e)<=9007199254740991}})},function(e,t,n){var r=n(363),o=Math.floor;e.exports=function(e){return!r(e)&&isFinite(e)&&o(e)===e}},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(423),i=r(o),a=n(344),s=r(a),l=n(24),c=r(l),u=function(e){var t=e.nodeType,n=e.styling,r=e.labelRenderer,o=e.keyPath,a=e.valueRenderer,l=e.value,u=e.valueGetter;return c[\"default\"].createElement(\"li\",n(\"value\",t,o),c[\"default\"].createElement(\"label\",i[\"default\"]({},n([\"label\",\"valueLabel\"],t,o),{\"data-key-path\":s[\"default\"](o)}),r(o,t,!1,!1)),c[\"default\"].createElement(\"span\",n(\"valueText\",t,o),a.apply(void 0,[u(l),l].concat(o))))};u.defaultProps={valueGetter:function(e){return e}},t[\"default\"]=u},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(423),i=r(o),a=n(455),s=n(509),l=r(s),c=function(e){return{BACKGROUND_COLOR:e.base00,TEXT_COLOR:e.base07,STRING_COLOR:e.base0B,DATE_COLOR:e.base0B,NUMBER_COLOR:e.base09,BOOLEAN_COLOR:e.base09,NULL_COLOR:e.base08,UNDEFINED_COLOR:e.base08,FUNCTION_COLOR:e.base08,SYMBOL_COLOR:e.base08,LABEL_COLOR:e.base0D,ARROW_COLOR:e.base0D,ITEM_STRING_COLOR:e.base0B,ITEM_STRING_EXPANDED_COLOR:e.base03}},u=function(e){return{String:e.STRING_COLOR,Date:e.DATE_COLOR,Number:e.NUMBER_COLOR,Boolean:e.BOOLEAN_COLOR,Null:e.NULL_COLOR,Undefined:e.UNDEFINED_COLOR,Function:e.FUNCTION_COLOR,Symbol:e.SYMBOL_COLOR}},d=function(e){var t=c(e);return{tree:{border:0,padding:0,marginTop:\"0.5em\",marginBottom:\"0.5em\",marginLeft:\"0.125em\",marginRight:0,listStyle:\"none\",MozUserSelect:\"none\",WebkitUserSelect:\"none\",backgroundColor:t.BACKGROUND_COLOR},value:function(e,t,n){var r=e.style;return{style:i[\"default\"]({},r,{paddingTop:\"0.25em\",paddingRight:0,marginLeft:\"0.875em\",WebkitUserSelect:\"text\",MozUserSelect:\"text\",wordWrap:\"break-word\",paddingLeft:n.length>1?\"2.125em\":\"1.25em\",textIndent:\"-0.5em\",wordBreak:\"break-all\"})}},label:{display:\"inline-block\",color:t.LABEL_COLOR},valueLabel:{margin:\"0 0.5em 0 0\"},valueText:function(e,n){var r=e.style;return{style:i[\"default\"]({},r,{color:u(t)[n]})}},itemRange:function(e,n){return{style:{paddingTop:n?0:\"0.25em\",cursor:\"pointer\",color:t.LABEL_COLOR}}},arrow:function(e,t,n){var r=e.style;return{style:i[\"default\"]({},r,{marginLeft:0,transition:\"150ms\",WebkitTransition:\"150ms\",MozTransition:\"150ms\",WebkitTransform:n?\"rotateZ(90deg)\":\"rotateZ(0deg)\",MozTransform:n?\"rotateZ(90deg)\":\"rotateZ(0deg)\",transform:n?\"rotateZ(90deg)\":\"rotateZ(0deg)\",transformOrigin:\"45% 50%\",WebkitTransformOrigin:\"45% 50%\",MozTransformOrigin:\"45% 50%\",position:\"relative\",lineHeight:\"1.1em\",fontSize:\"0.75em\"})}},arrowContainer:function(e,t){var n=e.style;return{style:i[\"default\"]({},n,{display:\"inline-block\",paddingRight:\"0.5em\",paddingLeft:\"double\"===t?\"1em\":0,cursor:\"pointer\"})}},arrowSign:{color:t.ARROW_COLOR},arrowSignInner:{position:\"absolute\",top:0,left:\"-0.4em\"},nestedNode:function(e,t,n,r,o){var a=e.style;return{style:i[\"default\"]({},a,{position:\"relative\",paddingTop:\"0.25em\",marginLeft:t.length>1?\"0.875em\":0,paddingLeft:o?0:\"1.125em\"})}},rootNode:{padding:0,margin:0},nestedNodeLabel:function(e,t,n,r,o){var a=e.style;return{style:i[\"default\"]({},a,{margin:0,padding:0,WebkitUserSelect:o?\"inherit\":\"text\",MozUserSelect:o?\"inherit\":\"text\",cursor:o?\"pointer\":\"default\"})}},nestedNodeItemString:function(e,n,r,o){var a=e.style;return{style:i[\"default\"]({},a,{paddingLeft:\"0.5em\",cursor:\"default\",color:o?t.ITEM_STRING_EXPANDED_COLOR:t.ITEM_STRING_COLOR})}},nestedNodeItemType:{marginLeft:\"0.3em\",marginRight:\"0.3em\"},nestedNodeChildren:function(e,t,n){var r=e.style;return{style:i[\"default\"]({},r,{padding:0,margin:0,listStyle:\"none\",display:n?\"block\":\"none\"})}},rootNodeChildren:{padding:0,margin:0,listStyle:\"none\"}}};t[\"default\"]=a.createStyling(d,{defaultBase16:l[\"default\"]})},function(e,t,n){\"use strict\";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t[\"default\"]=e,t}function o(e){return e&&e.__esModule?e:{\"default\":e}}Object.defineProperty(t,\"__esModule\",{value:!0}),t.getBase16Theme=t.createStyling=t.invertTheme=void 0;var i=n(347),a=o(i),s=n(423),l=o(s),c=n(456),u=o(c),d=n(428),p=o(d),h=n(460),g=o(h),f=n(461),m=r(f),A=n(499),M=o(A),w=n(501),v=o(w),b=n(507),y=o(b),x=n(508),T=m[\"default\"],C=p[\"default\"](T),N=function(e){return.25>e?1:.5>e?.9-e:1.1-e},I=y[\"default\"](v[\"default\"],x.rgb2yuv,function(e){var t=u[\"default\"](e,3),n=t[0],r=t[1],o=t[2];return[N(n),r,o]},x.yuv2rgb,M[\"default\"]),E=function(e){return function(t){return{className:[t.className,e.className].filter(Boolean).join(\" \"),style:l[\"default\"]({},t.style||{},e.style||{})}}},D=function(e,t){if(void 0===e)return t;if(void 0===t)return e;var n=\"undefined\"==typeof e?\"undefined\":a[\"default\"](e),r=\"undefined\"==typeof t?\"undefined\":a[\"default\"](t);switch(n){case\"string\":switch(r){case\"string\":return[t,e].filter(Boolean).join(\" \");case\"object\":return E({className:e,style:t});case\"function\":return function(n){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return E({className:e})(t.apply(void 0,[n].concat(o)))}}case\"object\":switch(r){case\"string\":return E({className:t,style:e});case\"object\":return l[\"default\"]({},t,e);case\"function\":return function(n){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return E({style:e})(t.apply(void 0,[n].concat(o)))}}case\"function\":switch(r){case\"string\":return function(n){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return e.apply(void 0,[E(n)({className:t})].concat(o))};case\"object\":return function(n){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return e.apply(void 0,[E(n)({style:t})].concat(o))};case\"function\":return function(n){for(var r=arguments.length,o=Array(r>1?r-1:0),i=1;r>i;i++)o[i-1]=arguments[i];return e.apply(void 0,[t.apply(void 0,[n].concat(o))].concat(o))}}}},S=function(e,t){var n=p[\"default\"](t);for(var r in e)-1===n.indexOf(r)&&n.push(r);return n.reduce(function(n,r){return n[r]=D(e[r],t[r]),n},{})},L=function(e,t){for(var n=arguments.length,r=Array(n>2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o];if(null===t)return e;Array.isArray(t)||(t=[t]);var i=t.map(function(t){return e[t]}).filter(Boolean),s=i.reduce(function(e,t){return\"string\"==typeof t?e.className=[e.className,t].filter(Boolean).join(\" \"):\"object\"===(\"undefined\"==typeof t?\"undefined\":a[\"default\"](t))?e.style=l[\"default\"]({},e.style,t):\"function\"==typeof t&&(e=l[\"default\"]({},e,t.apply(void 0,[e].concat(r)))),e},{className:\"\",style:{}});return s.className||delete s.className,0===p[\"default\"](s.style).length&&delete s.style,s},k=t.invertTheme=function(e){return p[\"default\"](e).reduce(function(t,n){return t[n]=/^base/.test(n)?I(e[n]):\"scheme\"===n?e[n]+\":inverted\":e[n],t},{})},j=(t.createStyling=g[\"default\"](function(e){for(var t=arguments.length,n=Array(t>3?t-3:0),r=3;t>r;r++)n[r-3]=arguments[r];var o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},a=o.defaultBase16,s=void 0===a?T:a,c=o.base16Themes,u=void 0===c?null:c,d=j(i,u);d&&(i=l[\"default\"]({},d,i));var h=C.reduce(function(e,t){return e[t]=i[t]||s[t],e},{}),f=p[\"default\"](i).reduce(function(e,t){return-1===C.indexOf(t)?(e[t]=i[t],e):e},{}),m=e(h),A=S(f,m);return g[\"default\"](L,2).apply(void 0,[A].concat(n))},3),t.getBase16Theme=function(e,t){if(e&&e.extend&&(e=e.extend),\"string\"==typeof e){var n=e.split(\":\"),r=u[\"default\"](n,2),o=r[0],i=r[1];e=(t||{})[o]||m[o],\"inverted\"===i&&(e=k(e))}return e&&e.hasOwnProperty(\"base00\")?e:void 0})},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e:{\"default\":e}}t.__esModule=!0;var o=n(457),i=r(o),a=n(441),s=r(a);t[\"default\"]=function(){function e(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,l=s[\"default\"](e);!(r=(a=l.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(c){o=!0,i=c}finally{try{!r&&l[\"return\"]&&l[\"return\"]()}finally{if(o)throw i}}return n}return function(t,n){if(Array.isArray(t))return t;if(i[\"default\"](Object(t)))return e(t,n);throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}}()},function(e,t,n){e.exports={\"default\":n(458),__esModule:!0}},function(e,t,n){n(393),n(350),e.exports=n(459)},function(e,t,n){var r=n(445),o=n(390)(\"iterator\"),i=n(372);e.exports=n(346).isIterable=function(e){var t=Object(e);return void 0!==t[o]||\"@@iterator\"in t||i.hasOwnProperty(r(t))}},function(e,t){(function(t){function n(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function r(e,t){for(var n=-1,r=e?e.length:0;++n<r&&t(e[n],n,e)!==!1;);return e}function o(e,t){var n=e?e.length:0;return!!n&&a(e,t,0)>-1}function i(e,t,n,r){for(var o=e.length,i=n+(r?1:-1);r?i--:++i<o;)if(t(e[i],i,e))return i;return-1}function a(e,t,n){if(t!==t)return i(e,s,n);for(var r=n-1,o=e.length;++r<o;)if(e[r]===t)return r;return-1}function s(e){return e!==e}function l(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&r++;return r}function c(e,t){return null==e?void 0:e[t]}function u(e){var t=!1;if(null!=e&&\"function\"!=typeof e.toString)try{t=!!(e+\"\")}catch(n){}return t}function d(e,t){for(var n=-1,r=e.length,o=0,i=[];++n<r;){var a=e[n];(a===t||a===Y)&&(e[n]=Y,i[o++]=n)}return i}function p(e){return B(e)?Le(e):{}}function h(e){if(!B(e)||D(e))return!1;var t=U(e)||u(e)?Se:me;return t.test(L(e))}function g(e,t,n,r){for(var o=-1,i=e.length,a=n.length,s=-1,l=t.length,c=ke(i-a,0),u=Array(l+c),d=!r;++s<l;)u[s]=t[s];for(;++o<a;)(d||i>o)&&(u[n[o]]=e[o]);for(;c--;)u[s++]=e[o++];return u}function f(e,t,n,r){for(var o=-1,i=e.length,a=-1,s=n.length,l=-1,c=t.length,u=ke(i-s,0),d=Array(u+c),p=!r;++o<u;)d[o]=e[o];for(var h=o;++l<c;)d[h+l]=t[l];for(;++a<s;)(p||i>o)&&(d[h+n[a]]=e[o++]);return d}function m(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n<r;)t[n]=e[n];return t}function A(e,t,n){function r(){var t=this&&this!==ye&&this instanceof r?i:e;return t.apply(o?n:this,arguments)}var o=t&G,i=M(e);return r}function M(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=p(e.prototype),r=e.apply(n,t);return B(r)?r:n}}function w(e,t,r){function o(){for(var a=arguments.length,s=Array(a),l=a,c=T(o);l--;)s[l]=arguments[l];var u=3>a&&s[0]!==c&&s[a-1]!==c?[]:d(s,c);if(a-=u.length,r>a)return y(e,t,v,o.placeholder,void 0,s,u,void 0,void 0,r-a);var p=this&&this!==ye&&this instanceof o?i:e;return n(p,this,s)}var i=M(e);return o}function v(e,t,n,r,o,i,a,s,c,u){function p(){for(var C=arguments.length,N=Array(C),I=C;I--;)N[I]=arguments[I];if(w)var E=T(p),D=l(N,E);if(r&&(N=g(N,r,o,w)),i&&(N=f(N,i,a,w)),C-=D,w&&u>C){var L=d(N,E);return y(e,t,v,p.placeholder,n,N,L,s,c,u-C)}var k=m?n:this,j=A?k[e]:e;return C=N.length,s?N=S(N,s):b&&C>1&&N.reverse(),h&&C>c&&(N.length=c),this&&this!==ye&&this instanceof p&&(j=x||M(j)),j.apply(k,N)}var h=t&Z,m=t&G,A=t&W,w=t&(_|q),b=t&ee,x=A?void 0:M(e);return p}function b(e,t,r,o){function i(){for(var t=-1,l=arguments.length,c=-1,u=o.length,d=Array(u+l),p=this&&this!==ye&&this instanceof i?s:e;++c<u;)d[c]=o[c];for(;l--;)d[c++]=arguments[++t];return n(p,a?r:this,d)}var a=t&G,s=M(e);return i}function y(e,t,n,r,o,i,a,s,l,c){var u=t&_,d=u?a:void 0,p=u?void 0:a,h=u?i:void 0,g=u?void 0:i;t|=u?J:K,t&=~(u?K:J),t&X||(t&=~(G|W));var f=n(e,t,o,h,d,g,p,s,l,c);return f.placeholder=r,Be(f,e,t)}function x(e,t,n,r,o,i,a,s){var l=t&W;if(!l&&\"function\"!=typeof e)throw new TypeError(P);var c=r?r.length:0;if(c||(t&=~(J|K),r=o=void 0),a=void 0===a?a:ke(O(a),0),s=void 0===s?s:O(s),c-=o?o.length:0,t&K){var u=r,d=o;r=o=void 0}var p=[e,t,n,r,o,u,d,i,a,s];if(e=p[0],t=p[1],n=p[2],r=p[3],o=p[4],s=p[9]=null==p[9]?l?0:e.length:ke(p[9]-c,0),!s&&t&(_|q)&&(t&=~(_|q)),t&&t!=G)h=t==_||t==q?w(e,t,s):t!=J&&t!=(G|J)||o.length?v.apply(void 0,p):b(e,t,n,r);else var h=A(e,t,n);return Be(h,e,t)}function T(e){var t=e;return t.placeholder}function C(e,t){var n=c(e,t);return h(n)?n:void 0}function N(e){var t=e.match(pe);return t?t[1].split(he):[]}function I(e,t){var n=t.length,r=n-1;return t[r]=(n>1?\"& \":\"\")+t[r],t=t.join(n>2?\", \":\" \"),e.replace(de,\"{\\n/* [wrapped with \"+t+\"] */\\n\")}function E(e,t){return t=null==t?ne:t,!!t&&(\"number\"==typeof e||Me.test(e))&&e>-1&&e%1==0&&t>e}function D(e){return!!Ne&&Ne in e}function S(e,t){for(var n=e.length,r=je(t.length,n),o=m(e);r--;){var i=t[r];e[r]=E(i,n)?o[i]:void 0}return e}function L(e){if(null!=e){try{return Ie.call(e)}catch(t){}try{return e+\"\"}catch(t){}}return\"\"}function k(e,t){return r(ie,function(n){var r=\"_.\"+n[0];t&n[1]&&!o(e,r)&&e.push(r)}),e.sort()}function j(e,t,n){t=n?void 0:t;var r=x(e,_,void 0,void 0,void 0,void 0,void 0,t);return r.placeholder=j.placeholder,r}function U(e){var t=B(e)?De.call(e):\"\";return t==ae||t==se}function B(e){var t=typeof e;return!!e&&(\"object\"==t||\"function\"==t)}function R(e){return!!e&&\"object\"==typeof e}function z(e){return\"symbol\"==typeof e||R(e)&&De.call(e)==le}function Q(e){if(!e)return 0===e?e:0;if(e=H(e),e===te||e===-te){var t=0>e?-1:1;return t*re}return e===e?e:0}function O(e){var t=Q(e),n=t%1;return t===t?n?t-n:t:0}function H(e){if(\"number\"==typeof e)return e;if(z(e))return oe;if(B(e)){var t=\"function\"==typeof e.valueOf?e.valueOf():e;e=B(t)?t+\"\":t}if(\"string\"!=typeof e)return 0===e?e:+e;e=e.replace(ue,\"\");var n=fe.test(e);return n||Ae.test(e)?we(e.slice(2),n?2:8):ge.test(e)?oe:+e}function F(e){return function(){return e}}function V(e){return e}var P=\"Expected a function\",Y=\"__lodash_placeholder__\",G=1,W=2,X=4,_=8,q=16,J=32,K=64,Z=128,$=256,ee=512,te=1/0,ne=9007199254740991,re=1.7976931348623157e308,oe=NaN,ie=[[\"ary\",Z],[\"bind\",G],[\"bindKey\",W],[\"curry\",_],[\"curryRight\",q],[\"flip\",ee],[\"partial\",J],[\"partialRight\",K],[\"rearg\",$]],ae=\"[object Function]\",se=\"[object GeneratorFunction]\",le=\"[object Symbol]\",ce=/[\\\\^$.*+?()[\\]{}|]/g,ue=/^\\s+|\\s+$/g,de=/\\{(?:\\n\\/\\* \\[wrapped with .+\\] \\*\\/)?\\n?/,pe=/\\{\\n\\/\\* \\[wrapped with (.+)\\] \\*/,he=/,? & /,ge=/^[-+]0x[0-9a-f]+$/i,fe=/^0b[01]+$/i,me=/^\\[object .+?Constructor\\]$/,Ae=/^0o[0-7]+$/i,Me=/^(?:0|[1-9]\\d*)$/,we=parseInt,ve=\"object\"==typeof t&&t&&t.Object===Object&&t,be=\"object\"==typeof self&&self&&self.Object===Object&&self,ye=ve||be||Function(\"return this\")(),xe=Function.prototype,Te=Object.prototype,Ce=ye[\"__core-js_shared__\"],Ne=function(){var e=/[^.]+$/.exec(Ce&&Ce.keys&&Ce.keys.IE_PROTO||\"\");return e?\"Symbol(src)_1.\"+e:\"\"}(),Ie=xe.toString,Ee=Te.hasOwnProperty,De=Te.toString,Se=RegExp(\"^\"+Ie.call(Ee).replace(ce,\"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g,\"$1.*?\")+\"$\"),Le=Object.create,ke=Math.max,je=Math.min,Ue=function(){var e=C(Object,\"defineProperty\"),t=C.name;return t&&t.length>2?e:void 0}(),Be=Ue?function(e,t,n){var r=t+\"\";return Ue(e,\"toString\",{configurable:!0,enumerable:!1,value:F(I(r,k(N(r),n)))})}:V;j.placeholder={},e.exports=j}).call(t,function(){return this}())},function(e,t,n){\"use strict\";function r(e){return e&&e.__esModule?e[\"default\"]:e}t.__esModule=!0;var o=n(462);t.threezerotwofour=r(o);var i=n(463);t.apathy=r(i);var a=n(464);t.ashes=r(a);var s=n(465);t.atelierDune=r(s);var l=n(466);t.atelierForest=r(l);var c=n(467);t.atelierHeath=r(c);var u=n(468);t.atelierLakeside=r(u);var d=n(469);t.atelierSeaside=r(d);var p=n(470);t.bespin=r(p);var h=n(471);t.brewer=r(h);var g=n(472);t.bright=r(g);var f=n(473);t.chalk=r(f);var m=n(474);t.codeschool=r(m);var A=n(475);t.colors=r(A);var M=n(476);t[\"default\"]=r(M);var w=n(477);t.eighties=r(w);var v=n(478);t.embers=r(v);var b=n(479);t.flat=r(b);var y=n(480);t.google=r(y);var x=n(481);t.grayscale=r(x);var T=n(482);t.greenscreen=r(T);var C=n(483);t.harmonic=r(C);var N=n(484);t.hopscotch=r(N);var I=n(485);t.isotope=r(I);var E=n(486);t.marrakesh=r(E);var D=n(487);t.mocha=r(D);var S=n(488);t.monokai=r(S);var L=n(489);t.ocean=r(L);var k=n(490);t.paraiso=r(k);var j=n(491);t.pop=r(j);var U=n(492);t.railscasts=r(U);var B=n(493);t.shapeshifter=r(B);var R=n(494);t.solarized=r(R);var z=n(495);t.summerfruit=r(z);var Q=n(496);t.tomorrow=r(Q);var O=n(497);t.tube=r(O);var H=n(498);t.twilight=r(H)},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"threezerotwofour\",author:\"jan t. sott (http://github.com/idleberg)\",base00:\"#090300\",base01:\"#3a3432\",base02:\"#4a4543\",base03:\"#5c5855\",base04:\"#807d7c\",base05:\"#a5a2a2\",base06:\"#d6d5d4\",base07:\"#f7f7f7\",base08:\"#db2d20\",base09:\"#e8bbd0\",base0A:\"#fded02\",base0B:\"#01a252\",base0C:\"#b5e4f4\",base0D:\"#01a0e4\",base0E:\"#a16a94\",base0F:\"#cdab53\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"apathy\",author:\"jannik siebert (https://github.com/janniks)\",base00:\"#031A16\",base01:\"#0B342D\",base02:\"#184E45\",base03:\"#2B685E\",base04:\"#5F9C92\",base05:\"#81B5AC\",base06:\"#A7CEC8\",base07:\"#D2E7E4\",base08:\"#3E9688\",base09:\"#3E7996\",base0A:\"#3E4C96\",base0B:\"#883E96\",base0C:\"#963E4C\",base0D:\"#96883E\",base0E:\"#4C963E\",base0F:\"#3E965B\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"ashes\",author:\"jannik siebert (https://github.com/janniks)\",base00:\"#1C2023\",base01:\"#393F45\",base02:\"#565E65\",base03:\"#747C84\",base04:\"#ADB3BA\",base05:\"#C7CCD1\",base06:\"#DFE2E5\",base07:\"#F3F4F5\",base08:\"#C7AE95\",base09:\"#C7C795\",base0A:\"#AEC795\",base0B:\"#95C7AE\",base0C:\"#95AEC7\",base0D:\"#AE95C7\",base0E:\"#C795AE\",base0F:\"#C79595\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"atelier dune\",author:\"bram de haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)\",base00:\"#20201d\",base01:\"#292824\",base02:\"#6e6b5e\",base03:\"#7d7a68\",base04:\"#999580\",base05:\"#a6a28c\",base06:\"#e8e4cf\",base07:\"#fefbec\",base08:\"#d73737\",base09:\"#b65611\",base0A:\"#cfb017\",base0B:\"#60ac39\",base0C:\"#1fad83\",base0D:\"#6684e1\",base0E:\"#b854d4\",base0F:\"#d43552\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"atelier forest\",author:\"bram de haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest)\",base00:\"#1b1918\",base01:\"#2c2421\",base02:\"#68615e\",base03:\"#766e6b\",base04:\"#9c9491\",base05:\"#a8a19f\",base06:\"#e6e2e0\",base07:\"#f1efee\",base08:\"#f22c40\",base09:\"#df5320\",base0A:\"#d5911a\",base0B:\"#5ab738\",base0C:\"#00ad9c\",base0D:\"#407ee7\",base0E:\"#6666ea\",base0F:\"#c33ff3\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"atelier heath\",author:\"bram de haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath)\",base00:\"#1b181b\",base01:\"#292329\",base02:\"#695d69\",base03:\"#776977\",base04:\"#9e8f9e\",base05:\"#ab9bab\",base06:\"#d8cad8\",base07:\"#f7f3f7\",base08:\"#ca402b\",base09:\"#a65926\",base0A:\"#bb8a35\",base0B:\"#379a37\",base0C:\"#159393\",base0D:\"#516aec\",base0E:\"#7b59c0\",base0F:\"#cc33cc\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"atelier lakeside\",author:\"bram de haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/)\",base00:\"#161b1d\",base01:\"#1f292e\",base02:\"#516d7b\",base03:\"#5a7b8c\",base04:\"#7195a8\",base05:\"#7ea2b4\",base06:\"#c1e4f6\",base07:\"#ebf8ff\",base08:\"#d22d72\",base09:\"#935c25\",base0A:\"#8a8a0f\",base0B:\"#568c3b\",base0C:\"#2d8f6f\",base0D:\"#257fad\",base0E:\"#5d5db1\",base0F:\"#b72dd2\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"atelier seaside\",author:\"bram de haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/)\",base00:\"#131513\",base01:\"#242924\",base02:\"#5e6e5e\",base03:\"#687d68\",base04:\"#809980\",base05:\"#8ca68c\",base06:\"#cfe8cf\",base07:\"#f0fff0\",base08:\"#e6193c\",base09:\"#87711d\",base0A:\"#c3c322\",base0B:\"#29a329\",base0C:\"#1999b3\",base0D:\"#3d62f5\",base0E:\"#ad2bee\",base0F:\"#e619c3\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"bespin\",author:\"jan t. sott\",base00:\"#28211c\",base01:\"#36312e\",base02:\"#5e5d5c\",base03:\"#666666\",base04:\"#797977\",base05:\"#8a8986\",base06:\"#9d9b97\",base07:\"#baae9e\",base08:\"#cf6a4c\",base09:\"#cf7d34\",base0A:\"#f9ee98\",base0B:\"#54be0d\",base0C:\"#afc4db\",base0D:\"#5ea6ea\",base0E:\"#9b859d\",base0F:\"#937121\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"brewer\",author:\"timothée poisot (http://github.com/tpoisot)\",base00:\"#0c0d0e\",base01:\"#2e2f30\",base02:\"#515253\",base03:\"#737475\",base04:\"#959697\",base05:\"#b7b8b9\",base06:\"#dadbdc\",base07:\"#fcfdfe\",base08:\"#e31a1c\",base09:\"#e6550d\",base0A:\"#dca060\",base0B:\"#31a354\",base0C:\"#80b1d3\",base0D:\"#3182bd\",base0E:\"#756bb1\",base0F:\"#b15928\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"bright\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#000000\",base01:\"#303030\",base02:\"#505050\",base03:\"#b0b0b0\",base04:\"#d0d0d0\",base05:\"#e0e0e0\",base06:\"#f5f5f5\",base07:\"#ffffff\",base08:\"#fb0120\",base09:\"#fc6d24\",base0A:\"#fda331\",base0B:\"#a1c659\",base0C:\"#76c7b7\",base0D:\"#6fb3d2\",base0E:\"#d381c3\",base0F:\"#be643c\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"chalk\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#151515\",base01:\"#202020\",base02:\"#303030\",base03:\"#505050\",base04:\"#b0b0b0\",base05:\"#d0d0d0\",base06:\"#e0e0e0\",base07:\"#f5f5f5\",base08:\"#fb9fb1\",base09:\"#eda987\",base0A:\"#ddb26f\",base0B:\"#acc267\",base0C:\"#12cfc0\",base0D:\"#6fc2ef\",base0E:\"#e1a3ee\",base0F:\"#deaf8f\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"codeschool\",author:\"brettof86\",base00:\"#232c31\",base01:\"#1c3657\",base02:\"#2a343a\",base03:\"#3f4944\",base04:\"#84898c\",base05:\"#9ea7a6\",base06:\"#a7cfa3\",base07:\"#b5d8f6\",base08:\"#2a5491\",base09:\"#43820d\",base0A:\"#a03b1e\",base0B:\"#237986\",base0C:\"#b02f30\",base0D:\"#484d79\",base0E:\"#c59820\",base0F:\"#c98344\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"colors\",author:\"mrmrs (http://clrs.cc)\",base00:\"#111111\",base01:\"#333333\",base02:\"#555555\",base03:\"#777777\",base04:\"#999999\",base05:\"#bbbbbb\",base06:\"#dddddd\",base07:\"#ffffff\",base08:\"#ff4136\",base09:\"#ff851b\",base0A:\"#ffdc00\",base0B:\"#2ecc40\",base0C:\"#7fdbff\",base0D:\"#0074d9\",base0E:\"#b10dc9\",base0F:\"#85144b\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"default\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#181818\",base01:\"#282828\",base02:\"#383838\",base03:\"#585858\",base04:\"#b8b8b8\",base05:\"#d8d8d8\",base06:\"#e8e8e8\",base07:\"#f8f8f8\",base08:\"#ab4642\",base09:\"#dc9656\",base0A:\"#f7ca88\",base0B:\"#a1b56c\",base0C:\"#86c1b9\",base0D:\"#7cafc2\",base0E:\"#ba8baf\",base0F:\"#a16946\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"eighties\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#2d2d2d\",base01:\"#393939\",base02:\"#515151\",base03:\"#747369\",base04:\"#a09f93\",base05:\"#d3d0c8\",base06:\"#e8e6df\",base07:\"#f2f0ec\",base08:\"#f2777a\",base09:\"#f99157\",base0A:\"#ffcc66\",base0B:\"#99cc99\",base0C:\"#66cccc\",base0D:\"#6699cc\",base0E:\"#cc99cc\",base0F:\"#d27b53\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"embers\",author:\"jannik siebert (https://github.com/janniks)\",base00:\"#16130F\",base01:\"#2C2620\",base02:\"#433B32\",base03:\"#5A5047\",base04:\"#8A8075\",base05:\"#A39A90\",base06:\"#BEB6AE\",base07:\"#DBD6D1\",base08:\"#826D57\",base09:\"#828257\",base0A:\"#6D8257\",base0B:\"#57826D\",base0C:\"#576D82\",base0D:\"#6D5782\",base0E:\"#82576D\",base0F:\"#825757\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"flat\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#2C3E50\",base01:\"#34495E\",base02:\"#7F8C8D\",base03:\"#95A5A6\",base04:\"#BDC3C7\",base05:\"#e0e0e0\",base06:\"#f5f5f5\",base07:\"#ECF0F1\",base08:\"#E74C3C\",base09:\"#E67E22\",base0A:\"#F1C40F\",base0B:\"#2ECC71\",base0C:\"#1ABC9C\",base0D:\"#3498DB\",base0E:\"#9B59B6\",base0F:\"#be643c\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"google\",author:\"seth wright (http://sethawright.com)\",base00:\"#1d1f21\",base01:\"#282a2e\",base02:\"#373b41\",base03:\"#969896\",base04:\"#b4b7b4\",base05:\"#c5c8c6\",base06:\"#e0e0e0\",base07:\"#ffffff\",base08:\"#CC342B\",base09:\"#F96A38\",base0A:\"#FBA922\",base0B:\"#198844\",base0C:\"#3971ED\",base0D:\"#3971ED\",base0E:\"#A36AC7\",base0F:\"#3971ED\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"grayscale\",author:\"alexandre gavioli (https://github.com/alexx2/)\",base00:\"#101010\",base01:\"#252525\",base02:\"#464646\",base03:\"#525252\",base04:\"#ababab\",base05:\"#b9b9b9\",base06:\"#e3e3e3\",base07:\"#f7f7f7\",base08:\"#7c7c7c\",base09:\"#999999\",base0A:\"#a0a0a0\",base0B:\"#8e8e8e\",base0C:\"#868686\",base0D:\"#686868\",base0E:\"#747474\",base0F:\"#5e5e5e\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"green screen\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#001100\",base01:\"#003300\",base02:\"#005500\",base03:\"#007700\",base04:\"#009900\",base05:\"#00bb00\",base06:\"#00dd00\",base07:\"#00ff00\",base08:\"#007700\",base09:\"#009900\",base0A:\"#007700\",base0B:\"#00bb00\",base0C:\"#005500\",base0D:\"#009900\",base0E:\"#00bb00\",base0F:\"#005500\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"harmonic16\",author:\"jannik siebert (https://github.com/janniks)\",base00:\"#0b1c2c\",base01:\"#223b54\",base02:\"#405c79\",base03:\"#627e99\",base04:\"#aabcce\",base05:\"#cbd6e2\",base06:\"#e5ebf1\",base07:\"#f7f9fb\",base08:\"#bf8b56\",base09:\"#bfbf56\",base0A:\"#8bbf56\",base0B:\"#56bf8b\",base0C:\"#568bbf\",base0D:\"#8b56bf\",base0E:\"#bf568b\",base0F:\"#bf5656\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"hopscotch\",author:\"jan t. sott\",base00:\"#322931\",base01:\"#433b42\",base02:\"#5c545b\",base03:\"#797379\",base04:\"#989498\",base05:\"#b9b5b8\",base06:\"#d5d3d5\",base07:\"#ffffff\",base08:\"#dd464c\",base09:\"#fd8b19\",base0A:\"#fdcc59\",base0B:\"#8fc13e\",base0C:\"#149b93\",base0D:\"#1290bf\",base0E:\"#c85e7c\",base0F:\"#b33508\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"isotope\",author:\"jan t. sott\",base00:\"#000000\",base01:\"#404040\",base02:\"#606060\",base03:\"#808080\",base04:\"#c0c0c0\",base05:\"#d0d0d0\",base06:\"#e0e0e0\",base07:\"#ffffff\",base08:\"#ff0000\",base09:\"#ff9900\",base0A:\"#ff0099\",base0B:\"#33ff00\",base0C:\"#00ffff\",base0D:\"#0066ff\",base0E:\"#cc00ff\",base0F:\"#3300ff\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"marrakesh\",author:\"alexandre gavioli (http://github.com/alexx2/)\",base00:\"#201602\",base01:\"#302e00\",base02:\"#5f5b17\",base03:\"#6c6823\",base04:\"#86813b\",base05:\"#948e48\",base06:\"#ccc37a\",base07:\"#faf0a5\",base08:\"#c35359\",base09:\"#b36144\",base0A:\"#a88339\",base0B:\"#18974e\",base0C:\"#75a738\",base0D:\"#477ca1\",base0E:\"#8868b3\",base0F:\"#b3588e\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"mocha\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#3B3228\",base01:\"#534636\",base02:\"#645240\",base03:\"#7e705a\",base04:\"#b8afad\",base05:\"#d0c8c6\",base06:\"#e9e1dd\",base07:\"#f5eeeb\",base08:\"#cb6077\",base09:\"#d28b71\",base0A:\"#f4bc87\",base0B:\"#beb55b\",base0C:\"#7bbda4\",base0D:\"#8ab3b5\",base0E:\"#a89bb9\",base0F:\"#bb9584\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"monokai\",author:\"wimer hazenberg (http://www.monokai.nl)\",base00:\"#272822\",base01:\"#383830\",base02:\"#49483e\",base03:\"#75715e\",base04:\"#a59f85\",base05:\"#f8f8f2\",base06:\"#f5f4f1\",base07:\"#f9f8f5\",base08:\"#f92672\",base09:\"#fd971f\",base0A:\"#f4bf75\",\nbase0B:\"#a6e22e\",base0C:\"#a1efe4\",base0D:\"#66d9ef\",base0E:\"#ae81ff\",base0F:\"#cc6633\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"ocean\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#2b303b\",base01:\"#343d46\",base02:\"#4f5b66\",base03:\"#65737e\",base04:\"#a7adba\",base05:\"#c0c5ce\",base06:\"#dfe1e8\",base07:\"#eff1f5\",base08:\"#bf616a\",base09:\"#d08770\",base0A:\"#ebcb8b\",base0B:\"#a3be8c\",base0C:\"#96b5b4\",base0D:\"#8fa1b3\",base0E:\"#b48ead\",base0F:\"#ab7967\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"paraiso\",author:\"jan t. sott\",base00:\"#2f1e2e\",base01:\"#41323f\",base02:\"#4f424c\",base03:\"#776e71\",base04:\"#8d8687\",base05:\"#a39e9b\",base06:\"#b9b6b0\",base07:\"#e7e9db\",base08:\"#ef6155\",base09:\"#f99b15\",base0A:\"#fec418\",base0B:\"#48b685\",base0C:\"#5bc4bf\",base0D:\"#06b6ef\",base0E:\"#815ba4\",base0F:\"#e96ba8\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"pop\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#000000\",base01:\"#202020\",base02:\"#303030\",base03:\"#505050\",base04:\"#b0b0b0\",base05:\"#d0d0d0\",base06:\"#e0e0e0\",base07:\"#ffffff\",base08:\"#eb008a\",base09:\"#f29333\",base0A:\"#f8ca12\",base0B:\"#37b349\",base0C:\"#00aabb\",base0D:\"#0e5a94\",base0E:\"#b31e8d\",base0F:\"#7a2d00\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"railscasts\",author:\"ryan bates (http://railscasts.com)\",base00:\"#2b2b2b\",base01:\"#272935\",base02:\"#3a4055\",base03:\"#5a647e\",base04:\"#d4cfc9\",base05:\"#e6e1dc\",base06:\"#f4f1ed\",base07:\"#f9f7f3\",base08:\"#da4939\",base09:\"#cc7833\",base0A:\"#ffc66d\",base0B:\"#a5c261\",base0C:\"#519f50\",base0D:\"#6d9cbe\",base0E:\"#b6b3eb\",base0F:\"#bc9458\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"shapeshifter\",author:\"tyler benziger (http://tybenz.com)\",base00:\"#000000\",base01:\"#040404\",base02:\"#102015\",base03:\"#343434\",base04:\"#555555\",base05:\"#ababab\",base06:\"#e0e0e0\",base07:\"#f9f9f9\",base08:\"#e92f2f\",base09:\"#e09448\",base0A:\"#dddd13\",base0B:\"#0ed839\",base0C:\"#23edda\",base0D:\"#3b48e3\",base0E:\"#f996e2\",base0F:\"#69542d\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"solarized\",author:\"ethan schoonover (http://ethanschoonover.com/solarized)\",base00:\"#002b36\",base01:\"#073642\",base02:\"#586e75\",base03:\"#657b83\",base04:\"#839496\",base05:\"#93a1a1\",base06:\"#eee8d5\",base07:\"#fdf6e3\",base08:\"#dc322f\",base09:\"#cb4b16\",base0A:\"#b58900\",base0B:\"#859900\",base0C:\"#2aa198\",base0D:\"#268bd2\",base0E:\"#6c71c4\",base0F:\"#d33682\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"summerfruit\",author:\"christopher corley (http://cscorley.github.io/)\",base00:\"#151515\",base01:\"#202020\",base02:\"#303030\",base03:\"#505050\",base04:\"#B0B0B0\",base05:\"#D0D0D0\",base06:\"#E0E0E0\",base07:\"#FFFFFF\",base08:\"#FF0086\",base09:\"#FD8900\",base0A:\"#ABA800\",base0B:\"#00C918\",base0C:\"#1faaaa\",base0D:\"#3777E6\",base0E:\"#AD00A1\",base0F:\"#cc6633\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"tomorrow\",author:\"chris kempson (http://chriskempson.com)\",base00:\"#1d1f21\",base01:\"#282a2e\",base02:\"#373b41\",base03:\"#969896\",base04:\"#b4b7b4\",base05:\"#c5c8c6\",base06:\"#e0e0e0\",base07:\"#ffffff\",base08:\"#cc6666\",base09:\"#de935f\",base0A:\"#f0c674\",base0B:\"#b5bd68\",base0C:\"#8abeb7\",base0D:\"#81a2be\",base0E:\"#b294bb\",base0F:\"#a3685a\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"london tube\",author:\"jan t. sott\",base00:\"#231f20\",base01:\"#1c3f95\",base02:\"#5a5758\",base03:\"#737171\",base04:\"#959ca1\",base05:\"#d9d8d8\",base06:\"#e7e7e8\",base07:\"#ffffff\",base08:\"#ee2e24\",base09:\"#f386a1\",base0A:\"#ffd204\",base0B:\"#00853e\",base0C:\"#85cebc\",base0D:\"#009ddc\",base0E:\"#98005d\",base0F:\"#b06110\"},e.exports=t[\"default\"]},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"twilight\",author:\"david hart (http://hart-dev.com)\",base00:\"#1e1e1e\",base01:\"#323537\",base02:\"#464b50\",base03:\"#5f5a60\",base04:\"#838184\",base05:\"#a7a7a7\",base06:\"#c3c3c3\",base07:\"#ffffff\",base08:\"#cf6a4c\",base09:\"#cda869\",base0A:\"#f9ee98\",base0B:\"#8f9d6a\",base0C:\"#afc4db\",base0D:\"#7587a6\",base0E:\"#9b859d\",base0F:\"#9b703f\"},e.exports=t[\"default\"]},function(e,t,n){function r(e){var t=Math.round(i(e,0,255)),n=t.toString(16);return 1==n.length?\"0\"+n:n}function o(e){var t=4===e.length?r(255*e[3]):\"\";return\"#\"+r(e[0])+r(e[1])+r(e[2])+t}var i=n(500);e.exports=o},function(e,t){function n(e,t,n){return Math.min(Math.max(e,t),n)}e.exports=n},function(e,t,n){function r(e){var t=i(e),n=l(t);return 4===t.length&&n.push(t[3]),n}function o(e){for(var t in c)if(0===e.indexOf(t))return c[t](e)}var i=n(502),a=n(504),s=n(505),l=n(506),c={\"#\":a,hsl:r,rgb:s};o.rgb=s,o.hsl=i,o.hex=a,e.exports=o},function(e,t,n){function r(e,t){switch(e=parseFloat(e),t){case 0:return a(e,0,360);case 1:case 2:return a(e,0,100);case 3:return a(e,0,1)}}function o(e){return i(e).map(r)}var i=n(503),a=n(500);e.exports=o},function(e,t){function n(e){return e.match(r)}var r=/-?\\d+(\\.\\d+)?%?/g;e.exports=n},function(e,t){function n(e){for(var t=\"#\",n=1;n<e.length;n++){var r=e.charAt(n);t+=r+r}return t}function r(e){(4===e.length||5===e.length)&&(e=n(e));var t=[parseInt(e.substring(1,3),16),parseInt(e.substring(3,5),16),parseInt(e.substring(5,7),16)];if(9===e.length){var r=parseFloat((parseInt(e.substring(7,9),16)/255).toFixed(2));t.push(r)}return t}e.exports=r},function(e,t,n){function r(e,t){return 3>t?-1!=e.indexOf(\"%\")?Math.round(255*a(parseInt(e,10),0,100)/100):a(parseInt(e,10),0,255):a(parseFloat(e),0,1)}function o(e){return i(e).map(r)}var i=n(503),a=n(500);e.exports=o},function(e,t){function n(e){var t,n,r,o,i,a=e[0]/360,s=e[1]/100,l=e[2]/100;if(0==s)return i=255*l,[i,i,i];n=.5>l?l*(1+s):l+s-l*s,t=2*l-n,o=[0,0,0];for(var c=0;3>c;c++)r=a+1/3*-(c-1),0>r&&r++,r>1&&r--,i=1>6*r?t+6*(n-t)*r:1>2*r?n:2>3*r?t+(n-t)*(2/3-r)*6:t,o[c]=255*i;return o}e.exports=n},function(e,t){(function(t){function n(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function r(e,t){for(var n=-1,r=t.length,o=e.length;++n<r;)e[o+n]=t[n];return e}function o(e,t,n,i,a){var l=-1,c=e.length;for(n||(n=s),a||(a=[]);++l<c;){var u=e[l];t>0&&n(u)?t>1?o(u,t-1,n,i,a):r(a,u):i||(a[a.length]=u)}return a}function i(e,t){return t=D(void 0===t?e.length-1:t,0),function(){for(var r=arguments,o=-1,i=D(r.length-t,0),a=Array(i);++o<i;)a[o]=r[t+o];o=-1;for(var s=Array(t+1);++o<t;)s[o]=r[o];return s[t]=a,n(e,this,s)}}function a(e){return i(function(t){t=o(t,1);var n=t.length,r=n;for(e&&t.reverse();r--;)if(\"function\"!=typeof t[r])throw new TypeError(f);return function(){for(var e=0,r=n?t[e].apply(this,arguments):arguments[0];++e<n;)r=t[e].call(this,r);return r}})}function s(e){return S(e)||l(e)||!!(E&&e&&e[E])}function l(e){return u(e)&&T.call(e,\"callee\")&&(!I.call(e,\"callee\")||C.call(e)==A)}function c(e){return null!=e&&p(e.length)&&!d(e)}function u(e){return g(e)&&c(e)}function d(e){var t=h(e)?C.call(e):\"\";return t==M||t==w}function p(e){return\"number\"==typeof e&&e>-1&&e%1==0&&m>=e}function h(e){var t=typeof e;return!!e&&(\"object\"==t||\"function\"==t)}function g(e){return!!e&&\"object\"==typeof e}var f=\"Expected a function\",m=9007199254740991,A=\"[object Arguments]\",M=\"[object Function]\",w=\"[object GeneratorFunction]\",v=\"object\"==typeof t&&t&&t.Object===Object&&t,b=\"object\"==typeof self&&self&&self.Object===Object&&self,y=v||b||Function(\"return this\")(),x=Object.prototype,T=x.hasOwnProperty,C=x.toString,N=y.Symbol,I=x.propertyIsEnumerable,E=N?N.isConcatSpreadable:void 0,D=Math.max,S=Array.isArray,L=a();e.exports=L}).call(t,function(){return this}())},function(e,t){\"use strict\";function n(e){var t,n,r,o=e[0],i=e[1],a=e[2];return t=1*o+0*i+1.13983*a,n=1*o+i*-.39465+a*-.5806,r=1*o+2.02311*i+0*a,t=Math.min(Math.max(0,t),1),n=Math.min(Math.max(0,n),1),r=Math.min(Math.max(0,r),1),[255*t,255*n,255*r]}function r(e){var t=e[0]/255,n=e[1]/255,r=e[2]/255,o=.299*t+.587*n+.114*r,i=t*-.14713+n*-.28886+.436*r,a=.615*t+n*-.51499+r*-.10001;return[o,i,a]}Object.defineProperty(t,\"__esModule\",{value:!0}),t.yuv2rgb=n,t.rgb2yuv=r},function(e,t){\"use strict\";t.__esModule=!0,t[\"default\"]={scheme:\"solarized\",author:\"ethan schoonover (http://ethanschoonover.com/solarized)\",base00:\"var(--c-j0)\",base01:\"var(--c-j1)\",base02:\"var(--c-j2)\",base03:\"var(--c-j3)\",base04:\"var(--c-j4)\",base05:\"var(--c-j5)\",base06:\"var(--c-j6)\",base07:\"var(--c-j7)\",base08:\"var(--c-j8)\",base09:\"var(--c-j9)\",base0A:\"var(--c-ja)\",base0B:\"var(--c-jb)\",base0C:\"var(--c-jc)\",base0D:\"var(--c-jd)\",base0E:\"var(--c-je)\",base0F:\"var(--c-jf)\"}},function(e,t,n){\"use strict\";var r=n(24),o=n(200),i=n(199),a=n(213),s=n(511),l=r.createClass({displayName:\"EnableHttpsBtn\",showHttpsSettingsDialog:function(){i.trigger(\"showHttpsSettingsDialog\")},render:function(){return o.isMultiEnv()?null:o.isCapture?r.createElement(s,{className:\"w-enable-https-help\",docsUrl:\"faq.html#tunnel-to\"}):r.createElement(a,{name:\"lock\",className:\"w-enable-https-btn\",onClick:this.showHttpsSettingsDialog})}});e.exports=l},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=r.createClass({displayName:\"HelpIcon\",shouldComponentUpdate:function(e){var t=this.props;return t.className!==e.className||t.docsUrl!==e.docsUrl||t.onClick!==e.onClick||t[\"data-name\"]!==e[\"data-name\"]},onClick:function(e){var t=this.props;return t.docsUrl?window.open(o.getDocUrl(t.docsUrl)):void t.onClick(e)},render:function(){var e=this.props.className,t=this.props[\"data-name\"];return r.createElement(\"a\",{\"data-name\":t,className:\"glyphicon glyphicon-question-sign w-help-icon\"+(e?\" \"+e:\"\"),onClick:this.onClick})}});e.exports=i},function(e,t,n){\"use strict\";var r=n(24),o=r.createClass({displayName:\"Empty\",render:function(){return r.createElement(\"div\",{className:\"box fill w-empty-data\"+(this.props.hide?\" hide\":\"\")},\"Empty\")}});e.exports=o},function(e,t,n){\"use strict\";function r(e,t){this.reset(e,t,!0)}function o(e,t){if(!c.isGroup(t))for(var n=0,r=e.length;r>n;n++)if(c.isGroup(e[n]))return e.splice(n,0,t);e.push(t)}function i(e,t,n){if(!e)return!1;var r=this.get(e);if(t=t||\"\",r)return void(r.value=t);n?this.list.splice(1,0,e):o(this.list,e);var i=this.data[e]={key:c.getKey(),name:e,value:t};return this.filter(),i}function a(e,t){return t?!e:e}function s(e){return!e.hide&&!c.isGroup(e.name)}var l=n(18),c=n(202),u=r.prototype;u.reset=function(e,t,n){var r=this;r.list=Array.isArray(e)?e:[],t=t||{},r.data={},r.groups={},r.list.forEach(function(e){var n=r.data[e]=t[e]||{};n.key=n.key||c.getKey(),n.name=e}),n||r.filter()},u.getList=function(){var e=this.data;return this.list.map(function(t){return e[t]})},u._getList=function(e){var t=[],n=this.data;return Object.keys(n).forEach(function(r){var o=n[r];o&&o[e]&&t.push(o)}),t},u.getItem=function(e){return this.data[e]},u.hasChanged=function(){var e=this.data;return Object.keys(e).some(function(t){var n=e[t];return n&&n.changed&&!c.isGroup(n.name)})},u.getGroupName=function(e){var t=this.list.indexOf(e);if(-1!==t)for(;t>=0;t--)if(e=this.list[t],c.isGroup(e))return e},u._setBoolProp=function(e,t,n){var r=this.get(e);return r&&(r[t]=n!==!1),this.filter(),r},u.getSelectedNames=function(){var e=[],t=this.data;return Object.keys(t).forEach(function(n){var r=t[n];r&&r.selected&&e.push(r.name)}),e},u.exists=function(e){return-1!=this.list.indexOf(e)},u.add=function(e,t){return i.call(this,e,t)},u.unshift=function(e,t){return i.call(this,e,t,!0)},u.set=function(e,t){var n=this.get(e);n&&(\"string\"==typeof t?n.value=t:l.extend(n,t))},u.get=function(e){return this.data[e]},u.getByKey=function(e){for(var t=this.list,n=this.data,r=0,o=t.length;o>r;r++){var i=n[t[r]];if(i.key==e)return i}},u.setSelected=function(e,t){return this._setBoolProp(e,\"selected\",t)},u.moveTo=function(e,t,n,r){var o=this.list,i=o.indexOf(e),a=o.indexOf(t);if(-1!==i&&-1!==a&&i!==a){if(n&&c.isGroup(e)){for(var s=this.data,l=[e],u=i+1,d=o.length;d>u;u++){var p=s[o[u]].name;if(c.isGroup(p))break;l.push(p)}if(a>i&&c.isGroup(t))for(;d>a&&!c.isGroup(o[a+1]);a++);d=l.length,d>1&&a>i&&(a=Math.max(0,a-d+1)),o.splice(i,d),l.unshift(a,0),o.splice.apply(o,l)}else r||c.isGroup(e)||!c.isGroup(t)||this.getGroupName(e)===t?(o.splice(i,1),o.splice(a,0,e)):(o.splice(i,1),o.splice(i>a?a+1:a,0,e));return!0}},u.moveToGroup=function(e,t,n){if(t){var r=this.list,o=r.indexOf(e);if(-1!==o&&-1!==r.indexOf(t)){if(r.splice(o,1),o=r.indexOf(t)+1,n)return r.splice(o,0,e);for(var i=r.length;i>o&&!c.isGroup(r[o]);o++);r.splice(o,0,e)}}},u.getSelectedList=function(){return this._getList(\"selected\")},u.setChanged=function(e,t){return this._setBoolProp(e,\"changed\",t)},u.getChangedList=function(){return this._getList(\"changed\")},u.getChangedGroupList=function(e){if(!c.isGroup(e.name))return[e];var t=[],n=this.list,r=this.data,o=n.indexOf(e.name)+1;if(o>0)for(var i=n.length;i>o&&(e=r[n[o]],!c.isGroup(e.name));o++)e.changed&&t.push(e);return t},u.clearAllActive=function(){var e=this.data;Object.keys(e).forEach(function(t){e[t].active=!1})},u.clearAllSelected=function(){var e=this.data;Object.keys(e).forEach(function(t){e[t].selected=!1})},u.setActive=function(e,t){var n=this.get(e);return n&&!c.isGroup(n.name)&&(t=t!==!1,t&&this.clearAllActive(),n.active=t),n},u.getActiveObj=function(){for(var e={},t=this.list,n=this.data,r=0,o=t.length;o>r;r++){var i=n[t[r]];if(c.isGroup(i.name)){if(i._isNewGroup&&(delete i._isNewGroup,e.groupItem=i,e.activeItem))return e}else if(i.active&&(e.activeItem=i,e.groupItem))return e}return e},u.getActive=function(e){var t;e=e===!0;for(var n=this.list,r=0,o=n.length;o>r;r++){var i=this.data[n[r]];if(!c.isGroup(i.name)){if(i.active)return i;e&&(t=t||i)}}return t&&(t.active=!0),t},u.remove=function(e){var t=this.getIndex(e);return-1!=t?(this.list.splice(t,1),delete this.data[e],!0):void 0},u.removeGroup=function(e){var t=this.list,n=t.indexOf(e)+1;if(n){for(var r,o=[e],i=this.data,a=t.length;a>n&&(e=t[n],!c.isGroup(e));n++){o.push(e);var s=i[e];r=r||s&&s.active}return o.forEach(function(e){t.splice(t.indexOf(e),1),delete i[e]}),r&&this.getSibling(e)}},u.rename=function(e,t){if(e&&t&&e!=t){var n=this.getIndex(e);if(-1!=n){this.list[n]=t;var r=this.data[e];return delete this.data[e],this.data[t]=r,r.name=t,this.filter(),!0}}},u.getIndex=function(e){return this.list.indexOf(e)},u.getSibling=function(e){var t=this.getIndex(e),n=this.list;if(e=n[t+1],e=c.isGroup(e)&&n[t-1],e&&!c.isGroup(e))return this.data[e];for(var r=t+1,o=n.length;o>r;r++)if(e=n[r],!c.isGroup(e))return this.data[e];for(r=t-1;r>=0;r--)if(e=n[r],!c.isGroup(e))return this.data[e]},u.search=function(e,t){e=\"string\"==typeof e?e.trim():\"\";var n=\"!\"===e[0];return n&&(e=e.substring(1).trim()),e&&(t?/^(b|v|n|k|s):(.*)$/:/^(selected|s|active|a|b|v|n|k|s):(.*)$/).test(e)?(this._type=RegExp.$1,e=RegExp.$2.trim(),\"!\"===e[0]&&(n=!0,e=e.substring(1).trim())):this._type=\"\",this._keyword=e&&(c.toRegExp(e)||e.toLowerCase()),this._not=n,this.filter(),!e},u.filter=function(){var e,t,n=this._not,r=this._keyword,o=this.list,i=\"b\"===this._type||\"v\"===this._type,s=\"n\"===this._type||\"k\"===this._type,l=!!this._type&&!i&&!s,u=this.data;return o.forEach(function(o){var d=u[o],p=l&&!d.selected;c.isGroup(o)&&(e&&t&&(e.hide=!1),e=d,t=!1),!r||p?d.hide=p:i?d.hide=!d.value||a(c.checkKey(d.value,d.value,r),n):(d.hide=!o||a(c.checkKey(o,o,r),n),d.hide&&!s&&(d.hide=!d.value||a(c.checkKey(d.value,d.value,r),n))),t=t||!d.hide}),o},u.up=function(){var e=this.list,t=e.length;if(t){var n=this.getActive();if(!n.fixed){var r=n?e.indexOf(n.name):t-1;if(r&&!this.data[e[r-1]].fixed)return e[r]=e[r-1],e[r-1]=n.name,n}}},u.down=function(){var e=this.list,t=e.length;if(t){var n=this.getActive();if(!n.fixed){var r=n?e.indexOf(n.name):t-1;if(!(r>=t-1||this.data[e[r+1]].fixed))return e[r]=e[r+1],e[r+1]=n.name,n}}},u.prev=function(){var e=this.list,t=e.length;if(t){var n,r,o=this.getActive(),i=o?e.indexOf(o.name):t-1,a=this.data;for(n=i-1;n>=0;n--)if(r=a[e[n]],s(r))return r;for(n=t-1;n>i;n--)if(r=a[e[n]],s(r))return r}},u.next=function(){var e=this.list,t=e.length;if(t){var n,r,o=this.getActive(),i=o?e.indexOf(o.name):0,a=this.data;for(n=i+1;t>n;n++)if(r=a[e[n]],s(r))return r;for(n=0;i>n;n++)if(r=a[e[n]],s(r))return r}},e.exports=r},function(e,t,n){\"use strict\";var r=n(18),o=n(24),i=n(202),a=n(210),s=n(220),l=n(515),c=n(524),u=n(199),d=function(e){var t=document.documentElement;return e?Math.max(Math.floor(t.clientHeight/2),360):Math.max(Math.floor(t.clientWidth/3),572)},p=o.createClass({displayName:\"Network\",getInitialState:function(){var e=a.get(\"dockToBottom\");return null==e&&/[&#?]dockToBottom=true(?:&|$|#)/.test(window.location.search)&&(e=!0),{dockToBottom:e,rightWidth:d(e)}},componentDidMount:function(){var e=this;r(window).on(\"keydown\",function(t){if(!e.props.hide&&(t.ctrlKey||t.metaKey)&&68==t.keyCode&&!i.isFocusEditor()&&!r(t.target).closest(\".w-frames-list\").length){var n=e.props.modal;t.shiftKey?n&&n.removeUnselectedItems()&&e.setState({}):n&&n.removeSelectedItems()&&e.setState({})}}).on(\"keydown\",function(t){123===t.keyCode&&(e.props.hide||e.onDockChange(),t.preventDefault())}),u.trigger(\"networkDidMount\"),u.on(\"toggleNetworkDock\",e.onDockChange)},shouldComponentUpdate:i.shouldComponentUpdate,onDockChange:function(){var e=this,t=!e.state.dockToBottom;a.set(\"dockToBottom\",t?1:\"\"),e.setState({dockToBottom:t,rightWidth:d(t)},function(){e.refs.divider.reset();var t=document.querySelectorAll(\".w-detail .w-divider\");t.forEach(function(e){window.MouseEvent&&e.dispatchEvent(new MouseEvent(\"dblclick\",{bubbles:!0,cancelable:!0,view:window}))})})},render:function(){var e=this.props.modal,t=this.state.dockToBottom;return o.createElement(\"div\",{className:\"v-box fill\"+(this.props.hide?\" hide\":\"\")},o.createElement(s,{ref:\"divider\",vertical:t,rightWidth:this.state.rightWidth},o.createElement(l,{modal:e}),o.createElement(c,{dockToBottom:t,onDockChange:this.onDockChange,modal:e,rulesModal:this.props.rulesModal})))}});e.exports=p},function(e,t,n){\"use strict\";function r(e,t){return t=t?m.extend({},t):{},t[e.minWidth?\"minWidth\":\"width\"]=w.getWidth(e),t}function o(e){var t=e.rules;if(!t)return\"\";var n=t.rule;return n&&(n.isLoc||n.isSpec)?\" w-has-local\":e[T.HAS_RULES_KEY]?\" w-has-rules\":\"\"}function i(e){return l(e)+\" w-req-data-item\"+(e.isHttps?\" w-tunnel\":\"\")+o(e)+(e.selected?\" w-selected\":\"\")+(e.isPR?\" w-pr\":\"\")}function a(e){return!e.hide}function s(e){var t=e.parent;return!t||t.expand&&t.pExpand}function l(e){var t=\"\",n=e.res.headers;switch(M.getContentType(n)){case\"JS\":t=\"warning\";break;case\"CSS\":t=\"info\";break;case\"HTML\":t=\"success\";break;case\"IMG\":t=\"active\";break;case\"JSON\":t=\"_json\";break;case\"XML\":t=\"_xml\"}var r=e.res&&e.res.statusCode;return e.reqError||e.resError||e.customData&&e.customData.error?t+=\" danger w-error-status\":403==r?t+=\" w-forbidden\":r&&(!/^\\d+$/.test(r)||r>=400)&&(t+=\" w-error-status\"),e.mark&&(t+=\" w-mark\"),t}function c(e){return-1!==e.indexOf(\"w-error-status\")?\"ERROR\":-1!==e.indexOf(\"warning\")?\"JS\":-1!==e.indexOf(\"info\")?\"CSS\":-1!==e.indexOf(\"success\")?\"HTML\":-1!==e.indexOf(\"active\")?\"IMG\":-1!==e.indexOf(\"_json\")?\"JSON\":-1!==e.indexOf(\"_xml\")?\"XML\":\"\"}function u(e,t){if(-1!==t.indexOf(\"danger\"))return g.createElement(I,{name:\"remove-circle\",className:\"icon-leaf\"});if(-1!==t.indexOf(\"w-forbidden\"))return g.createElement(I,{name:\"ban-circle\",className:\"icon-leaf\"});if(e&&!e.endTime&&!e.lost)return g.createElement(I,{name:\"hourglass\",className:\"icon-leaf\"});var n=c(t),r=e.res&&e.res.statusCode;return\"ERROR\"!==n&&101!=r&&(r=null),r||n?g.createElement(\"span\",{className:\"w-type-icon\"},r||n):g.createElement(I,{name:\"file\"})}function d(e){e.removeClass(\"highlight\")}var p=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e};n(516),n(518);var h=n(520),g=n(24),f=n(57),m=n(18),A=n(521),M=n(202),w=n(523),v=n(290),b=n(293),y=n(199),x=n(296),T=n(200),C=n(210),N=n(206),I=n(213),E=f.findDOMNode,D=24,S={outline:\"none\"},L={},k={},j=/^:dump\\s+(\\d{1,15})\\s*$/,U=/(^\\s*|\\s+)(content|c|b|body):/i,B=64,R=[\"<keyword or regex for URL>\",\"d:<keyword or regex for domain>\",\"m:<keyword or regex for HTTP method>\",\"s:<keyword or regex for HTTP status code>\",\"h:<keyword or regex for request or response headers>\",\"b:<keyword or regex for request or response body>\"],z=[{name:\"Open\",list:[{name:\"New Tab\"},{name:\"QR Code\"},{name:\"Overview\"},{name:\"Inspectors\"},{name:\"Timeline\"},{name:\"Composer\"},{name:\"Preview\"},{name:\"Source\"},{name:\"Tree View\",action:\"toggleView\"}]},{name:\"Copy\",shiftToEdit:!0,list:[{name:\"Cell Text\"},{name:\"Host\"},{name:\"Path\"},{name:\"URL\"},{name:\"Full URL\"},{name:\"As cURL\"},{name:\"Client IP\"},{name:\"Server IP\"},{name:\"Cookie\"}]},{name:\"Remove\",list:[{name:\"All\"},{name:\"One\"},{name:\"Others\"},{name:\"Selected\"},{name:\"Unselected\"},{name:\"Unmarked\"},{name:\"All Matching Hosts\",action:\"removeAllSuchHost\"},{name:\"All Matching URLs\",action:\"removeAllSuchURL\"}]},{name:\"Settings\",list:[{name:\"Edit Settings\"},{name:\"Exclude All Matching Hosts\",action:\"excludeHost\"},{name:\"Exclude All Matching URLs\",action:\"excludeUrl\"}]},{name:\"Actions\",list:[{name:\"Abort\"},{name:\"Replay\"},{name:\"Replay Times\",action:\"replayTimes\"},{name:\"Edit Request\"},{name:\"Mark\"},{name:\"Unmark\"}]},{name:\"Tree\",list:[{name:\"Expand\"},{name:\"Collapse\"},{name:\"Expand All\"},{name:\"Collapse All\"},{name:\"List View\",action:\"toggleView\"}]},{name:\"Mock\"},{name:\"Service\",hide:!0,list:[{name:\"Create API Test\",action:\"createApiTest\"},{name:\"Copy As Script\",action:\"copyAsScript\"},{name:\"Share Via URL\",action:\"Export\"}]},{name:\"Save\"},{name:\"Import\"},{name:\"Export\"},{name:\"Others\",action:\"Plugins\",list:[]},{name:\"Help\",sep:!0}],Q=function(e){return e&&!e.selected?[e]:void 0},O=g.createClass({displayName:\"Spinner\",render:function(){var e=this.props.order,t=\"desc\"==e;return t||\"asc\"==e||(e=null),e=e?\" spinner-\"+e:\"\",g.createElement(\"div\",{className:\"w-spinner\"},g.createElement(I,{name:\"triangle-top\",className:e}),g.createElement(I,{name:\"triangle-bottom\",className:e}))}}),H=g.createClass({displayName:\"Row\",render:function(){var e=this.props,t=e.order,n=e.notQuery,o=e.draggable,a=e.columnList,s=e.item,l=s.style;return g.createElement(\"table\",{className:\"table w-req-table\",key:e.key,style:e.style,\"data-id\":s.id},g.createElement(\"tbody\",{className:\"w-hover-body\"},g.createElement(\"tr\",{tabIndex:\"-1\",draggable:o,\"data-id\":s.id,className:i(s)+(s.highlight?\" w-highlight\":\"\"),style:S},g.createElement(\"th\",{className:\"order\",scope:\"row\",style:l},t,s.importedData?g.createElement(I,{name:\"import\"}):null,s.fc&&!s.importedData?g.createElement(I,{name:\"send\"}):null),a.map(function(e){var t,o,i=e.name;if(\"custom1\"===i){var a=T.custom1Key;M.notEStr(a)&&(s.custom1=M.getValue(s,a))}else if(\"custom2\"===i){var c=T.custom2Key;M.notEStr(c)&&(s.custom2=M.getValue(s,c))}else\"path\"===i&&(t=s.shortPath,o=s.url);if(t=t||M.getCellValue(s,e),t&&n&&\"path\"===i){var u=t.indexOf(\"?\");-1!==u&&(t=t.substring(0,u))}var d=r(e,l),p=e.iconKey&&M.getProperty(s,e.iconKey),h=\"function\"==typeof e.getIcon?e.getIcon(s):void 0;return g.createElement(\"td\",{key:i,className:e.className,style:d,title:e.showTitle?o||t:void 0},h,p?g.createElement(\"img\",{className:\"w-cell-img\",src:p}):null,t)}))))}}),F=g.createClass({displayName:\"ReqData\",getInitialState:function(){var e=w.getDragger();return e.onDrop=e.onDrop.bind(this),{draggable:!0,columns:w.getSelectedColumns(),dragger:e,sessionsName:\"\"}},componentDidUpdate:function(){if(this.isShownBtn&&y.trigger(\"checkAtBottom\"),\"1\"!==C.get(\"disabledHNR\")){var e=this.props.modal;if(e.isTreeView&&this.visibleList&&null!=this.startIndex){var t=T.curNewIdList;if(t&&t.length){T.curNewIdList=null;for(var n,r={},o={},i=this.visibleList,a=[],l={},c=this.startIndex;c<=this.endIndex;c++)n=i[c],n&&(n.parent?n.data?s(n)&&(l[n.data.id]=1,-1!==t.indexOf(n.data.id)&&a.push(n)):o[n.path]=1:(m.extend(r,n.map),o[n.path]=1));var u=T.overflowCount();if(u>0){var p,h=e.list,g=h.length;for(c=0;g>c&&(n=h[c],!n.hide&&(n.active||l[n.id]||r[n.id])||(h.splice(c,1),--u,--c,--g,p=!0,!(0>=u)));c++);if(u>0)for(g=h.length,c=0;g>c&&(n=h[c],n.active||l[n.id]||(h.splice(c,1),--u,--c,--g,p=!0,!(0>=u)));c++);p&&(e.updateTree(),y.trigger(\"updateGlobal\"))}t.forEach(function(e){var t=r[e];if(t)for(var n=t.parent;n;)o[n.path]&&(o[n.path]=0,a.push(n)),-1===a.indexOf(t)&&s(t)&&a.push(t),n=n.parent});var f=Math.floor((a.length-60)/2);f>0&&(a=a.slice(f,-f)),a=a.map(function(e){var t;return t=m(e.data?'tr[data-id=\"'+e.data.id+'\"]:not(.highlight)':'tr[data-tree=\"'+e.path+'\"]:not(.highlight)'),t.length?t.addClass(\"highlight\"):void 0}).filter(M.noop),a.length&&setTimeout(function(){a.forEach(d)},800)}}}},getActivedList:function(e){var t=this.props.modal;return Q(e)||t&&t.getSelectedList()},componentDidMount:function(){var e,t=this;y.on(\"hashFilterChange\",function(){t.setState({})}),y.on(\"onColumnsChanged\",function(){t.setState({columns:w.getSelectedColumns()})}),y.on(\"onColumnTitleChange\",function(){t.setState({})}),y.on(\"changeRecordState\",function(e,n){t.setState({record:n},t.updateList)}),y.on(\"selectedIndex\",function(e,n){var r=t.props.modal.getList(),o=r&&(r[n]||r[r.length-1]);o&&t.triggerActiveItem(o)}),y.on(\"focusNetworkFilterInput\",function(){t.refs.filterInput.focus()}),y.on(\"saveSessions\",function(e,n){var r=t.getActivedList(n),o=r&&r.length;o&&(t._sessionsList=r,t._pendingSave=!1,m(E(t.refs.saveSessions)).modal(\"show\"),setTimeout(function(){var e=E(t.refs.sessionsName);e.focus(),e.select()},500))}),y.on(\"replayTreeView\",function(e,n,r){var o=t.props.modal.getTreeNode(n),i=o&&o.parent;if(i){var a=i.children.filter(s);o=a[a.length-1],o&&t.scrollToRow(o,r)}});var n=function(){t.setState({})},r=function(){e&&clearTimeout(e),e=setTimeout(n,60)};t.container=m(E(t.refs.container)),t.content=E(t.refs.content);var o=0;t.$content=m(t.content).on(\"dblclick\",\"tr\",function(e){o>2&&y.trigger(\"toggleDetailTab\")}).on(\"click\",\"tr\",function(e){var n=this.getAttribute(\"data-id\");if(n){T.lastSelectedDataId!=n&&(o=0,T.lastSelectedDataId=n),++o;var r=t.props.modal.getItem(n);t.onClick(e,r)}});var i=function(e){var n=!e.shiftKey;t.state.draggable!==n&&t.setState({draggable:n})};t.container.on(\"keydown\",function(e){var n=t.props.modal;i(e);var r;38==e.keyCode?r=e.ctrlKey||e.metaKey?n.start():n.prev():40==e.keyCode&&(r=e.ctrlKey||e.metaKey?n.end():n.next()),r&&(t.onClick(e,r,!0),e.preventDefault())}).on(\"scroll\",r).on(\"keyup\",i).on(\"mouseover\",i).on(\"mouseleave\",i),m(window).on(\"resize\",r),y.on(\"ensureSelectedItemVisible\",function(){var e=t.props.modal,n=t.props.modal.getSelectedList()[0];n&&e.isTreeView&&(n=e.getTreeNode(n.id)),n&&t.scrollToRow(n)}),y.on(\"focusNetworkList\",function(){t.container.focus()});var a,l=function(){var e=location.hash.substring(1),t=e.indexOf(\"?\");if(-1!==t){var n=M.parseQueryString(e.substring(t+1),null,null,decodeURIComponent).sessionsUrl;if(/^https?:\\/\\/[^/]/i.test(n)&&n!==a){a=n.replace(/#.*$/,\"\");var r=a;-1===a.indexOf(\"&from_5b6af7b9884e1165\")&&(r+=(-1===r.indexOf(\"?\")?\"?\":\"\")+\"&from_5b6af7b9884e1165\"),y.trigger(\"importSessionsFromUrl\",r)}}};l(),m(window).on(\"hashchange\",l);var c=m(E(t.refs.backBtn)),u=function(){t.isShownBtn&&(t.isShownBtn=!1,c.hide())};t.hideBackBtn=u,t.isShownBtn=!1,y.on(\"toggleBackToBottomBtn\",function(e,n){n?t.isShownBtn||(t.isShownBtn=!0,c.show()):u()}),y.on(\"checkViewInspectors\",function(e,n){t._curComposerReqId=n&&{reqId:n},n&&t.props.modal.getItem(n)&&t.showViewInspectorsBtn(!0)}),y.on(\"setActiveSession\",function(e,n){var r=t.props.modal.getItem(n);r&&t.triggerActiveItem(r)})},onDragStart:function(e){var t=m(e.target).closest(\".w-req-data-item\"),n=t.attr(\"data-id\");n&&e.dataTransfer.setData(\"reqDataId\",n)},getSelectedRows:function(e){var t=this.props.modal.getActive();if(t&&e!==t)return[t,e]},onClick:function(e,t,n){if(t){var r,o=this,i=o.props.modal,a=e.ctrlKey||e.metaKey;n?(o.clearSelection(),this.$content.find(\"tr.w-selected\").removeClass(\"w-selected\"),t.selected=!0,o.setSelected(t)):e.shiftKey&&(r=o.getSelectedRows(t))?(this.$content.find(\"tr.w-selected\").removeClass(\"w-selected\"),i.setSelectedList(r[0],r[1],o.setSelected)):(a||(this.$content.find(\"tr.w-selected\").removeClass(\"w-selected\"),o.clearSelection()),t.selected=!a||!t.selected,o.setSelected(t,!0)),i.clearActive(),t.active=t.selected,n&&o.scrollToRow(t),y.trigger(\"networkStateChange\"),y.trigger(\"selectedSessionChange\",t)}},setSelected:function(e,t){e.selected?this.$content.find(\"tr[data-id=\"+e.id+\"]\").addClass(\"w-selected\"):t&&this.$content.find(\"tr[data-id=\"+e.id+\"]\").removeClass(\"w-selected\")},clearSelection:function(){this.props.modal.clearSelection()},getFilterList:function(){var e=T.getFilterText();return e.disabledExcludeText?[]:e.excludeText.trim().split(/\\s+/g)},updateFilter:function(e){var t=T.getFilterText();t.excludeText=e,t.disabledExcludeText=!1,T.setFilterText(t),y.trigger(\"filterChanged\")},getActiveList:function(e){return e.selected?this.props.modal.getSelectedList():[e]},removeAllSuchHost:function(e,t){var n=[],r=this.getActiveList(e);if(r.forEach(function(e){var t=e.isHttps?e.path:e.hostname;-1===n.indexOf(t)&&n.push(t)}),this.props.modal.removeByHostList(n),!t){var o=this.getFilterList();n.forEach(function(e){e=\"H:\"+e,-1===o.indexOf(e)&&o.unshift(e)}),this.updateFilter(o.join(\"\\n\")),y.trigger(\"shakeSettings\")}y.trigger(\"updateGlobal\")},removeTreeNode:function(e,t){this.props.modal.removeTreeNode(e,t)&&y.trigger(\"updateGlobal\")},removeAllSuchURL:function(e,t){var n=[],r=this.getActiveList(e);if(r.forEach(function(e){var t=e.isHttps?e.path:e.url.replace(/\\?.*$/,\"\").substring(0,1024);-1===n.indexOf(t)&&n.push(t)}),this.props.modal.removeByUrlList(n),!t){var o=this.getFilterList();n.forEach(function(e){-1===o.indexOf(e)&&o.unshift(e)}),this.updateFilter(o.join(\"\\n\")),y.trigger(\"shakeSettings\")}y.trigger(\"updateGlobal\")},triggerActiveItem:function(e){this.onClick(\"\",e,!0),y.trigger(\"networkStateChange\")},onClickHeadMenu:function(e){var t=this.curHeadCol;t&&(w.setWidth(t.name,e),this.setState({columns:w.getSelectedColumns()}))},onClickContextMenu:function(e,t,n,r){var o=this,i=o.currentFocusItem,a=o.props.modal,s=o.treeTarget,l=i&&i.url||s&&s+\"/\";switch(o.currentFocusItem=null,n||e){case\"New Tab\":l&&window.open(l);break;case\"QR Code\":o.refs.qrcodeDialog.show(l);break;case\"Preview\":M.openPreview(i);break;case\"Source\":M.openEditor(JSON.stringify(i,null,\"  \"));break;case\"toggleView\":y.trigger(\"switchTreeView\");break;case\"Overview\":o.triggerActiveItem(i),y.trigger(\"showOverview\");break;case\"Inspectors\":o.triggerActiveItem(i),y.trigger(\"showInspectors\");break;case\"Timeline\":o.triggerActiveItem(i),y.trigger(\"showTimeline\");break;case\"Composer\":case\"Edit Request\":y.trigger(\"composer\",i);break;case\"Mark\":case\"Unmark\":var c=this.getActivedList(i);if(c){var u=\"Mark\"===e;c.forEach(function(e){e.mark=u})}this.setState({});break;case\"Replay\":y.trigger(\"replaySessions\",[i,t.shiftKey]);break;case\"replayTimes\":y.trigger(\"replaySessions\",[i,!0]);break;case\"Export\":o.treeTarget&&!o.isTreeLeafNode?y.trigger(\"exportSessions\",[a.getListByPath(o.treeTarget)]):y.trigger(\"exportSessions\",i);break;case\"createApiTest\":return M.showService(\"createApiTest\");case\"copyAsScript\":return M.showService(\"copyAsScript\");case\"Save\":y.trigger(\"saveSessions\",[i]);break;case\"Abort\":y.trigger(\"abortRequest\",i);break;case\"Import\":y.trigger(\"showImportDialog\");break;case\"Edit Settings\":y.trigger(\"filterSessions\",t);break;case\"removeAllSuchHost\":l&&o.removeAllSuchHost(i,!0);break;case\"removeAllSuchURL\":l&&o.removeAllSuchURL(i||l,!0);break;case\"excludeHost\":l&&o.removeAllSuchHost(i);break;case\"excludeUrl\":l&&o.removeAllSuchURL(i||l);break;case\"One\":s?o.removeTreeNode(s):y.trigger(\"removeIt\",i);break;case\"All\":y.trigger(\"clearAll\");break;case\"Others\":s?o.removeTreeNode(s,!0):y.trigger(\"removeOthers\",i);break;case\"Selected\":y.trigger(\"removeSelected\");break;case\"Unselected\":y.trigger(\"removeUnselected\");break;case\"Unmarked\":y.trigger(\"removeUnmarked\");break;case\"Help\":window.open(M.getDocUrl(\"gui/network.html\"));\nbreak;case\"Plugins\":x.fork(e,{port:T.getPort(),type:\"network\",name:r,activeItem:i,activeList:a.getTreeLeafs(s),selectedList:o.props.modal.getSelectedList()});break;case\"Expand\":case\"Collapse\":o.toggleNode(s);break;case\"Expand All\":o.expandAll(s);break;case\"Collapse All\":o.collapseAll(s);break;case\"Mock\":y.trigger(\"showMockDialog\",{item:i})}},onHeadCtxMenu:function(e){e.preventDefault();var t=m(e.target).closest(\"th\").attr(\"data-name\"),n=w.getColumn(t),r=n&&n.menus;if(r){this.curHeadCol=n;var o=M.getMenuPosition(e,130,310);o.list=r,o.radio=!0,o.className=\"w-ctx-radio-list\",this.refs.headContextMenu.show(o)}},onContextMenu:function(e){var t=m(e.target),n=t.prop(\"nodeName\"),r=t.closest(\".w-req-data-item\");e.preventDefault(),r.length||(r=t.closest(\".w-req-table\"));var o=this.props.modal,i=r.attr(\"data-id\");if(clearTimeout(this._delayCtxTimer),!o.isTreeView&&!i){var a=this.container.find(\".ReactVirtualized__Grid:first\");if(a.length&&document.elementFromPoint&&a[0].offsetHeight<a[0].scrollHeight){var s=this,l=e.pageX,c=e.pageY;return void(this._delayCtxTimer=setTimeout(function(){t=m(document.elementFromPoint(l,c)).closest(\".w-req-data-item\")[0],t&&s.onContextMenu({target:t,pageX:l,pageY:c,preventDefault:M.noop})},300))}}var u=r.attr(\"data-tree\"),d=o.getItem(i),p=!d,h=d&&(\"TD\"===n||\"TH\"===n)&&(t.text()||\"\").trim(),g=o.isTreeView&&o.getTreeNode(u);this.treeTarget=null,this.currentFocusItem=d;var f=p&&!g,A=z[0].list;if(A[4].disabled=f||!/^https?:\\/\\//.test(u||d.url),p||f)A[6].disabled=!0;else{var w=M.getContentType(d.res.headers);A[6].disabled=!d.res.base64||\"HTML\"!==w&&\"IMG\"!==w}A[0].disabled=f,A[1].disabled=f,A[2].disabled=p,A[3].disabled=p,A[4].disabled=p,A[5].disabled=p,A[7].disabled=p,o.isTreeView?A[8].name=\"List View\":A[8].name=\"Tree View\",z[1].disabled=p&&!u;var v=u?u+\"/\":\"\",b=p&&!v;z[1].list.forEach(function(e){switch(e.disabled=p,e.name){case\"Cell Text\":e.copyText=h,e.disabled=p||!h;break;case\"URL\":e.copyText=M.getUrl(d&&d.url.replace(/[?#].*$/,\"\")||v),e.disabled=b;break;case\"Host\":e.copyText=d&&(d.isHttps?d.path:d.hostname)||M.getHost(v),e.disabled=b;break;case\"Path\":e.copyText=d&&d.path||M.getPath(v),e.disabled=b;break;case\"Full URL\":e.copyText=M.getUrl(d&&d.url||v),e.disabled=b;break;case\"As cURL\":e.copyText=M.asCURL(d);break;case\"Client IP\":e.copyText=d&&d.clientIp;break;case\"Server IP\":var t=d&&M.getServerIp(d);e.disabled=!t,e.copyText=t;break;case\"Cookie\":var n=d&&d.req.headers.cookie;e.disabled=!n,e.copyText=n}});var y=z[2].list;z[2].disabled=p;for(var x=0;4>x;x++)y[x].disabled=p;p||(y[0].disabled=!d.requestTime||!d.req.base64,y[1].disabled=!d.endTime||!d.res.base64,y[2].disabled=!d.requestTime,y[3].disabled=!d.endTime);var C=o.getSelectedList(),N=C.length,I=o.list.length,E=z[2].list;z[2].disabled=!I,E[0].disabled=!I,E[1].disabled=f,E[2].disabled=p||N===I,E[3].disabled=!N,E[4].disabled=N===I,E[5].disabled=!o.hasUnmarked(),E[6].disabled=f,E[7].disabled=f;var D=z[3].list;D[1].disabled=f,D[2].disabled=f;var S=z[4].list;if(z[4].disabled=p,d){if(S[3].disabled=!1,d.selected)S[4].disabled=!0,S[5].disabled=!0,C.forEach(function(e){e.mark?S[5].disabled=!1:S[4].disabled=!1});else{var L=!d.mark;S[4].disabled=!L,S[5].disabled=L}if(d.selected){var k=C.length;S[0].disabled=!C.filter(M.canAbort).length,S[1].disabled=!k,S[2].disabled=!k||k>1}else S[0].disabled=!M.canAbort(d),S[1].disabled=!1,S[2].disabled=!1}else S[0].disabled=!0,S[1].disabled=!0,S[2].disabled=!0,S[3].disabled=!0,S[4].disabled=!0;var j=z[5],U=j.list;if(j.hide=!o.isTreeView,j.disabled=!g&&!I,g){var B=g.data,R=g.expand;this.treeTarget=u,this.isTreeLeafNode=B,U[0].disabled=R||B,U[1].disabled=!R||B,U[2].disabled=B,U[3].disabled=B;var Q=(g.parent||o.root).children.length;E[2].disabled=1>=Q}else o.isTreeView&&(U[0].disabled=!0,U[1].disabled=!0,U[2].disabled=!I,U[3].disabled=!I);var O=z[6],H=z[7],F=z[11];H.list.forEach(function(e,t){e.disabled=p&&(t<H.list.length-1||!g)}),O.disabled=p,O.hide=T.hideMockMenu,z[10].disabled=f&&!N,z[8].disabled=f&&!N,H.disabled=f,H.hide=!T.whistleId,M.addPluginMenus(F,T.getNetworkMenus(),(j.hide?9:10)+(H.hide?0:1)-(O.hide?1:0),p,u,d&&d.url);var V=(j.hide?370:400)-(H.hide?30:0)-(F.hide?30:0)-(O.hide?30:0);F.maxHeight=V;var P=M.getMenuPosition(e,110,V);P.list=z,P.className=P.marginRight<360?\"w-ctx-menu-left\":\"\",this.refs.contextMenu.show(P)},updateList:function(){this.refs.content.refs.list.forceUpdateGrid(),y.trigger(\"checkAtBottom\")},onFilterChange:function(e){var t=this,n=U.test(e);clearTimeout(t.networkStateChangeTimer),!n&&t.props.modal.search(e),t.networkStateChangeTimer=setTimeout(function(){n&&t.props.modal.search(e),t.setState({filterText:e},t.updateList),y.trigger(\"networkStateChange\")},600)},onFilterTypeChange:function(e){var t,n=this,r=n.container;if(r){r=r.find(\".ReactVirtualized__Grid:first\");var o=r.find(\".ReactVirtualized__Grid__innerScrollContainer\")[0];if(o){var i=r[0].offsetHeight+5,a=o.offsetHeight;t=i>=a||r[0].scrollTop+i>=a}else t=!0}n.props.modal.setFilterType(e),n.setState({filterType:e},function(){n.updateList(),t?(n.autoRefresh(),n._scrollTimer=setTimeout(n.autoRefresh,30)):clearTimeout(n._scrollTimer)})},onFilterKeyDown:function(e){13===e.keyCode&&j.test(e.target.value)&&(T.setDumpCount(parseInt(RegExp.$1,10)),this.props.modal.clear(),this.refs.filterInput.clearFilterText())},autoRefresh:function(){this.container.find(\".ReactVirtualized__Grid:first\")[0].scrollTop=1e8,this.hideBackBtn()},orderBy:function(e){var t=this.willResort&&m(e.target).closest(\"th\")[0];if(t){var n,r=t.className;\"order\"==r?(L={},k={}):(n=L[r],k[r]=t.getAttribute(\"data-key\"),\"desc\"==n?L[r]=\"asc\":\"asc\"==n?L[r]=null:L[r]=\"desc\");var o=[];Object.keys(L).forEach(function(e){(n=L[e])&&o.push({name:e,order:n,key:k[e]})}),this.props.modal.setSortColumns(o),this.setState({})}},onColumnsResort:function(){this.setState({columns:w.getSelectedColumns()})},onMouseDown:function(e){this.willResort=\"w-header-drag-block\"!==e.target.className},onReplay:function(e){if(e.metaKey||e.ctrlKey)if(13===e.keyCode){if(!M.hasShortcut(e.shiftKey?\"replaySelectedRequestsTimes\":\"replaySelectedRequests\"))return;y.trigger(\"replaySessions\",[null,e.shiftKey])}else if(65===e.keyCode){if(!M.hasShortcut(\"abortRequest\"))return;e.preventDefault(),y.trigger(\"abortRequest\")}},renderColumn:function(e){var t=e.name,n=r(e);L[t]&&(n.color=\"var(--c-link)\");var o;return o=\"custom1\"===t||\"custom2\"===t?T[t]:e.title,g.createElement(\"th\",p({onMouseDown:this.onMouseDown},this.state.dragger,{\"data-name\":t,draggable:!0,key:t,\"data-key\":e.key,className:e.className,style:n}),o,g.createElement(O,{order:L[t]}))},scrollToRow:function(e,t){if(e&&(e.id||e.data&&e.data.id)){var n=this.getVisibleList().indexOf(e);if(-1===n)return;e=n+(t>0?t:0)}try{this.refs.content.refs.list.scrollToRow(e)}catch(r){}this.container.focus()},getTreeNode:function(e){var t=this.props.modal;if(\"string\"==typeof e)return t.getTreeNode(e);var n=m(e.target).closest(\".w-req-data-item\");return t.getTreeNode(n.attr(\"data-tree\"))},toggleNode:function(e){var t=this.getTreeNode(e);t&&(t.expand?M.collapse(t):M.expand(t),this.setState({}))},expandAll:function(e){if(!e){var t=this.props.modal.getTree();return t.children.forEach(M.expandAll),this.setState({})}var n=this.getTreeNode(e);n&&(M.expandAll(n),this.setState({}))},collapseAll:function(e){if(!e){var t=this.props.modal.getTree();return t.children.forEach(M.collapseAll),this.setState({})}var n=this.getTreeNode(e);n&&(M.collapseAll(n),this.setState({}))},saveSessions:function(e){var t=this;if(!(t._pendingSave||e&&\"click\"!==e.type&&13!==e.keyCode)){var n=t._sessionsList;t._pendingSave=!0,T.saveSessions(JSON.stringify({filename:t.state.sessionsName.trim(),sessions:n}),function(e,n){return t._pendingSave=!1,e?e.em?N.error(e.em):(m(E(t.refs.saveSessions)).modal(\"hide\"),t._sessionsList=null,t.setState({sessionsName:\"\"}),y.trigger(\"shakeSavedTab\"),y.trigger(\"savedSessionsChanged\"),void N.success(\"Sessions saved successfully\")):M.showSystemError(n)})}},preventBlur:function(e){\"INPUT\"!=e.target.nodeName&&e.preventDefault()},renderTreeNode:function(e,t){var n=this.state.draggable,r=t.style,o=e.data,a=o?i(o):\"\",s=e.value;return r.marginLeft=32*e.depth,g.createElement(\"tr\",{key:o?o.id:e.path,style:r,className:\"w-req-data-item tree-node\"+(o?\" tree-leaf\":\"\")+(a?\" \"+a:\"\"),\"data-id\":o&&o.id,\"data-tree\":e.path,draggable:o&&n,onClick:o?null:this.toggleNode,title:o?M.getUrl(o.url):s,onKeyDown:function(){}},o?u(o,a):g.createElement(I,{name:\"triangle-\"+(e.expand?\"bottom\":\"right\"),className:\"icon-fold\"}),s.length>320?s.substring(0,320)+\"...\":s)},enableRecord:function(){y.trigger(\"enableRecord\")},getVisibleList:function(){var e=this.props.modal;return e.isTreeView?e.getTree().list.filter(s):e.getList().filter(a)},filterSessionsName:function(e){this.setState({sessionsName:M.formatFilename(e.target.value)})},showViewInspectorsBtn:function(e){var t=this._curComposerReqId;t&&t.visible!==e&&(y.trigger(\"showViewInspectorsBtn\",e),t.visible=e)},render:function(){var e=this,t=this.state,n=e.props.modal,r=n.isTreeView,o=this.getVisibleList(),i=n.hasKeyword(),a=t.draggable,s=t.columns.list,l=t.columns.style,c=(t.filterText||\"\").trim(),u=t.record,d=\"-\"===C.get(\"urlType\"),p=e._curComposerReqId,f=o.length,m=!f;return e.startIndex=null,e.endIndex=null,e.visibleList=o,m&&e.showViewInspectorsBtn(!1),g.createElement(\"div\",{className:\"fill w-req-data-con v-box\"+(e.props.hide?\" hide\":\"\")},g.createElement(\"div\",{className:\"w-req-data-ctn fill v-box\",style:l},u?g.createElement(\"div\",{className:\"w-record-status\"},\"stop\"===u?\"Recording stopped\":\"Recording paused\",g.createElement(\"button\",{className:\"btn btn-primary\",onClick:e.enableRecord},\"Enable\")):null,g.createElement(\"div\",{className:\"w-req-data-headers\"+(r?\" hide\":\"\")},g.createElement(\"table\",{className:\"table\"},g.createElement(\"thead\",null,g.createElement(\"tr\",{onClick:e.orderBy,onContextMenu:e.onHeadCtxMenu},g.createElement(\"th\",{className:\"order\",\"data-name\":\"order\"},\"#\"),s.map(e.renderColumn))))),g.createElement(\"div\",{ref:\"container\",tabIndex:\"0\",onContextMenu:e.onContextMenu,onKeyDown:e.onReplay,style:{background:T.hashFilterObj||c||t.filterType?\"var(--b-filtered)\":void 0},className:\"w-req-data-list fill\"+(r?\" w-tree-view-list\":\"\"),onDragStart:e.onDragStart},g.createElement(h.AutoSizer,{ref:\"content\"},function(t){return g.createElement(h.List,{ref:\"list\",rowHeight:r?D:28,width:t.width,height:t.height,rowCount:f,rowRenderer:function(t){var n=t.index,l=o[n];if(p&&l.id===p.reqId&&(m=!0,e.showViewInspectorsBtn(!0)),r)return null==e.startIndex&&(e.startIndex=n),e.endIndex=n,e.renderTreeNode(l,t);var c=i?n+1:l.order;return!m&&n===f-1&&e.showViewInspectorsBtn(!1),g.createElement(H,{style:t.style,key:t.key,order:c,columnList:s,draggable:a,notQuery:d,item:l})}})})),g.createElement(\"div\",{className:\"w-back-to-the-bottom\"+(r?\" hide\":\"\"),ref:\"backBtn\",onClick:this.autoRefresh},g.createElement(I,{name:\"arrow-down\"}),\"Back to the bottom\")),g.createElement(v,{ref:\"filterInput\",onKeyDown:this.onFilterKeyDown,onChange:this.onFilterChange,wStyle:l,addonHints:R,onFilterTypeChange:this.onFilterTypeChange,hintKey:\"networkHintList\"}),g.createElement(b,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}),g.createElement(b,{onClick:this.onClickHeadMenu,ref:\"headContextMenu\"}),g.createElement(A,{ref:\"qrcodeDialog\"}),g.createElement(\"div\",{ref:\"saveSessions\",className:\"modal fade w-choose-filte-type\"},g.createElement(\"div\",{className:\"modal-dialog\"},g.createElement(\"div\",{className:\"modal-content\"},g.createElement(\"div\",{className:\"modal-body\"},g.createElement(\"label\",{className:\"w-choose-filte-type-label w-save-sessions-label\"},\"Save as:\",g.createElement(\"input\",{ref:\"sessionsName\",onChange:this.filterSessionsName,onKeyDown:this.saveSessions,placeholder:\"Enter filename (optional)\",className:\"form-control\",maxLength:B,value:t.sessionsName||\"\"}))),g.createElement(\"div\",{className:\"modal-footer\"},g.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),g.createElement(\"button\",{type:\"button\",onKeyDown:this.saveSessions,tabIndex:\"0\",onMouseDown:this.preventBlur,className:\"btn btn-primary\",onClick:this.saveSessions},\"Save\"))))))}});e.exports=F},function(e,t,n){var r=n(517);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-req-data-con{overflow-x:auto}.w-req-data-ctn{overflow-x:auto;overflow-y:hidden;position:relative}.w-req-data-ctn .order{width:50px;position:relative}.w-req-data-ctn .order .glyphicon{font-size:6px;position:absolute;top:2px;left:1px;font-weight:400!important}.w-req-data-ctn .date{width:150px}.w-req-data-ctn .result{width:65px}.w-req-data-ctn .protocol{width:92px}.w-req-data-ctn .method{width:75px}.w-req-data-ctn .dns,.w-req-data-ctn .time{width:70px}.w-req-data-ctn .clientPort,.w-req-data-ctn .serverPort{width:90px}.w-req-data-ctn .type{width:125px}.w-req-data-ctn .clientIp,.w-req-data-ctn .hostIp{width:110px}.w-req-data-ctn .hostname{width:150px}.w-req-data-ctn .body,.w-req-data-ctn .contentEncoding,.w-req-data-ctn .download,.w-req-data-ctn .request,.w-req-data-ctn .response{width:90px}.w-req-data-headers{height:29px;border-bottom:1px solid var(--c-border);overflow:hidden}.w-req-data-headers .table{height:30px}.w-req-data-headers .table th{border:none!important;padding:2px 0 6px 6px;-webkit-user-select:none;user-select:none;font-weight:400;color:var(--c-default);position:relative;cursor:pointer;background-color:var(--b-bar)}.w-req-data-headers .table th:last-child{box-sizing:content-box}.w-req-data-headers .table th:hover{background-color:var(--b-btn-hover)!important}.w-req-data-list{overflow:hidden;outline:0}.w-req-data-list td,.w-req-data-list th{border-top:none!important;border-bottom:1px solid var(--c-border);font-weight:400;font-size:9pt;padding:3px 0 3px 6px!important;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:21px!important}.w-req-data-con .w-filter-con,.w-req-data-ctn{min-width:820px}.w-left-menu,.w-menu,.w-req-data-con .w-filter-con,.w-req-data-ctn{-moz-user-select:none;-webkit-user-select:none;user-select:none}.w-req-data-list .w-tunnel td,.w-req-data-list .w-tunnel th{color:var(--c-gray)}.w-req-data-list .tree-leaf.w-has-rules,.w-req-data-list .w-has-rules td,.w-req-data-list .w-has-rules th{font-weight:500;color:var(--c-has)}.w-req-data-list .tree-leaf.w-has-local,.w-req-data-list .w-has-local td,.w-req-data-list .w-has-local th{font-weight:500;color:var(--c-default)}.w-req-data-list .w-error-status td,.w-req-data-list .w-error-status th{color:var(--c-danger)}.w-req-data-list .w-forbidden td,.w-req-data-list .w-forbidden th{color:var(--c-forbidden)}.w-req-data-list tr{cursor:default;background:var(--b-default)}.w-req-data-list .w-hover-body tr.w-selected td,.w-req-data-list .w-hover-body tr.w-selected th{background:var(--c-link)!important;color:var(--c-active)!important}.w-req-data-list .tree-leaf .glyphicon{color:var(--c-thin)!important;font-size:9pt;display:inline-block;margin-right:5px;overflow:hidden;font-weight:400!important}.w-req-data-list .tree-leaf .w-type-icon{transform:scale(0.8);display:inline-block;padding-right:3px;font-weight:400!important;color:var(--c-thin)!important;margin-left:-4px}.w-req-data-list .tree-leaf.success span{color:var(--c-tree-success)!important}.w-req-data-list .tree-leaf.info span{color:var(--c-tree-info)!important}.w-req-data-list .tree-leaf.warning span{color:var(--c-tree-warning)!important}.w-req-data-list .tree-leaf.active span{color:var(--c-link)!important}.w-req-data-list .tree-leaf.w-error-status,.w-req-data-list .tree-leaf.w-error-status span{color:var(--c-danger)!important}.w-req-data-list .tree-leaf.w-forbidden span{color:var(--c-tree-forbidden)!important}.w-req-data-list .tree-leaf.w-mark{color:var(--c-tree-mark)!important}.w-req-data-list .tree-leaf.danger span{color:var(--c-tree-danger)!important}.w-qrcode-dialog .modal-dialog{width:350px}.w-qrcode-dialog .close{position:absolute;top:10px;right:10px;height:21px}.w-qrcode-dialog h4{margin:5px 0 0;font-size:15px;font-weight:400}.w-qrcode-dialog canvas{width:20pc;height:20pc}.w-qrcode-url-wrap{display:flex;align-items:center;width:20pc}.w-qrcode-url-wrap input{flex:1}.w-qrcode-url-wrap span,.w-root-ca-url-wrap span{font-size:18px;cursor:pointer;margin-left:10px;color:var(--c-thin)}.w-qrcode-dialog input{border:1px solid var(--c-border);padding:3px;display:block;width:20pc;overflow:hidde;margin:15px 0 10px}.w-qrcode-dialog .modal-dialog .modal-body,.w-qrcode-dialog .modal-dialog .modal-footer{padding:10px 15px}.w-ctx-menu li[data-menu-action=Plugins] ul{overflow-x:hidden;overflow-y:auto;max-height:310px}.w-ctx-menu li[data-menu-action=Plugins] li{max-width:260px;overflow:hidden;text-overflow:ellipsis}.w-ctx-menu li[data-menu-action=Modify] ul{top:-61px!important}.w-spinner{position:absolute;top:-9px;right:-2px;font-size:9pt;transform:scale(0.625,0.625);overflow:hidden;padding:11px 4px 10px 0}.w-spinner span{display:block}.w-spinner .glyphicon-triangle-bottom.spinner-asc,.w-spinner .glyphicon-triangle-top.spinner-desc{visibility:hidden}.w-header-drag-block{position:absolute;top:0;right:-2px;height:28px;width:9pt;cursor:col-resize;z-index:1}.tree-node{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:24px;padding:0 10px;font-size:9pt;outline:0}.tree-node:hover{background:var(--b-btn-hover)}.tree-node.w-selected{background:var(--b-tree-active);color:var(--c-active)}.highlight,.w-highlight td,.w-highlight th{animation-name:blink;animation-iteration-count:1;animation-duration:1s;animation-timing-function:linear}@keyframes blink{70%{background-color:var(--b-blink)}}.icon-fold{font-size:10px;opacity:.5;padding:10px;margin:-10px;margin-right:-6px;transform:scale(0.8)}.w-tree-view-list>div>.ReactVirtualized__Grid{overflow:auto!important}.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer{overflow:auto!important;width:100%!important;overflow-y:hidden!important}.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer tr{width:auto!important;white-space:nowrap}.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer::-webkit-scrollbar{width:10px;height:0}.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer::-webkit-scrollbar-thumb{border-radius:8px}.w-record-status{line-height:28px;font-size:14px;text-align:center;background-color:var(--b-warn);border-bottom:1px solid var(--c-border);color:var(--c-error)}.w-record-status .btn{font-size:9pt;line-height:20px;height:22px;margin-left:20px;padding:0 8px;margin-top:-2px}.w-pr td,.w-pr th{font-style:italic;font-weight:400!important}.w-back-to-the-bottom{position:absolute;bottom:5px;right:13px;font-size:11px;color:var(--c-link);padding:5px 8px;background-color:var(--b-default);border:1px solid var(--c-border);border-radius:20px;opacity:.85;display:none}.w-back-to-the-bottom span{margin-right:3px}.w-back-to-the-bottom:hover{opacity:1}.w-save-sessions-label .form-control{width:350px!important}\",\"\"])},function(e,t,n){var r=n(519);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".ReactVirtualized__Table__headerRow{font-weight:700;text-transform:uppercase}.ReactVirtualized__Table__headerRow,.ReactVirtualized__Table__row{display:flex;flex-direction:row;align-items:center}.ReactVirtualized__Table__headerTruncatedText{display:inline-block;max-width:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.ReactVirtualized__Table__headerColumn,.ReactVirtualized__Table__rowColumn{margin-right:10px;min-width:0}.ReactVirtualized__Table__rowColumn{text-overflow:ellipsis;white-space:nowrap}.ReactVirtualized__Table__headerColumn:first-of-type,.ReactVirtualized__Table__rowColumn:first-of-type{margin-left:10px}.ReactVirtualized__Table__sortableHeaderColumn{cursor:pointer}.ReactVirtualized__Table__sortableHeaderIconContainer{display:flex;align-items:center}.ReactVirtualized__Table__sortableHeaderIcon{flex:0 0 24px;height:1em;width:1em;fill:currentColor}\",\"\"])},function(e,t,n){(function(e){!function(e,r){r(t,n(24),n(57))}(this,function(t,n,r){\"use strict\";function o(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}function i(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,\"value\"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function a(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(){return(l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function u(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?c(n,!0).forEach(function(t){s(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):c(n).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function d(e,t){if(\"function\"!=typeof t&&null!==t)throw new TypeError(\"Super expression must either be null or a function\");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&h(e,t)}function p(e){return(p=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function h(e,t){return(h=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function g(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],0<=t.indexOf(n)||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],0<=t.indexOf(n)||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function f(e){if(void 0===e)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return e}function m(e,t){return!t||\"object\"!=typeof t&&\"function\"!=typeof t?f(e):t}function A(e,t){return function(e){return Array.isArray(e)?e:void 0}(e)||function(e,t){if(Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e)){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(l){o=!0,i=l}finally{try{r||null==s[\"return\"]||s[\"return\"]()}finally{if(o)throw i}}return n}}(e,t)||function(){throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}()}function M(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}(e)||function(e){return Symbol.iterator in Object(e)||\"[object Arguments]\"===Object.prototype.toString.call(e)?Array.from(e):void 0}(e)||function(){throw new TypeError(\"Invalid attempt to spread non-iterable instance\")}()}function w(){var e=this.constructor.getDerivedStateFromProps(this.props,this.state);null!=e&&this.setState(e)}function v(e){this.setState(function(t){var n=this.constructor.getDerivedStateFromProps(e,t);return null!=n?n:null}.bind(this))}function b(e,t){try{var n=this.props,r=this.state;this.props=e,this.state=t,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(n,r)}finally{this.props=n,this.state=r}}function y(e){var t=e.prototype;if(!t||!t.isReactComponent)throw new Error(\"Can only polyfill class components\");if(\"function\"!=typeof e.getDerivedStateFromProps&&\"function\"!=typeof t.getSnapshotBeforeUpdate)return e;var n=null,r=null,o=null;if(\"function\"==typeof t.componentWillMount?n=\"componentWillMount\":\"function\"==typeof t.UNSAFE_componentWillMount&&(n=\"UNSAFE_componentWillMount\"),\"function\"==typeof t.componentWillReceiveProps?r=\"componentWillReceiveProps\":\"function\"==typeof t.UNSAFE_componentWillReceiveProps&&(r=\"UNSAFE_componentWillReceiveProps\"),\"function\"==typeof t.componentWillUpdate?o=\"componentWillUpdate\":\"function\"==typeof t.UNSAFE_componentWillUpdate&&(o=\"UNSAFE_componentWillUpdate\"),null!==n||null!==r||null!==o){var i=e.displayName||e.name,a=\"function\"==typeof e.getDerivedStateFromProps?\"getDerivedStateFromProps()\":\"getSnapshotBeforeUpdate()\";throw Error(\"Unsafe legacy lifecycles will not be called for components using new component APIs.\\n\\n\"+i+\" uses \"+a+\" but also contains the following legacy lifecycles:\"+(null!==n?\"\\n  \"+n:\"\")+(null!==r?\"\\n  \"+r:\"\")+(null!==o?\"\\n  \"+o:\"\")+\"\\n\\nThe above lifecycles should be removed. Learn more about this warning here:\\nhttps://fb.me/react-async-component-lifecycle-hooks\")}if(\"function\"==typeof e.getDerivedStateFromProps&&(t.componentWillMount=w,t.componentWillReceiveProps=v),\"function\"==typeof t.getSnapshotBeforeUpdate){if(\"function\"!=typeof t.componentDidUpdate)throw new Error(\"Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype\");t.componentWillUpdate=b;var s=t.componentDidUpdate;t.componentDidUpdate=function(e,t,n){var r=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:n;s.call(this,e,t,r)}}return e}function x(t,n){var r,o,i,a=void 0!==(r=void 0!==n?n:\"undefined\"!=typeof window?window:\"undefined\"!=typeof self?self:e).document&&r.document.attachEvent;if(!a){var s=(i=r.requestAnimationFrame||r.mozRequestAnimationFrame||r.webkitRequestAnimationFrame||function(e){return r.setTimeout(e,20)},function(e){return i(e)}),l=(o=r.cancelAnimationFrame||r.mozCancelAnimationFrame||r.webkitCancelAnimationFrame||r.clearTimeout,function(e){return o(e)}),c=function(e){var t=e.__resizeTriggers__,n=t.firstElementChild,r=t.lastElementChild,o=n.firstElementChild;r.scrollLeft=r.scrollWidth,r.scrollTop=r.scrollHeight,o.style.width=n.offsetWidth+1+\"px\",o.style.height=n.offsetHeight+1+\"px\",n.scrollLeft=n.scrollWidth,n.scrollTop=n.scrollHeight},u=function(e){if(!(e.target.className&&\"function\"==typeof e.target.className.indexOf&&e.target.className.indexOf(\"contract-trigger\")<0&&e.target.className.indexOf(\"expand-trigger\")<0)){var t=this;c(this),this.__resizeRAF__&&l(this.__resizeRAF__),this.__resizeRAF__=s(function(){!function(e){return e.offsetWidth!=e.__resizeLast__.width||e.offsetHeight!=e.__resizeLast__.height}(t)||(t.__resizeLast__.width=t.offsetWidth,t.__resizeLast__.height=t.offsetHeight,t.__resizeListeners__.forEach(function(n){n.call(t,e)}))})}},d=!1,p=\"\",h=\"animationstart\",g=\"Webkit Moz O ms\".split(\" \"),f=\"webkitAnimationStart animationstart oAnimationStart MSAnimationStart\".split(\" \"),m=r.document.createElement(\"fakeelement\");if(void 0!==m.style.animationName&&(d=!0),!1===d)for(var A=0;A<g.length;A++)if(void 0!==m.style[g[A]+\"AnimationName\"]){p=\"-\"+g[A].toLowerCase()+\"-\",h=f[A],d=!0;break}var M=\"resizeanim\",w=\"@\"+p+\"keyframes \"+M+\" { from { opacity: 0; } to { opacity: 0; } } \",v=p+\"animation: 1ms \"+M+\"; \"}return{addResizeListener:function(e,n){if(a)e.attachEvent(\"onresize\",n);else{if(!e.__resizeTriggers__){var o=e.ownerDocument,i=r.getComputedStyle(e);i&&\"static\"==i.position&&(e.style.position=\"relative\"),function(e){if(!e.getElementById(\"detectElementResize\")){var n=(w||\"\")+\".resize-triggers { \"+(v||\"\")+'visibility: hidden; opacity: 0; } .resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }',r=e.head||e.getElementsByTagName(\"head\")[0],o=e.createElement(\"style\");o.id=\"detectElementResize\",o.type=\"text/css\",null!=t&&o.setAttribute(\"nonce\",t),o.styleSheet?o.styleSheet.cssText=n:o.appendChild(e.createTextNode(n)),r.appendChild(o)}}(o),e.__resizeLast__={},e.__resizeListeners__=[],(e.__resizeTriggers__=o.createElement(\"div\")).className=\"resize-triggers\";var s='<div class=\"expand-trigger\"><div></div></div><div class=\"contract-trigger\"></div>';if(window.trustedTypes){var l=trustedTypes.createPolicy(\"react-virtualized-auto-sizer\",{createHTML:function(){return s}});e.__resizeTriggers__.innerHTML=l.createHTML(\"\")}else e.__resizeTriggers__.innerHTML=s;e.appendChild(e.__resizeTriggers__),c(e),e.addEventListener(\"scroll\",u,!0),h&&(e.__resizeTriggers__.__animationListener__=function(t){t.animationName==M&&c(e)},e.__resizeTriggers__.addEventListener(h,e.__resizeTriggers__.__animationListener__))}e.__resizeListeners__.push(n)}},removeResizeListener:function(e,t){if(a)e.detachEvent(\"onresize\",t);else if(e.__resizeListeners__.splice(e.__resizeListeners__.indexOf(t),1),!e.__resizeListeners__.length){e.removeEventListener(\"scroll\",u,!0),e.__resizeTriggers__.__animationListener__&&(e.__resizeTriggers__.removeEventListener(h,e.__resizeTriggers__.__animationListener__),e.__resizeTriggers__.__animationListener__=null);try{e.__resizeTriggers__=!e.removeChild(e.__resizeTriggers__)}catch(n){}}}}}function T(e,t){return\"\".concat(e,\"-\").concat(t)}function C(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,\"default\")?e[\"default\"]:e}function N(e,t){return e(t={exports:{}},t.exports),t.exports}function I(e,t,n,r,o){for(var i in e)if(Ee(e,i)){var a;try{if(\"function\"!=typeof e[i]){var s=Error((r||\"React class\")+\": \"+n+\" type `\"+i+\"` is invalid; it must be a function, usually from the `prop-types` package, but received `\"+typeof e[i]+\"`.\");throw s.name=\"Invariant Violation\",s}a=e[i](t,i,r,n,null,\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\")}catch(l){a=l}if(!a||a instanceof Error||ve((r||\"React class\")+\": type specification of \"+n+\" `\"+i+\"` is invalid; the type checker function must return `null` or an `Error` but returned a \"+typeof a+\". You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).\"),a instanceof Error&&!(a.message in Ie)){Ie[a.message]=!0;var c=o?o():\"\";ve(\"Failed \"+n+\" type: \"+a.message+(null!=c?c:\"\"))}}}function E(){return null}function D(e,t){function n(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}function r(e){this.message=e,this.stack=\"\"}function o(e){function n(n,a,s,l,c,u,d){if(l=l||p,u=u||s,d!==Ne){if(t){var h=new Error(\"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types\");throw h.name=\"Invariant Violation\",h}if(\"undefined\"!=typeof console){var g=l+\":\"+s;!o[g]&&3>i&&(De(\"You are manually calling a React.PropTypes validation function for the `\"+u+\"` prop on `\"+l+\"`. This is deprecated and will throw in the standalone `prop-types` package. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.\"),o[g]=!0,i++)}}return null==a[s]?n?new r(null===a[s]?\"The \"+c+\" `\"+u+\"` is marked as required in `\"+l+\"`, but its value is `null`.\":\"The \"+c+\" `\"+u+\"` is marked as required in `\"+l+\"`, but its value is `undefined`.\"):null:e(a,s,l,c,u)}var o={},i=0,a=n.bind(null,!1);return a.isRequired=n.bind(null,!0),a}function i(e){return o(function(t,n,o,i,a,c){var u=t[n];return s(u)===e?null:new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+l(u)+\"` supplied to `\"+o+\"`, expected `\"+e+\"`.\")})}function a(t){switch(typeof t){case\"number\":case\"string\":case\"undefined\":return!0;case\"boolean\":return!t;case\"object\":if(Array.isArray(t))return t.every(a);if(null===t||e(t))return!0;var n=function(e){var t=e&&(u&&e[u]||e[d]);return\"function\"==typeof t?t:void 0}(t);if(!n)return!1;var r,o=n.call(t);if(n!==t.entries){for(;!(r=o.next()).done;)if(!a(r.value))return!1}else for(;!(r=o.next()).done;){var i=r.value;if(i&&!a(i[1]))return!1}return!0;default:return!1}}function s(e){var t=typeof e;return Array.isArray(e)?\"array\":e instanceof RegExp?\"object\":function(e,t){\nreturn\"symbol\"===e||!!t&&(\"Symbol\"===t[\"@@toStringTag\"]||\"function\"==typeof Symbol&&t instanceof Symbol)}(t,e)?\"symbol\":t}function l(e){if(null==e)return\"\"+e;var t=s(e);if(\"object\"===t){if(e instanceof Date)return\"date\";if(e instanceof RegExp)return\"regexp\"}return t}function c(e){var t=l(e);switch(t){case\"array\":case\"object\":return\"an \"+t;case\"boolean\":case\"date\":case\"regexp\":return\"a \"+t;default:return t}}var u=\"function\"==typeof Symbol&&Symbol.iterator,d=\"@@iterator\",p=\"<<anonymous>>\",h={array:i(\"array\"),bool:i(\"boolean\"),func:i(\"function\"),number:i(\"number\"),object:i(\"object\"),string:i(\"string\"),symbol:i(\"symbol\"),any:o(E),arrayOf:function(e){return o(function(t,n,o,i,a){if(\"function\"!=typeof e)return new r(\"Property `\"+a+\"` of component `\"+o+\"` has invalid PropType notation inside arrayOf.\");var l=t[n];if(!Array.isArray(l))return new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+s(l)+\"` supplied to `\"+o+\"`, expected an array.\");for(var c=0;c<l.length;c++){var u=e(l,c,o,i,a+\"[\"+c+\"]\",Ne);if(u instanceof Error)return u}return null})},element:o(function(t,n,o,i,a){var l=t[n];return e(l)?null:new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+s(l)+\"` supplied to `\"+o+\"`, expected a single ReactElement.\")}),elementType:o(function(e,t,n,o,i){var a=e[t];return be.isValidElementType(a)?null:new r(\"Invalid \"+o+\" `\"+i+\"` of type `\"+s(a)+\"` supplied to `\"+n+\"`, expected a single ReactElement type.\")}),instanceOf:function(e){return o(function(t,n,o,i,a){if(t[n]instanceof e)return null;var s=e.name||p;return new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+function(e){return e.constructor&&e.constructor.name?e.constructor.name:p}(t[n])+\"` supplied to `\"+o+\"`, expected instance of `\"+s+\"`.\")})},node:o(function(e,t,n,o,i){return a(e[t])?null:new r(\"Invalid \"+o+\" `\"+i+\"` supplied to `\"+n+\"`, expected a ReactNode.\")}),objectOf:function(e){return o(function(t,n,o,i,a){if(\"function\"!=typeof e)return new r(\"Property `\"+a+\"` of component `\"+o+\"` has invalid PropType notation inside objectOf.\");var l=t[n],c=s(l);if(\"object\"!==c)return new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+c+\"` supplied to `\"+o+\"`, expected an object.\");for(var u in l)if(Le(l,u)){var d=e(l,u,o,i,a+\".\"+u,Ne);if(d instanceof Error)return d}return null})},oneOf:function(e){return Array.isArray(e)?o(function(t,o,i,a,s){for(var c=t[o],u=0;u<e.length;u++)if(n(c,e[u]))return null;var d=JSON.stringify(e,function(e,t){return\"symbol\"===l(t)?String(t):t});return new r(\"Invalid \"+a+\" `\"+s+\"` of value `\"+String(c)+\"` supplied to `\"+i+\"`, expected one of \"+d+\".\")}):(De(1<arguments.length?\"Invalid arguments supplied to oneOf, expected an array, got \"+arguments.length+\" arguments. A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).\":\"Invalid argument supplied to oneOf, expected an array.\"),E)},oneOfType:function(e){if(!Array.isArray(e))return De(\"Invalid argument supplied to oneOfType, expected an instance of array.\"),E;for(var t=0;t<e.length;t++){var n=e[t];if(\"function\"!=typeof n)return De(\"Invalid argument supplied to oneOfType. Expected an array of check functions, but received \"+c(n)+\" at index \"+t+\".\"),E}return o(function(t,n,o,i,a){for(var s=0;s<e.length;s++)if(null==e[s](t,n,o,i,a,Ne))return null;return new r(\"Invalid \"+i+\" `\"+a+\"` supplied to `\"+o+\"`.\")})},shape:function(e){return o(function(t,n,o,i,a){var l=t[n],c=s(l);if(\"object\"!==c)return new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+c+\"` supplied to `\"+o+\"`, expected `object`.\");for(var u in e){var d=e[u];if(d){var p=d(l,u,o,i,a+\".\"+u,Ne);if(p)return p}}return null})},exact:function(e){return o(function(t,n,o,i,a){var l=t[n],c=s(l);if(\"object\"!==c)return new r(\"Invalid \"+i+\" `\"+a+\"` of type `\"+c+\"` supplied to `\"+o+\"`, expected `object`.\");var u=Ce({},t[n],e);for(var d in u){var p=e[d];if(!p)return new r(\"Invalid \"+i+\" `\"+a+\"` key `\"+d+\"` supplied to `\"+o+\"`.\\nBad object: \"+JSON.stringify(t[n],null,\"  \")+\"\\nValid keys: \"+JSON.stringify(Object.keys(e),null,\"  \"));var h=p(l,d,o,i,a+\".\"+d,Ne);if(h)return h}return null})}};return r.prototype=Error.prototype,h.checkPropTypes=Se,h.resetWarningCache=Se.resetWarningCache,h.PropTypes=h}function S(e){var t,n,r=\"\";if(e)if(\"object\"==typeof e)if(e.push)for(t=0;t<e.length;t++)e[t]&&(n=S(e[t]))&&(r&&(r+=\" \"),r+=n);else for(t in e)e[t]&&(n=S(t))&&(r&&(r+=\" \"),r+=n);else\"boolean\"==typeof e||e.call||(r&&(r+=\" \"),r+=e);return r}function L(){for(var e,t=0,n=\"\";t<arguments.length;)(e=S(arguments[t++]))&&(n&&(n+=\" \"),n+=e);return n}function k(e){var t=!(0<arguments.length&&void 0!==e)||e,n={};return function(e){var r=e.callback,o=e.indices,i=Object.keys(o),a=!t||i.every(function(e){var t=o[e];return Array.isArray(t)?0<t.length:t>=0}),s=i.length!==Object.keys(n).length||i.some(function(e){var t=n[e],r=o[e];return Array.isArray(r)?t.join(\",\")!==r.join(\",\"):t!==r});n=o,a&&s&&r(o)}}function j(e){if((!ke&&0!==ke||e)&&Ue){var t=document.createElement(\"div\");t.style.position=\"absolute\",t.style.top=\"-9999px\",t.style.width=\"50px\",t.style.height=\"50px\",t.style.overflow=\"scroll\",document.body.appendChild(t),ke=t.offsetWidth-t.clientWidth,document.body.removeChild(t)}return ke}function U(e){var t=e.align,n=void 0===t?\"auto\":t,r=e.cellOffset,o=e.cellSize,i=e.containerSize,a=e.currentOffset,s=r,l=s-i+o;switch(n){case\"start\":return s;case\"end\":return l;case\"center\":return s-(i-o)/2;default:return Math.max(l,Math.min(s,a))}}function B(e){var t=e.cellCount,n=e.cellSize,r=e.computeMetadataCallback,o=e.computeMetadataCallbackProps,i=e.nextCellsCount,a=e.nextCellSize,s=e.nextScrollToIndex,l=e.scrollToIndex,c=e.updateScrollOffsetForScrollToIndex;t===i&&(\"number\"!=typeof n&&\"number\"!=typeof a||n===a)||(r(o),l>=0&&l===s&&c())}function R(e){var t=e.cellCount,n=e.overscanCellsCount,r=e.scrollDirection,o=e.startIndex,i=e.stopIndex;return 1===r?{overscanStartIndex:Math.max(0,o),overscanStopIndex:Math.min(t-1,i+n)}:{overscanStartIndex:Math.max(0,o-n),overscanStopIndex:Math.min(t-1,i)}}function z(e){var t=e.cellSize,n=e.cellSizeAndPositionManager,r=e.previousCellsCount,o=e.previousCellSize,i=e.previousScrollToAlignment,a=e.previousScrollToIndex,s=e.previousSize,l=e.scrollOffset,c=e.scrollToAlignment,u=e.scrollToIndex,d=e.size,p=e.sizeJustIncreasedFromZero,h=e.updateScrollIndexCallback,g=n.getCellCount(),f=u>=0&&g>u;f&&(d!==s||p||!o||\"number\"==typeof t&&t!==o||c!==i||u!==a)?h(u):!f&&g>0&&(s>d||r>g)&&l>n.getTotalSize()-d&&h(g-1)}function Q(e){for(var t=e.cellCache,n=e.cellRenderer,r=e.columnSizeAndPositionManager,o=e.columnStartIndex,i=e.columnStopIndex,a=e.deferredMeasurementCache,s=e.horizontalOffsetAdjustment,l=e.isScrolling,c=e.isScrollingOptOut,u=e.parent,d=e.rowSizeAndPositionManager,p=e.rowStartIndex,h=e.rowStopIndex,g=e.styleCache,f=e.verticalOffsetAdjustment,m=e.visibleColumnIndices,A=e.visibleRowIndices,M=[],w=r.areOffsetsAdjusted()||d.areOffsetsAdjusted(),v=!l&&!w,b=p;h>=b;b++)for(var y=d.getSizeAndPositionOfCell(b),x=o;i>=x;x++){var T=r.getSizeAndPositionOfCell(x),C=x>=m.start&&x<=m.stop&&b>=A.start&&b<=A.stop,N=\"\".concat(b,\"-\").concat(x),I=void 0;v&&g[N]?I=g[N]:a&&!a.has(b,x)?I={height:\"auto\",left:0,position:\"absolute\",top:0,width:\"auto\"}:(I={height:y.size,left:T.offset+s,position:\"absolute\",top:y.offset+f,width:T.size},g[N]=I);var E={columnIndex:x,isScrolling:l,isVisible:C,key:N,parent:u,rowIndex:b,style:I},D=void 0;null!=(D=!c&&!l||s||f?n(E):(t[N]||(t[N]=n(E)),t[N]))&&!1!==D&&(O(u,D),M.push(D))}return M}function O(e,t){t&&(t.type&&t.type.__internalCellMeasurerFlag&&(t=t.props.children),t&&t.props&&void 0===t.props.style&&!0!==e.__warnedAboutMissingStyle&&(e.__warnedAboutMissingStyle=!0,console.warn(\"Rendered cell should include style property for positioning.\")))}function H(e){var t=e.cellCount,n=e.overscanCellsCount,r=e.scrollDirection,o=e.startIndex,i=e.stopIndex;return n=Math.max(1,n),1===r?{overscanStartIndex:Math.max(0,o-1),overscanStopIndex:Math.min(t-1,i+n)}:{overscanStartIndex:Math.max(0,o-n),overscanStopIndex:Math.min(t-1,i+1)}}function F(e,t,n,r,o){this.mid=e,this.left=t,this.right=n,this.leftPoints=r,this.rightPoints=o,this.count=(t?t.count:0)+(n?n.count:0)+r.length}function V(e,t){e.mid=t.mid,e.left=t.left,e.right=t.right,e.leftPoints=t.leftPoints,e.rightPoints=t.rightPoints,e.count=t.count}function P(e,t){var n=Z(t);e.mid=n.mid,e.left=n.left,e.right=n.right,e.leftPoints=n.leftPoints,e.rightPoints=n.rightPoints,e.count=n.count}function Y(e,t){var n=e.intervals([]);n.push(t),P(e,n)}function G(e,t){var n=e.intervals([]),r=n.indexOf(t);return 0>r?0:(n.splice(r,1),P(e,n),1)}function W(e,t,n){for(var r=0;r<e.length&&e[r][0]<=t;++r){var o=n(e[r]);if(o)return o}}function X(e,t,n){for(var r=e.length-1;r>=0&&e[r][1]>=t;--r){var o=n(e[r]);if(o)return o}}function _(e,t){for(var n=0;n<e.length;++n){var r=t(e[n]);if(r)return r}}function q(e,t){return e-t}function J(e,t){var n=e[0]-t[0];return n||e[1]-t[1]}function K(e,t){var n=e[1]-t[1];return n||e[0]-t[0]}function Z(e){if(0===e.length)return null;for(var t=[],n=0;n<e.length;++n)t.push(e[n][0],e[n][1]);t.sort(q);var r=t[t.length>>1],o=[],i=[],a=[];for(n=0;n<e.length;++n){var s=e[n];s[1]<r?o.push(s):r<s[0]?i.push(s):a.push(s)}var l=a,c=a.slice();return l.sort(J),c.sort(K),new F(r,Z(o),Z(i),l,c)}function $(e){this.root=e}function ee(){}function te(e){var t=e.dataKey,n=e.rowData;return\"function\"==typeof n.get?n.get(t):n[t]}function ne(e){var t=e.cellData;return null==t?\"\":String(t)}function re(e){var t=e.className,r=e.columns,o=e.style;return n.createElement(\"div\",{className:t,role:\"row\",style:o},r)}function oe(e){var t=e.sortDirection,r=L(\"ReactVirtualized__Table__sortableHeaderIcon\",{\"ReactVirtualized__Table__sortableHeaderIcon--ASC\":t===dt.ASC,\"ReactVirtualized__Table__sortableHeaderIcon--DESC\":t===dt.DESC});return n.createElement(\"svg\",{className:r,width:18,height:18,viewBox:\"0 0 24 24\"},t===dt.ASC?n.createElement(\"path\",{d:\"M7 14l5-5 5 5z\"}):n.createElement(\"path\",{d:\"M7 10l5 5 5-5z\"}),n.createElement(\"path\",{d:\"M0 0h24v24H0z\",fill:\"none\"}))}function ie(e){var t=e.dataKey,r=e.label,o=e.sortBy,i=e.sortDirection,a=o===t,s=[n.createElement(\"span\",{className:\"ReactVirtualized__Table__headerTruncatedText\",key:\"label\",title:\"string\"==typeof r?r:null},r)];return a&&s.push(n.createElement(oe,{key:\"SortIndicator\",sortDirection:i})),s}function ae(e){var t=e.className,r=e.columns,o=e.index,i=e.key,a=e.onRowClick,s=e.onRowDoubleClick,c=e.onRowMouseOut,u=e.onRowMouseOver,d=e.onRowRightClick,p=e.rowData,h=e.style,g={\"aria-rowindex\":o+1};return(a||s||c||u||d)&&(g[\"aria-label\"]=\"row\",g.tabIndex=0,a&&(g.onClick=function(e){return a({event:e,index:o,rowData:p})}),s&&(g.onDoubleClick=function(e){return s({event:e,index:o,rowData:p})}),c&&(g.onMouseOut=function(e){return c({event:e,index:o,rowData:p})}),u&&(g.onMouseOver=function(e){return u({event:e,index:o,rowData:p})}),d&&(g.onContextMenu=function(e){return d({event:e,index:o,rowData:p})})),n.createElement(\"div\",l({},g,{className:t,key:i,role:\"row\",style:h}),r)}function se(){mt&&(mt=null,document.body&&null!=ft&&(document.body.style.pointerEvents=ft),ft=null)}function le(){se(),gt.forEach(function(e){return e.__resetIsScrolling()})}function ce(e){e.currentTarget===window&&null==ft&&document.body&&(ft=document.body.style.pointerEvents,document.body.style.pointerEvents=\"none\"),function(){mt&&Je(mt);var e=0;gt.forEach(function(t){e=Math.max(e,t.props.scrollingResetTimeInterval)}),mt=Ke(le,e)}(),gt.forEach(function(t){t.props.scrollElement===e.currentTarget&&t.__handleWindowScrollEvent()})}function ue(e,t){gt.some(function(e){return e.props.scrollElement===t})||t.addEventListener(\"scroll\",ce),gt.push(e)}function de(e,t){(gt=gt.filter(function(t){return t!==e})).length||(t.removeEventListener(\"scroll\",ce),mt&&(Je(mt),se()))}function pe(e,t){if(e){if(At(e)){var n=window,r=n.innerHeight,o=n.innerWidth;return{height:\"number\"==typeof r?r:0,width:\"number\"==typeof o?o:0}}return Mt(e)}return{height:t.serverHeight,width:t.serverWidth}}function he(e){return At(e)&&document.documentElement?{top:\"scrollY\"in window?window.scrollY:document.documentElement.scrollTop,left:\"scrollX\"in window?window.scrollX:document.documentElement.scrollLeft}:{top:e.scrollTop,left:e.scrollLeft}}b.__suppressDeprecationWarning=v.__suppressDeprecationWarning=w.__suppressDeprecationWarning=!0;var ge=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"state\",{scrollToColumn:0,scrollToRow:0,instanceProps:{prevScrollToColumn:0,prevScrollToRow:0}}),s(f(n),\"_columnStartIndex\",0),s(f(n),\"_columnStopIndex\",0),s(f(n),\"_rowStartIndex\",0),s(f(n),\"_rowStopIndex\",0),s(f(n),\"_onKeyDown\",function(e){var t=n.props,r=t.columnCount,o=t.disabled,i=t.mode,a=t.rowCount;if(!o){var s=n._getScrollState(),l=s.scrollToColumn,c=s.scrollToRow,u=n._getScrollState(),d=u.scrollToColumn,p=u.scrollToRow;switch(e.key){case\"ArrowDown\":p=\"cells\"===i?Math.min(p+1,a-1):Math.min(n._rowStopIndex+1,a-1);break;case\"ArrowLeft\":d=\"cells\"===i?Math.max(d-1,0):Math.max(n._columnStartIndex-1,0);break;case\"ArrowRight\":d=\"cells\"===i?Math.min(d+1,r-1):Math.min(n._columnStopIndex+1,r-1);break;case\"ArrowUp\":p=\"cells\"===i?Math.max(p-1,0):Math.max(n._rowStartIndex-1,0)}d===l&&p===c||(e.preventDefault(),n._updateScrollState({scrollToColumn:d,scrollToRow:p}))}}),s(f(n),\"_onSectionRendered\",function(e){var t=e.columnStartIndex,r=e.columnStopIndex,o=e.rowStartIndex,i=e.rowStopIndex;n._columnStartIndex=t,n._columnStopIndex=r,n._rowStartIndex=o,n._rowStopIndex=i}),n}return d(e,n.PureComponent),a(e,[{key:\"setScrollIndexes\",value:function(e){var t=e.scrollToColumn,n=e.scrollToRow;this.setState({scrollToRow:n,scrollToColumn:t})}},{key:\"render\",value:function(){var e=this.props,t=e.className,r=e.children,o=this._getScrollState(),i=o.scrollToColumn,a=o.scrollToRow;return n.createElement(\"div\",{className:t,onKeyDown:this._onKeyDown},r({onSectionRendered:this._onSectionRendered,scrollToColumn:i,scrollToRow:a}))}},{key:\"_getScrollState\",value:function(){return this.props.isControlled?this.props:this.state}},{key:\"_updateScrollState\",value:function(e){var t=e.scrollToColumn,n=e.scrollToRow,r=this.props,o=r.isControlled,i=r.onScrollToChange;\"function\"==typeof i&&i({scrollToColumn:t,scrollToRow:n}),o||this.setState({scrollToColumn:t,scrollToRow:n})}}],[{key:\"getDerivedStateFromProps\",value:function(e,t){return e.isControlled?{}:e.scrollToColumn!==t.instanceProps.prevScrollToColumn||e.scrollToRow!==t.instanceProps.prevScrollToRow?u({},t,{scrollToColumn:e.scrollToColumn,scrollToRow:e.scrollToRow,instanceProps:{prevScrollToColumn:e.scrollToColumn,prevScrollToRow:e.scrollToRow}}):{}}}]),e}();s(ge,\"defaultProps\",{disabled:!1,isControlled:!1,mode:\"edges\",scrollToColumn:0,scrollToRow:0}),y(ge);var fe=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"state\",{height:n.props.defaultHeight||0,width:n.props.defaultWidth||0}),s(f(n),\"_parentNode\",void 0),s(f(n),\"_autoSizer\",void 0),s(f(n),\"_window\",void 0),s(f(n),\"_detectElementResize\",void 0),s(f(n),\"_onResize\",function(){var e=n.props,t=e.disableHeight,r=e.disableWidth,o=e.onResize;if(n._parentNode){var i=n._parentNode.offsetHeight||0,a=n._parentNode.offsetWidth||0,s=(n._window||window).getComputedStyle(n._parentNode)||{},l=parseInt(s.paddingLeft,10)||0,c=parseInt(s.paddingRight,10)||0,u=parseInt(s.paddingTop,10)||0,d=parseInt(s.paddingBottom,10)||0,p=i-u-d,h=a-l-c;(!t&&n.state.height!==p||!r&&n.state.width!==h)&&(n.setState({height:i-u-d,width:a-l-c}),o({height:i,width:a}))}}),s(f(n),\"_setRef\",function(e){n._autoSizer=e}),n}return d(e,n.Component),a(e,[{key:\"componentDidMount\",value:function(){var e=this.props.nonce;this._autoSizer&&this._autoSizer.parentNode&&this._autoSizer.parentNode.ownerDocument&&this._autoSizer.parentNode.ownerDocument.defaultView&&this._autoSizer.parentNode instanceof this._autoSizer.parentNode.ownerDocument.defaultView.HTMLElement&&(this._parentNode=this._autoSizer.parentNode,this._window=this._autoSizer.parentNode.ownerDocument.defaultView,this._detectElementResize=x(e,this._window),this._detectElementResize.addResizeListener(this._parentNode,this._onResize),this._onResize())}},{key:\"componentWillUnmount\",value:function(){this._detectElementResize&&this._parentNode&&this._detectElementResize.removeResizeListener(this._parentNode,this._onResize)}},{key:\"render\",value:function(){var e=this.props,t=e.children,r=e.className,o=e.disableHeight,i=e.disableWidth,a=e.style,s=this.state,l=s.height,c=s.width,d={overflow:\"visible\"},p={};return o||(d.height=0,p.height=l),i||(d.width=0,p.width=c),n.createElement(\"div\",{className:r,ref:this._setRef,style:u({},d,{},a)},t(p))}}]),e}();s(fe,\"defaultProps\",{onResize:function(){},disableHeight:!1,disableWidth:!1,style:{}});var me=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"_child\",void 0),s(f(n),\"_measure\",function(){var e=n.props,t=e.cache,r=e.columnIndex,o=void 0===r?0:r,i=e.parent,a=e.rowIndex,s=void 0===a?n.props.index||0:a,l=n._getCellMeasurements(),c=l.height,u=l.width;c===t.getHeight(s,o)&&u===t.getWidth(s,o)||(t.set(s,o,u,c),i&&\"function\"==typeof i.recomputeGridSize&&i.recomputeGridSize({columnIndex:o,rowIndex:s}))}),s(f(n),\"_registerChild\",function(e){!e||e instanceof Element||console.warn(\"CellMeasurer registerChild expects to be passed Element or null\"),(n._child=e)&&n._maybeMeasureCell()}),n}return d(e,n.PureComponent),a(e,[{key:\"componentDidMount\",value:function(){this._maybeMeasureCell()}},{key:\"componentDidUpdate\",value:function(){this._maybeMeasureCell()}},{key:\"render\",value:function(){var e=this.props.children;return\"function\"==typeof e?e({measure:this._measure,registerChild:this._registerChild}):e}},{key:\"_getCellMeasurements\",value:function(){var e=this.props.cache,t=this._child||r.findDOMNode(this);if(t&&t.ownerDocument&&t.ownerDocument.defaultView&&t instanceof t.ownerDocument.defaultView.HTMLElement){var n=t.style.width,o=t.style.height;e.hasFixedWidth()||(t.style.width=\"auto\"),e.hasFixedHeight()||(t.style.height=\"auto\");var i=Math.ceil(t.offsetHeight),a=Math.ceil(t.offsetWidth);return n&&(t.style.width=n),o&&(t.style.height=o),{height:i,width:a}}return{height:0,width:0}}},{key:\"_maybeMeasureCell\",value:function(){var e=this.props,t=e.cache,n=e.columnIndex,r=void 0===n?0:n,o=e.parent,i=e.rowIndex,a=void 0===i?this.props.index||0:i;if(!t.has(a,r)){var s=this._getCellMeasurements(),l=s.height,c=s.width;t.set(a,r,c,l),o&&\"function\"==typeof o.invalidateCellSizeAfterRender&&o.invalidateCellSizeAfterRender({columnIndex:r,rowIndex:a})}}}]),e}();s(me,\"__internalCellMeasurerFlag\",!1),me.__internalCellMeasurerFlag=!0;var Ae=function(){function e(){var t=this,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};o(this,e),s(this,\"_cellHeightCache\",{}),s(this,\"_cellWidthCache\",{}),s(this,\"_columnWidthCache\",{}),s(this,\"_rowHeightCache\",{}),s(this,\"_defaultHeight\",void 0),s(this,\"_defaultWidth\",void 0),s(this,\"_minHeight\",void 0),s(this,\"_minWidth\",void 0),s(this,\"_keyMapper\",void 0),s(this,\"_hasFixedHeight\",void 0),s(this,\"_hasFixedWidth\",void 0),s(this,\"_columnCount\",0),s(this,\"_rowCount\",0),s(this,\"columnWidth\",function(e){var n=e.index,r=t._keyMapper(0,n);return void 0!==t._columnWidthCache[r]?t._columnWidthCache[r]:t._defaultWidth}),s(this,\"rowHeight\",function(e){var n=e.index,r=t._keyMapper(n,0);return void 0!==t._rowHeightCache[r]?t._rowHeightCache[r]:t._defaultHeight});var r=n.defaultHeight,i=n.defaultWidth,a=n.fixedHeight,l=n.fixedWidth,c=n.keyMapper,u=n.minHeight,d=n.minWidth;this._hasFixedHeight=!0===a,this._hasFixedWidth=!0===l,this._minHeight=u||0,this._minWidth=d||0,this._keyMapper=c||T,this._defaultHeight=Math.max(this._minHeight,\"number\"==typeof r?r:30),this._defaultWidth=Math.max(this._minWidth,\"number\"==typeof i?i:100),!1===this._hasFixedHeight&&!1===this._hasFixedWidth&&console.warn(\"CellMeasurerCache should only measure a cell's width or height. You have configured CellMeasurerCache to measure both. This will result in poor performance.\"),!1===this._hasFixedHeight&&0===this._defaultHeight&&console.warn(\"Fixed height CellMeasurerCache should specify a :defaultHeight greater than 0. Failing to do so will lead to unnecessary layout and poor performance.\"),!1===this._hasFixedWidth&&0===this._defaultWidth&&console.warn(\"Fixed width CellMeasurerCache should specify a :defaultWidth greater than 0. Failing to do so will lead to unnecessary layout and poor performance.\")}return a(e,[{key:\"clear\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0,r=this._keyMapper(e,n);delete this._cellHeightCache[r],delete this._cellWidthCache[r],this._updateCachedColumnAndRowSizes(e,n)}},{key:\"clearAll\",value:function(){this._cellHeightCache={},this._cellWidthCache={},this._columnWidthCache={},this._rowHeightCache={},this._rowCount=0,this._columnCount=0}},{key:\"hasFixedHeight\",value:function(){return this._hasFixedHeight}},{key:\"hasFixedWidth\",value:function(){return this._hasFixedWidth}},{key:\"getHeight\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0;if(this._hasFixedHeight)return this._defaultHeight;var r=this._keyMapper(e,n);return void 0!==this._cellHeightCache[r]?Math.max(this._minHeight,this._cellHeightCache[r]):this._defaultHeight}},{key:\"getWidth\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0;if(this._hasFixedWidth)return this._defaultWidth;var r=this._keyMapper(e,n);return void 0!==this._cellWidthCache[r]?Math.max(this._minWidth,this._cellWidthCache[r]):this._defaultWidth}},{key:\"has\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0,r=this._keyMapper(e,n);return void 0!==this._cellHeightCache[r]}},{key:\"set\",value:function(e,t,n,r){var o=this._keyMapper(e,t);t>=this._columnCount&&(this._columnCount=t+1),e>=this._rowCount&&(this._rowCount=e+1),this._cellHeightCache[o]=r,this._cellWidthCache[o]=n,this._updateCachedColumnAndRowSizes(e,t)}},{key:\"_updateCachedColumnAndRowSizes\",value:function(e,t){if(!this._hasFixedWidth){for(var n=0,r=0;r<this._rowCount;r++)n=Math.max(n,this.getWidth(r,t));var o=this._keyMapper(0,t);this._columnWidthCache[o]=n}if(!this._hasFixedHeight){for(var i=0,a=0;a<this._columnCount;a++)i=Math.max(i,this.getHeight(e,a));var s=this._keyMapper(e,0);this._rowHeightCache[s]=i}}},{key:\"defaultHeight\",get:function(){return this._defaultHeight}},{key:\"defaultWidth\",get:function(){return this._defaultWidth}}]),e}(),Me=N(function(e,t){function n(e){if(\"object\"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case i:switch(e=e.type){case p:case h:case s:case c:case l:case f:return e;default:switch(e=e&&e.$$typeof){case d:case g:case u:return e;default:return t}}case M:case A:case a:return t}}}function r(e){return n(e)===h}Object.defineProperty(t,\"__esModule\",{value:!0});var o=\"function\"==typeof Symbol&&Symbol[\"for\"],i=o?Symbol[\"for\"](\"react.element\"):60103,a=o?Symbol[\"for\"](\"react.portal\"):60106,s=o?Symbol[\"for\"](\"react.fragment\"):60107,l=o?Symbol[\"for\"](\"react.strict_mode\"):60108,c=o?Symbol[\"for\"](\"react.profiler\"):60114,u=o?Symbol[\"for\"](\"react.provider\"):60109,d=o?Symbol[\"for\"](\"react.context\"):60110,p=o?Symbol[\"for\"](\"react.async_mode\"):60111,h=o?Symbol[\"for\"](\"react.concurrent_mode\"):60111,g=o?Symbol[\"for\"](\"react.forward_ref\"):60112,f=o?Symbol[\"for\"](\"react.suspense\"):60113,m=o?Symbol[\"for\"](\"react.suspense_list\"):60120,A=o?Symbol[\"for\"](\"react.memo\"):60115,M=o?Symbol[\"for\"](\"react.lazy\"):60116,w=o?Symbol[\"for\"](\"react.fundamental\"):60117,v=o?Symbol[\"for\"](\"react.responder\"):60118,b=o?Symbol[\"for\"](\"react.scope\"):60119;t.typeOf=n,t.AsyncMode=p,t.ConcurrentMode=h,t.ContextConsumer=d,t.ContextProvider=u,t.Element=i,t.ForwardRef=g,t.Fragment=s,t.Lazy=M,t.Memo=A,t.Portal=a,t.Profiler=c,t.StrictMode=l,t.Suspense=f,t.isValidElementType=function(e){return\"string\"==typeof e||\"function\"==typeof e||e===s||e===h||e===c||e===l||e===f||e===m||\"object\"==typeof e&&null!==e&&(e.$$typeof===M||e.$$typeof===A||e.$$typeof===u||e.$$typeof===d||e.$$typeof===g||e.$$typeof===w||e.$$typeof===v||e.$$typeof===b)},t.isAsyncMode=function(e){return r(e)||n(e)===p},t.isConcurrentMode=r,t.isContextConsumer=function(e){return n(e)===d},t.isContextProvider=function(e){return n(e)===u},t.isElement=function(e){return\"object\"==typeof e&&null!==e&&e.$$typeof===i},t.isForwardRef=function(e){return n(e)===g},t.isFragment=function(e){return n(e)===s},t.isLazy=function(e){return n(e)===M},t.isMemo=function(e){return n(e)===A},t.isPortal=function(e){return n(e)===a},t.isProfiler=function(e){return n(e)===c},t.isStrictMode=function(e){return n(e)===l},t.isSuspense=function(e){return n(e)===f}});C(Me),Me.typeOf,Me.AsyncMode,Me.ConcurrentMode,Me.ContextConsumer,Me.ContextProvider,Me.Element,Me.ForwardRef,Me.Fragment,Me.Lazy,Me.Memo,Me.Portal,Me.Profiler,Me.StrictMode,Me.Suspense,Me.isValidElementType,Me.isAsyncMode,Me.isConcurrentMode,Me.isContextConsumer,Me.isContextProvider,Me.isElement,Me.isForwardRef,Me.isFragment,Me.isLazy,Me.isMemo,Me.isPortal,Me.isProfiler,Me.isStrictMode,Me.isSuspense;var we=N(function(e,t){!function(){function e(e){if(\"object\"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case o:var n=e.type;switch(n){case d:case p:case a:case l:case s:case g:return n;default:var r=n&&n.$$typeof;switch(r){case u:case h:case c:return r;default:return t}}case A:case m:case i:return t}}}function n(t){return e(t)===p}Object.defineProperty(t,\"__esModule\",{value:!0});var r=\"function\"==typeof Symbol&&Symbol[\"for\"],o=r?Symbol[\"for\"](\"react.element\"):60103,i=r?Symbol[\"for\"](\"react.portal\"):60106,a=r?Symbol[\"for\"](\"react.fragment\"):60107,s=r?Symbol[\"for\"](\"react.strict_mode\"):60108,l=r?Symbol[\"for\"](\"react.profiler\"):60114,c=r?Symbol[\"for\"](\"react.provider\"):60109,u=r?Symbol[\"for\"](\"react.context\"):60110,d=r?Symbol[\"for\"](\"react.async_mode\"):60111,p=r?Symbol[\"for\"](\"react.concurrent_mode\"):60111,h=r?Symbol[\"for\"](\"react.forward_ref\"):60112,g=r?Symbol[\"for\"](\"react.suspense\"):60113,f=r?Symbol[\"for\"](\"react.suspense_list\"):60120,m=r?Symbol[\"for\"](\"react.memo\"):60115,A=r?Symbol[\"for\"](\"react.lazy\"):60116,M=r?Symbol[\"for\"](\"react.fundamental\"):60117,w=r?Symbol[\"for\"](\"react.responder\"):60118,v=r?Symbol[\"for\"](\"react.scope\"):60119,b=function(e,t){if(void 0===t)throw new Error(\"`lowPriorityWarningWithoutStack(condition, format, ...args)` requires a warning message argument\");if(!e){for(var n=arguments.length,r=new Array(n>2?n-2:0),o=2;n>o;o++)r[o-2]=arguments[o];(function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;t>r;r++)n[r-1]=arguments[r];var o=0,i=\"Warning: \"+e.replace(/%s/g,function(){return n[o++]});\"undefined\"!=typeof console&&console.warn(i);try{throw new Error(i)}catch(a){}}).apply(void 0,[t].concat(r))}},y=d,x=p,T=u,C=c,N=o,I=h,E=a,D=A,S=m,L=i,k=l,j=s,U=g,B=!1;t.typeOf=e,t.AsyncMode=y,t.ConcurrentMode=x,t.ContextConsumer=T,t.ContextProvider=C,t.Element=N,t.ForwardRef=I,t.Fragment=E,t.Lazy=D,t.Memo=S,t.Portal=L,t.Profiler=k,t.StrictMode=j,t.Suspense=U,t.isValidElementType=function(e){return\"string\"==typeof e||\"function\"==typeof e||e===a||e===p||e===l||e===s||e===g||e===f||\"object\"==typeof e&&null!==e&&(e.$$typeof===A||e.$$typeof===m||e.$$typeof===c||e.$$typeof===u||e.$$typeof===h||e.$$typeof===M||e.$$typeof===w||e.$$typeof===v)},t.isAsyncMode=function(t){return B||b(!(B=!0),\"The ReactIs.isAsyncMode() alias has been deprecated, and will be removed in React 17+. Update your code to use ReactIs.isConcurrentMode() instead. It has the exact same API.\"),n(t)||e(t)===d},t.isConcurrentMode=n,t.isContextConsumer=function(t){return e(t)===u},t.isContextProvider=function(t){return e(t)===c},t.isElement=function(e){return\"object\"==typeof e&&null!==e&&e.$$typeof===o},t.isForwardRef=function(t){return e(t)===h},t.isFragment=function(t){return e(t)===a},t.isLazy=function(t){return e(t)===A},t.isMemo=function(t){return e(t)===m},t.isPortal=function(t){return e(t)===i},t.isProfiler=function(t){return e(t)===l},t.isStrictMode=function(t){return e(t)===s},t.isSuspense=function(t){return e(t)===g}}()});C(we),we.typeOf,we.AsyncMode,we.ConcurrentMode,we.ContextConsumer,we.ContextProvider,we.Element,we.ForwardRef,we.Fragment,we.Lazy,we.Memo,we.Portal,we.Profiler,we.StrictMode,we.Suspense,we.isValidElementType,we.isAsyncMode,we.isConcurrentMode,we.isContextConsumer,we.isContextProvider,we.isElement,we.isForwardRef,we.isFragment,we.isLazy,we.isMemo,we.isPortal,we.isProfiler,we.isStrictMode,we.isSuspense;var ve,be=N(function(e){e.exports=we}),ye=Object.getOwnPropertySymbols,xe=Object.prototype.hasOwnProperty,Te=Object.prototype.propertyIsEnumerable,Ce=function(){try{if(!Object.assign)return!1;var e=new String(\"abc\");if(e[5]=\"de\",\"5\"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;10>n;n++)t[\"_\"+String.fromCharCode(n)]=n;if(\"0123456789\"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(\"\"))return!1;var r={};return\"abcdefghijklmnopqrst\".split(\"\").forEach(function(e){r[e]=e}),\"abcdefghijklmnopqrst\"===Object.keys(Object.assign({},r)).join(\"\")}catch(o){return!1}}()?Object.assign:function(e,t){for(var n,r,o=function(e){if(null==e)throw new TypeError(\"Object.assign cannot be called with null or undefined\");return Object(e)}(e),i=1;i<arguments.length;i++){for(var a in n=Object(arguments[i]))xe.call(n,a)&&(o[a]=n[a]);if(ye){r=ye(n);for(var s=0;s<r.length;s++)Te.call(n,r[s])&&(o[r[s]]=n[r[s]])}}return o},Ne=\"SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED\",Ie={},Ee=Function.call.bind(Object.prototype.hasOwnProperty);ve=function(e){var t=\"Warning: \"+e;\"undefined\"!=typeof console&&console.error(t);try{throw new Error(t)}catch(n){}},I.resetWarningCache=function(){Ie={}};var De,Se=I,Le=Function.call.bind(Object.prototype.hasOwnProperty);De=function(e){var t=\"Warning: \"+e;\"undefined\"!=typeof console&&console.error(t);try{throw new Error(t)}catch(n){}};var ke,je=N(function(e){var t=be;e.exports=D(t.isElement,!0)}),Ue=!(\"undefined\"==typeof window||!window.document||!window.document.createElement),Be=\"observed\",Re=\"requested\",ze=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"state\",{isScrolling:!1,scrollLeft:0,scrollTop:0}),s(f(n),\"_calculateSizeAndPositionDataOnNextUpdate\",!1),s(f(n),\"_onSectionRenderedMemoizer\",k()),s(f(n),\"_onScrollMemoizer\",k(!1)),s(f(n),\"_invokeOnSectionRenderedHelper\",function(){var e=n.props,t=e.cellLayoutManager,r=e.onSectionRendered;n._onSectionRenderedMemoizer({callback:r,indices:{indices:t.getLastRenderedIndices()}})}),s(f(n),\"_setScrollingContainerRef\",function(e){n._scrollingContainer=e}),s(f(n),\"_updateScrollPositionForScrollToCell\",function(){var e=n.props,t=e.cellLayoutManager,r=e.height,o=e.scrollToAlignment,i=e.scrollToCell,a=e.width,s=n.state,l=s.scrollLeft,c=s.scrollTop;if(i>=0){var u=t.getScrollPositionForCell({align:o,cellIndex:i,height:r,scrollLeft:l,scrollTop:c,width:a});u.scrollLeft===l&&u.scrollTop===c||n._setScrollPosition(u)}}),s(f(n),\"_onScroll\",function(e){if(e.target===n._scrollingContainer){n._enablePointerEventsAfterDelay();var t=n.props,r=t.cellLayoutManager,o=t.height,i=t.isScrollingChange,a=t.width,s=n._scrollbarSize,l=r.getTotalSize(),c=l.height,u=l.width,d=Math.max(0,Math.min(u-a+s,e.target.scrollLeft)),p=Math.max(0,Math.min(c-o+s,e.target.scrollTop));if(n.state.scrollLeft!==d||n.state.scrollTop!==p){var h=e.cancelable?Be:Re;n.state.isScrolling||i(!0),n.setState({isScrolling:!0,scrollLeft:d,scrollPositionChangeReason:h,scrollTop:p})}n._invokeOnScrollMemoizer({scrollLeft:d,scrollTop:p,totalWidth:u,totalHeight:c})}}),n._scrollbarSize=j(),void 0===n._scrollbarSize?(n._scrollbarSizeMeasured=!1,n._scrollbarSize=0):n._scrollbarSizeMeasured=!0,n}return d(e,n.PureComponent),a(e,[{key:\"recomputeCellSizesAndPositions\",value:function(){this._calculateSizeAndPositionDataOnNextUpdate=!0,this.forceUpdate()}},{key:\"componentDidMount\",value:function(){var e=this.props,t=e.cellLayoutManager,n=e.scrollLeft,r=e.scrollToCell,o=e.scrollTop;\nthis._scrollbarSizeMeasured||(this._scrollbarSize=j(),this._scrollbarSizeMeasured=!0,this.setState({})),r>=0?this._updateScrollPositionForScrollToCell():(n>=0||o>=0)&&this._setScrollPosition({scrollLeft:n,scrollTop:o}),this._invokeOnSectionRenderedHelper();var i=t.getTotalSize(),a=i.height,s=i.width;this._invokeOnScrollMemoizer({scrollLeft:n||0,scrollTop:o||0,totalHeight:a,totalWidth:s})}},{key:\"componentDidUpdate\",value:function(e,t){var n=this.props,r=n.height,o=n.scrollToAlignment,i=n.scrollToCell,a=n.width,s=this.state,l=s.scrollLeft,c=s.scrollPositionChangeReason,u=s.scrollTop;c===Re&&(l>=0&&l!==t.scrollLeft&&l!==this._scrollingContainer.scrollLeft&&(this._scrollingContainer.scrollLeft=l),u>=0&&u!==t.scrollTop&&u!==this._scrollingContainer.scrollTop&&(this._scrollingContainer.scrollTop=u)),r===e.height&&o===e.scrollToAlignment&&i===e.scrollToCell&&a===e.width||this._updateScrollPositionForScrollToCell(),this._invokeOnSectionRenderedHelper()}},{key:\"componentWillUnmount\",value:function(){this._disablePointerEventsTimeoutId&&clearTimeout(this._disablePointerEventsTimeoutId)}},{key:\"render\",value:function(){var e=this.props,t=e.autoHeight,r=e.cellCount,o=e.cellLayoutManager,i=e.className,a=e.height,s=e.horizontalOverscanSize,l=e.id,c=e.noContentRenderer,d=e.style,p=e.verticalOverscanSize,h=e.width,g=this.state,f=g.isScrolling,m=g.scrollLeft,A=g.scrollTop;this._lastRenderedCellCount===r&&this._lastRenderedCellLayoutManager===o&&!this._calculateSizeAndPositionDataOnNextUpdate||(this._lastRenderedCellCount=r,this._lastRenderedCellLayoutManager=o,this._calculateSizeAndPositionDataOnNextUpdate=!1,o.calculateSizeAndPositionData());var M=o.getTotalSize(),w=M.height,v=M.width,b=Math.max(0,m-s),y=Math.max(0,A-p),x=Math.min(v,m+h+s),T=Math.min(w,A+a+p),C=a>0&&h>0?o.cellRenderers({height:T-y,isScrolling:f,width:x-b,x:b,y:y}):[],N={boxSizing:\"border-box\",direction:\"ltr\",height:t?\"auto\":a,position:\"relative\",WebkitOverflowScrolling:\"touch\",width:h,willChange:\"transform\"},I=w>a?this._scrollbarSize:0,E=v>h?this._scrollbarSize:0;return N.overflowX=h>=v+I?\"hidden\":\"auto\",N.overflowY=a>=w+E?\"hidden\":\"auto\",n.createElement(\"div\",{ref:this._setScrollingContainerRef,\"aria-label\":this.props[\"aria-label\"],className:L(\"ReactVirtualized__Collection\",i),id:l,onScroll:this._onScroll,role:\"grid\",style:u({},N,{},d),tabIndex:0},r>0&&n.createElement(\"div\",{className:\"ReactVirtualized__Collection__innerScrollContainer\",style:{height:w,maxHeight:w,maxWidth:v,overflow:\"hidden\",pointerEvents:f?\"none\":\"\",width:v}},C),0===r&&c())}},{key:\"_enablePointerEventsAfterDelay\",value:function(){var e=this;this._disablePointerEventsTimeoutId&&clearTimeout(this._disablePointerEventsTimeoutId),this._disablePointerEventsTimeoutId=setTimeout(function(){e.props.isScrollingChange(!1),e._disablePointerEventsTimeoutId=null,e.setState({isScrolling:!1})},150)}},{key:\"_invokeOnScrollMemoizer\",value:function(e){var t=this,n=e.scrollLeft,r=e.scrollTop,o=e.totalHeight,i=e.totalWidth;this._onScrollMemoizer({callback:function(e){var n=e.scrollLeft,r=e.scrollTop,a=t.props,s=a.height;a.onScroll({clientHeight:s,clientWidth:a.width,scrollHeight:o,scrollLeft:n,scrollTop:r,scrollWidth:i})},indices:{scrollLeft:n,scrollTop:r}})}},{key:\"_setScrollPosition\",value:function(e){var t=e.scrollLeft,n=e.scrollTop,r={scrollPositionChangeReason:Re};t>=0&&(r.scrollLeft=t),n>=0&&(r.scrollTop=n),(t>=0&&t!==this.state.scrollLeft||n>=0&&n!==this.state.scrollTop)&&this.setState(r)}}],[{key:\"getDerivedStateFromProps\",value:function(e,t){return 0!==e.cellCount||0===t.scrollLeft&&0===t.scrollTop?e.scrollLeft!==t.scrollLeft||e.scrollTop!==t.scrollTop?{scrollLeft:null!=e.scrollLeft?e.scrollLeft:t.scrollLeft,scrollTop:null!=e.scrollTop?e.scrollTop:t.scrollTop,scrollPositionChangeReason:Re}:null:{scrollLeft:0,scrollTop:0,scrollPositionChangeReason:Re}}}]),e}();s(ze,\"propTypes\",{\"aria-label\":je.string,autoHeight:je.bool,cellCount:je.number.isRequired,cellLayoutManager:je.object.isRequired,className:je.string,height:je.number.isRequired,id:je.string,horizontalOverscanSize:je.number.isRequired,isScrollingChange:je.func,noContentRenderer:je.func.isRequired,onScroll:je.func.isRequired,onSectionRendered:je.func.isRequired,scrollLeft:je.number,scrollToAlignment:je.oneOf([\"auto\",\"end\",\"start\",\"center\"]).isRequired,scrollToCell:je.number.isRequired,scrollTop:je.number,style:je.object,verticalOverscanSize:je.number.isRequired,width:je.number.isRequired}),s(ze,\"defaultProps\",{\"aria-label\":\"grid\",horizontalOverscanSize:0,noContentRenderer:function(){return null},onScroll:function(){return null},onSectionRendered:function(){return null},scrollToAlignment:\"auto\",scrollToCell:-1,style:{},verticalOverscanSize:0}),y(ze);var Qe=function(){function e(t){var n=t.height,r=t.width,i=t.x,a=t.y;o(this,e),this.height=n,this.width=r,this.x=i,this.y=a,this._indexMap={},this._indices=[]}return a(e,[{key:\"addCellIndex\",value:function(e){var t=e.index;this._indexMap[t]||(this._indexMap[t]=!0,this._indices.push(t))}},{key:\"getCellIndices\",value:function(){return this._indices}},{key:\"toString\",value:function(){return\"\".concat(this.x,\",\").concat(this.y,\" \").concat(this.width,\"x\").concat(this.height)}}]),e}(),Oe=function(){function e(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:100;o(this,e),this._sectionSize=t,this._cellMetadata=[],this._sections={}}return a(e,[{key:\"getCellIndices\",value:function(e){var t=e.height,n=e.width,r=e.x,o=e.y,i={};return this.getSections({height:t,width:n,x:r,y:o}).forEach(function(e){return e.getCellIndices().forEach(function(e){i[e]=e})}),Object.keys(i).map(function(e){return i[e]})}},{key:\"getCellMetadata\",value:function(e){var t=e.index;return this._cellMetadata[t]}},{key:\"getSections\",value:function(e){for(var t=e.height,n=e.width,r=e.x,o=e.y,i=Math.floor(r/this._sectionSize),a=Math.floor((r+n-1)/this._sectionSize),s=Math.floor(o/this._sectionSize),l=Math.floor((o+t-1)/this._sectionSize),c=[],u=i;a>=u;u++)for(var d=s;l>=d;d++){var p=\"\".concat(u,\".\").concat(d);this._sections[p]||(this._sections[p]=new Qe({height:this._sectionSize,width:this._sectionSize,x:u*this._sectionSize,y:d*this._sectionSize})),c.push(this._sections[p])}return c}},{key:\"getTotalSectionCount\",value:function(){return Object.keys(this._sections).length}},{key:\"toString\",value:function(){var e=this;return Object.keys(this._sections).map(function(t){return e._sections[t].toString()})}},{key:\"registerCell\",value:function(e){var t=e.cellMetadatum,n=e.index;this._cellMetadata[n]=t,this.getSections(t).forEach(function(e){return e.addCellIndex({index:n})})}}]),e}(),He=function(){function e(t,n){var r;return o(this,e),(r=m(this,p(e).call(this,t,n)))._cellMetadata=[],r._lastRenderedCellIndices=[],r._cellCache=[],r._isScrollingChange=r._isScrollingChange.bind(f(r)),r._setCollectionViewRef=r._setCollectionViewRef.bind(f(r)),r}return d(e,n.PureComponent),a(e,[{key:\"forceUpdate\",value:function(){void 0!==this._collectionView&&this._collectionView.forceUpdate()}},{key:\"recomputeCellSizesAndPositions\",value:function(){this._cellCache=[],this._collectionView.recomputeCellSizesAndPositions()}},{key:\"render\",value:function(){var e=l({},this.props);return n.createElement(ze,l({cellLayoutManager:this,isScrollingChange:this._isScrollingChange,ref:this._setCollectionViewRef},e))}},{key:\"calculateSizeAndPositionData\",value:function(){var e=this.props,t=function(e){for(var t=e.cellCount,n=e.cellSizeAndPositionGetter,r=e.sectionSize,o=[],i=new Oe(r),a=0,s=0,l=0;t>l;l++){var c=n({index:l});if(null==c.height||isNaN(c.height)||null==c.width||isNaN(c.width)||null==c.x||isNaN(c.x)||null==c.y||isNaN(c.y))throw Error(\"Invalid metadata returned for cell \".concat(l,\":\\n        x:\").concat(c.x,\", y:\").concat(c.y,\", width:\").concat(c.width,\", height:\").concat(c.height));a=Math.max(a,c.y+c.height),s=Math.max(s,c.x+c.width),o[l]=c,i.registerCell({cellMetadatum:c,index:l})}return{cellMetadata:o,height:a,sectionManager:i,width:s}}({cellCount:e.cellCount,cellSizeAndPositionGetter:e.cellSizeAndPositionGetter,sectionSize:e.sectionSize});this._cellMetadata=t.cellMetadata,this._sectionManager=t.sectionManager,this._height=t.height,this._width=t.width}},{key:\"getLastRenderedIndices\",value:function(){return this._lastRenderedCellIndices}},{key:\"getScrollPositionForCell\",value:function(e){var t=e.align,n=e.cellIndex,r=e.height,o=e.scrollLeft,i=e.scrollTop,a=e.width,s=this.props.cellCount;if(n>=0&&s>n){var l=this._cellMetadata[n];o=U({align:t,cellOffset:l.x,cellSize:l.width,containerSize:a,currentOffset:o,targetIndex:n}),i=U({align:t,cellOffset:l.y,cellSize:l.height,containerSize:r,currentOffset:i,targetIndex:n})}return{scrollLeft:o,scrollTop:i}}},{key:\"getTotalSize\",value:function(){return{height:this._height,width:this._width}}},{key:\"cellRenderers\",value:function(e){var t=this,n=e.height,r=e.isScrolling,o=e.width,i=e.x,a=e.y,s=this.props,l=s.cellGroupRenderer,c=s.cellRenderer;return this._lastRenderedCellIndices=this._sectionManager.getCellIndices({height:n,width:o,x:i,y:a}),l({cellCache:this._cellCache,cellRenderer:c,cellSizeAndPositionGetter:function(e){var n=e.index;return t._sectionManager.getCellMetadata({index:n})},indices:this._lastRenderedCellIndices,isScrolling:r})}},{key:\"_isScrollingChange\",value:function(e){e||(this._cellCache=[])}},{key:\"_setCollectionViewRef\",value:function(e){this._collectionView=e}}]),e}();s(He,\"propTypes\",{\"aria-label\":je.string,cellCount:je.number.isRequired,cellGroupRenderer:je.func.isRequired,cellRenderer:je.func.isRequired,cellSizeAndPositionGetter:je.func.isRequired,sectionSize:je.number}),s(He,\"defaultProps\",{\"aria-label\":\"grid\",cellGroupRenderer:function(e){var t=e.cellCache,n=e.cellRenderer,r=e.cellSizeAndPositionGetter,o=e.indices,i=e.isScrolling;return o.map(function(e){var o=r({index:e}),a={index:e,isScrolling:i,key:e,style:{height:o.height,left:o.x,position:\"absolute\",top:o.y,width:o.width}};return i?(e in t||(t[e]=n(a)),t[e]):n(a)}).filter(function(e){return!!e})}});var Fe=function(){function e(t,n){var r;return o(this,e),(r=m(this,p(e).call(this,t,n)))._registerChild=r._registerChild.bind(f(r)),r}return d(e,n.PureComponent),a(e,[{key:\"componentDidUpdate\",value:function(e){var t=this.props,n=t.columnMaxWidth,r=t.columnMinWidth,o=t.columnCount,i=t.width;n===e.columnMaxWidth&&r===e.columnMinWidth&&o===e.columnCount&&i===e.width||this._registeredChild&&this._registeredChild.recomputeGridSize()}},{key:\"render\",value:function(){var e=this.props,t=e.children,n=e.columnMaxWidth,r=e.columnMinWidth,o=e.columnCount,i=e.width,a=r||1,s=n?Math.min(n,i):i,l=i/o;return l=Math.max(a,l),l=Math.min(s,l),l=Math.floor(l),t({adjustedWidth:Math.min(i,l*o),columnWidth:l,getColumnWidth:function(){return l},registerChild:this._registerChild})}},{key:\"_registerChild\",value:function(e){if(e&&\"function\"!=typeof e.recomputeGridSize)throw Error(\"Unexpected child type registered; only Grid/MultiGrid children are supported.\");this._registeredChild=e,this._registeredChild&&this._registeredChild.recomputeGridSize()}}]),e}();s(Fe,\"propTypes\",{children:je.func.isRequired,columnMaxWidth:je.number,columnMinWidth:je.number,columnCount:je.number.isRequired,width:je.number.isRequired});var Ve,Pe=function(){function e(t){var n=t.cellCount,r=t.cellSizeGetter,i=t.estimatedCellSize;o(this,e),s(this,\"_cellSizeAndPositionData\",{}),s(this,\"_lastMeasuredIndex\",-1),s(this,\"_lastBatchedIndex\",-1),s(this,\"_cellCount\",void 0),s(this,\"_cellSizeGetter\",void 0),s(this,\"_estimatedCellSize\",void 0),this._cellSizeGetter=r,this._cellCount=n,this._estimatedCellSize=i}return a(e,[{key:\"areOffsetsAdjusted\",value:function(){return!1}},{key:\"configure\",value:function(e){var t=e.cellCount,n=e.estimatedCellSize,r=e.cellSizeGetter;this._cellCount=t,this._estimatedCellSize=n,this._cellSizeGetter=r}},{key:\"getCellCount\",value:function(){return this._cellCount}},{key:\"getEstimatedCellSize\",value:function(){return this._estimatedCellSize}},{key:\"getLastMeasuredIndex\",value:function(){return this._lastMeasuredIndex}},{key:\"getOffsetAdjustment\",value:function(){return 0}},{key:\"getSizeAndPositionOfCell\",value:function(e){if(0>e||e>=this._cellCount)throw Error(\"Requested index \".concat(e,\" is outside of range 0..\").concat(this._cellCount));if(e>this._lastMeasuredIndex)for(var t=this.getSizeAndPositionOfLastMeasuredCell(),n=t.offset+t.size,r=this._lastMeasuredIndex+1;e>=r;r++){var o=this._cellSizeGetter({index:r});if(void 0===o||isNaN(o))throw Error(\"Invalid size returned for cell \".concat(r,\" of value \").concat(o));null===o?(this._cellSizeAndPositionData[r]={offset:n,size:0},this._lastBatchedIndex=e):(this._cellSizeAndPositionData[r]={offset:n,size:o},n+=o,this._lastMeasuredIndex=e)}return this._cellSizeAndPositionData[e]}},{key:\"getSizeAndPositionOfLastMeasuredCell\",value:function(){return 0<=this._lastMeasuredIndex?this._cellSizeAndPositionData[this._lastMeasuredIndex]:{offset:0,size:0}}},{key:\"getTotalSize\",value:function(){var e=this.getSizeAndPositionOfLastMeasuredCell();return e.offset+e.size+(this._cellCount-this._lastMeasuredIndex-1)*this._estimatedCellSize}},{key:\"getUpdatedOffsetForIndex\",value:function(e){var t=e.align,n=void 0===t?\"auto\":t,r=e.containerSize,o=e.currentOffset,i=e.targetIndex;if(0>=r)return 0;var a,s=this.getSizeAndPositionOfCell(i),l=s.offset,c=l-r+s.size;switch(n){case\"start\":a=l;break;case\"end\":a=c;break;case\"center\":a=l-(r-s.size)/2;break;default:a=Math.max(c,Math.min(l,o))}var u=this.getTotalSize();return Math.max(0,Math.min(u-r,a))}},{key:\"getVisibleCellRange\",value:function(e){var t=e.containerSize,n=e.offset;if(0===this.getTotalSize())return{};var r=n+t,o=this._findNearestCell(n),i=this.getSizeAndPositionOfCell(o);n=i.offset+i.size;for(var a=o;r>n&&a<this._cellCount-1;)a++,n+=this.getSizeAndPositionOfCell(a).size;return{start:o,stop:a}}},{key:\"resetCell\",value:function(e){this._lastMeasuredIndex=Math.min(this._lastMeasuredIndex,e-1)}},{key:\"_binarySearch\",value:function(e,t,n){for(;e>=t;){var r=t+Math.floor((e-t)/2),o=this.getSizeAndPositionOfCell(r).offset;if(o===n)return r;n>o?t=r+1:o>n&&(e=r-1)}return t>0?t-1:0}},{key:\"_exponentialSearch\",value:function(e,t){for(var n=1;e<this._cellCount&&this.getSizeAndPositionOfCell(e).offset<t;)e+=n,n*=2;return this._binarySearch(Math.min(e,this._cellCount-1),Math.floor(e/2),t)}},{key:\"_findNearestCell\",value:function(e){if(isNaN(e))throw Error(\"Invalid offset \".concat(e,\" specified\"));e=Math.max(0,e);var t=this.getSizeAndPositionOfLastMeasuredCell(),n=Math.max(0,this._lastMeasuredIndex);return t.offset>=e?this._binarySearch(n,0,e):this._exponentialSearch(n,e)}}]),e}(),Ye=function(){return\"undefined\"!=typeof window&&window.chrome?16777100:15e5},Ge=function(){function e(t){var n=t.maxScrollSize,r=void 0===n?Ye():n,i=g(t,[\"maxScrollSize\"]);o(this,e),s(this,\"_cellSizeAndPositionManager\",void 0),s(this,\"_maxScrollSize\",void 0),this._cellSizeAndPositionManager=new Pe(i),this._maxScrollSize=r}return a(e,[{key:\"areOffsetsAdjusted\",value:function(){return this._cellSizeAndPositionManager.getTotalSize()>this._maxScrollSize}},{key:\"configure\",value:function(e){this._cellSizeAndPositionManager.configure(e)}},{key:\"getCellCount\",value:function(){return this._cellSizeAndPositionManager.getCellCount()}},{key:\"getEstimatedCellSize\",value:function(){return this._cellSizeAndPositionManager.getEstimatedCellSize()}},{key:\"getLastMeasuredIndex\",value:function(){return this._cellSizeAndPositionManager.getLastMeasuredIndex()}},{key:\"getOffsetAdjustment\",value:function(e){var t=e.containerSize,n=e.offset,r=this._cellSizeAndPositionManager.getTotalSize(),o=this.getTotalSize(),i=this._getOffsetPercentage({containerSize:t,offset:n,totalSize:o});return Math.round(i*(o-r))}},{key:\"getSizeAndPositionOfCell\",value:function(e){return this._cellSizeAndPositionManager.getSizeAndPositionOfCell(e)}},{key:\"getSizeAndPositionOfLastMeasuredCell\",value:function(){return this._cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell()}},{key:\"getTotalSize\",value:function(){return Math.min(this._maxScrollSize,this._cellSizeAndPositionManager.getTotalSize())}},{key:\"getUpdatedOffsetForIndex\",value:function(e){var t=e.align,n=void 0===t?\"auto\":t,r=e.containerSize,o=e.currentOffset,i=e.targetIndex;o=this._safeOffsetToOffset({containerSize:r,offset:o});var a=this._cellSizeAndPositionManager.getUpdatedOffsetForIndex({align:n,containerSize:r,currentOffset:o,targetIndex:i});return this._offsetToSafeOffset({containerSize:r,offset:a})}},{key:\"getVisibleCellRange\",value:function(e){var t=e.containerSize,n=e.offset;return n=this._safeOffsetToOffset({containerSize:t,offset:n}),this._cellSizeAndPositionManager.getVisibleCellRange({containerSize:t,offset:n})}},{key:\"resetCell\",value:function(e){this._cellSizeAndPositionManager.resetCell(e)}},{key:\"_getOffsetPercentage\",value:function(e){var t=e.containerSize,n=e.offset,r=e.totalSize;return t>=r?0:n/(r-t)}},{key:\"_offsetToSafeOffset\",value:function(e){var t=e.containerSize,n=e.offset,r=this._cellSizeAndPositionManager.getTotalSize(),o=this.getTotalSize();if(r===o)return n;var i=this._getOffsetPercentage({containerSize:t,offset:n,totalSize:r});return Math.round(i*(o-t))}},{key:\"_safeOffsetToOffset\",value:function(e){var t=e.containerSize,n=e.offset,r=this._cellSizeAndPositionManager.getTotalSize(),o=this.getTotalSize();if(r===o)return n;var i=this._getOffsetPercentage({containerSize:t,offset:n,totalSize:o});return Math.round(i*(r-t))}}]),e}(),We=(Ve=\"undefined\"!=typeof window?window:\"undefined\"!=typeof self?self:{}).requestAnimationFrame||Ve.webkitRequestAnimationFrame||Ve.mozRequestAnimationFrame||Ve.oRequestAnimationFrame||Ve.msRequestAnimationFrame||function(e){return Ve.setTimeout(e,1e3/60)},Xe=Ve.cancelAnimationFrame||Ve.webkitCancelAnimationFrame||Ve.mozCancelAnimationFrame||Ve.oCancelAnimationFrame||Ve.msCancelAnimationFrame||function(e){Ve.clearTimeout(e)},_e=We,qe=Xe,Je=function(e){return qe(e.id)},Ke=function(e,t){var n;Promise.resolve().then(function(){n=Date.now()});var r={id:_e(function o(){Date.now()-n>=t?e.call():r.id=_e(o)})};return r},Ze=\"observed\",$e=\"requested\",et=function(){function e(t){var n;o(this,e),s(f(n=m(this,p(e).call(this,t))),\"_onGridRenderedMemoizer\",k()),s(f(n),\"_onScrollMemoizer\",k(!1)),s(f(n),\"_deferredInvalidateColumnIndex\",null),s(f(n),\"_deferredInvalidateRowIndex\",null),s(f(n),\"_recomputeScrollLeftFlag\",!1),s(f(n),\"_recomputeScrollTopFlag\",!1),s(f(n),\"_horizontalScrollBarSize\",0),s(f(n),\"_verticalScrollBarSize\",0),s(f(n),\"_scrollbarPresenceChanged\",!1),s(f(n),\"_scrollingContainer\",void 0),s(f(n),\"_childrenToDisplay\",void 0),s(f(n),\"_columnStartIndex\",void 0),s(f(n),\"_columnStopIndex\",void 0),s(f(n),\"_rowStartIndex\",void 0),s(f(n),\"_rowStopIndex\",void 0),s(f(n),\"_renderedColumnStartIndex\",0),s(f(n),\"_renderedColumnStopIndex\",0),s(f(n),\"_renderedRowStartIndex\",0),s(f(n),\"_renderedRowStopIndex\",0),s(f(n),\"_initialScrollTop\",void 0),s(f(n),\"_initialScrollLeft\",void 0),s(f(n),\"_disablePointerEventsTimeoutId\",void 0),s(f(n),\"_styleCache\",{}),s(f(n),\"_cellCache\",{}),s(f(n),\"_debounceScrollEndedCallback\",function(){n._disablePointerEventsTimeoutId=null,n.setState({isScrolling:!1,needToResetStyleCache:!1})}),s(f(n),\"_invokeOnGridRenderedHelper\",function(){var e=n.props.onSectionRendered;n._onGridRenderedMemoizer({callback:e,indices:{columnOverscanStartIndex:n._columnStartIndex,columnOverscanStopIndex:n._columnStopIndex,columnStartIndex:n._renderedColumnStartIndex,columnStopIndex:n._renderedColumnStopIndex,rowOverscanStartIndex:n._rowStartIndex,rowOverscanStopIndex:n._rowStopIndex,rowStartIndex:n._renderedRowStartIndex,rowStopIndex:n._renderedRowStopIndex}})}),s(f(n),\"_setScrollingContainerRef\",function(e){n._scrollingContainer=e}),s(f(n),\"_onScroll\",function(e){e.target===n._scrollingContainer&&n.handleScrollEvent(e.target)});var r=new Ge({cellCount:t.columnCount,cellSizeGetter:function(n){return e._wrapSizeGetter(t.columnWidth)(n)},estimatedCellSize:e._getEstimatedColumnSize(t)}),i=new Ge({cellCount:t.rowCount,cellSizeGetter:function(n){return e._wrapSizeGetter(t.rowHeight)(n)},estimatedCellSize:e._getEstimatedRowSize(t)});return n.state={instanceProps:{columnSizeAndPositionManager:r,rowSizeAndPositionManager:i,prevColumnWidth:t.columnWidth,prevRowHeight:t.rowHeight,prevColumnCount:t.columnCount,prevRowCount:t.rowCount,prevIsScrolling:!0===t.isScrolling,prevScrollToColumn:t.scrollToColumn,prevScrollToRow:t.scrollToRow,scrollbarSize:0,scrollbarSizeMeasured:!1},isScrolling:!1,scrollDirectionHorizontal:1,scrollDirectionVertical:1,scrollLeft:0,scrollTop:0,scrollPositionChangeReason:null,needToResetStyleCache:!1},0<t.scrollToRow&&(n._initialScrollTop=n._getCalculatedScrollTop(t,n.state)),0<t.scrollToColumn&&(n._initialScrollLeft=n._getCalculatedScrollLeft(t,n.state)),n}return d(e,n.PureComponent),a(e,[{key:\"getOffsetForCell\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.alignment,r=void 0===n?this.props.scrollToAlignment:n,o=t.columnIndex,i=void 0===o?this.props.scrollToColumn:o,a=t.rowIndex,s=void 0===a?this.props.scrollToRow:a,l=u({},this.props,{scrollToAlignment:r,scrollToColumn:i,scrollToRow:s});return{scrollLeft:this._getCalculatedScrollLeft(l),scrollTop:this._getCalculatedScrollTop(l)}}},{key:\"getTotalRowsHeight\",value:function(){return this.state.instanceProps.rowSizeAndPositionManager.getTotalSize()}},{key:\"getTotalColumnsWidth\",value:function(){return this.state.instanceProps.columnSizeAndPositionManager.getTotalSize()}},{key:\"handleScrollEvent\",value:function(e){var t=e.scrollLeft,n=void 0===t?0:t,r=e.scrollTop,o=void 0===r?0:r;if(!(0>o)){this._debounceScrollEnded();var i=this.props,a=i.autoHeight,s=i.autoWidth,l=i.height,c=i.width,u=this.state.instanceProps,d=u.scrollbarSize,p=u.rowSizeAndPositionManager.getTotalSize(),h=u.columnSizeAndPositionManager.getTotalSize(),g=Math.min(Math.max(0,h-c+d),n),f=Math.min(Math.max(0,p-l+d),o);if(this.state.scrollLeft!==g||this.state.scrollTop!==f){var m={isScrolling:!0,scrollDirectionHorizontal:g!==this.state.scrollLeft?g>this.state.scrollLeft?1:-1:this.state.scrollDirectionHorizontal,scrollDirectionVertical:f!==this.state.scrollTop?f>this.state.scrollTop?1:-1:this.state.scrollDirectionVertical,scrollPositionChangeReason:Ze};a||(m.scrollTop=f),s||(m.scrollLeft=g),m.needToResetStyleCache=!1,this.setState(m)}this._invokeOnScrollMemoizer({scrollLeft:g,scrollTop:f,totalColumnsWidth:h,totalRowsHeight:p})}}},{key:\"invalidateCellSizeAfterRender\",value:function(e){var t=e.columnIndex,n=e.rowIndex;this._deferredInvalidateColumnIndex=\"number\"==typeof this._deferredInvalidateColumnIndex?Math.min(this._deferredInvalidateColumnIndex,t):t,this._deferredInvalidateRowIndex=\"number\"==typeof this._deferredInvalidateRowIndex?Math.min(this._deferredInvalidateRowIndex,n):n}},{key:\"measureAllCells\",value:function(){var e=this.props,t=e.columnCount,n=e.rowCount,r=this.state.instanceProps;r.columnSizeAndPositionManager.getSizeAndPositionOfCell(t-1),r.rowSizeAndPositionManager.getSizeAndPositionOfCell(n-1)}},{key:\"recomputeGridSize\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.columnIndex,r=void 0===n?0:n,o=t.rowIndex,i=void 0===o?0:o,a=this.props,s=a.scrollToColumn,l=a.scrollToRow,c=this.state.instanceProps;c.columnSizeAndPositionManager.resetCell(r),c.rowSizeAndPositionManager.resetCell(i),this._recomputeScrollLeftFlag=s>=0&&(1===this.state.scrollDirectionHorizontal?s>=r:r>=s),this._recomputeScrollTopFlag=l>=0&&(1===this.state.scrollDirectionVertical?l>=i:i>=l),this._styleCache={},this._cellCache={},this.forceUpdate()}},{key:\"scrollToCell\",value:function(e){var t=e.columnIndex,n=e.rowIndex,r=this.props.columnCount,o=this.props;r>1&&void 0!==t&&this._updateScrollLeftForScrollToColumn(u({},o,{scrollToColumn:t})),void 0!==n&&this._updateScrollTopForScrollToRow(u({},o,{scrollToRow:n}))}},{key:\"componentDidMount\",value:function(){var t=this.props,n=t.getScrollbarSize,r=t.height,o=t.scrollLeft,i=t.scrollToColumn,a=t.scrollTop,s=t.scrollToRow,l=t.width,c=this.state.instanceProps;if(this._initialScrollTop=0,this._initialScrollLeft=0,this._handleInvalidatedGridSize(),c.scrollbarSizeMeasured||this.setState(function(e){var t=u({},e,{needToResetStyleCache:!1});return t.instanceProps.scrollbarSize=n(),t.instanceProps.scrollbarSizeMeasured=!0,t}),\"number\"==typeof o&&o>=0||\"number\"==typeof a&&a>=0){var d=e._getScrollToPositionStateUpdate({prevState:this.state,scrollLeft:o,scrollTop:a});d&&(d.needToResetStyleCache=!1,this.setState(d))}this._scrollingContainer&&(this._scrollingContainer.scrollLeft!==this.state.scrollLeft&&(this._scrollingContainer.scrollLeft=this.state.scrollLeft),this._scrollingContainer.scrollTop!==this.state.scrollTop&&(this._scrollingContainer.scrollTop=this.state.scrollTop));var p=r>0&&l>0;i>=0&&p&&this._updateScrollLeftForScrollToColumn(),s>=0&&p&&this._updateScrollTopForScrollToRow(),this._invokeOnGridRenderedHelper(),this._invokeOnScrollMemoizer({scrollLeft:o||0,scrollTop:a||0,totalColumnsWidth:c.columnSizeAndPositionManager.getTotalSize(),totalRowsHeight:c.rowSizeAndPositionManager.getTotalSize()}),this._maybeCallOnScrollbarPresenceChange()}},{key:\"componentDidUpdate\",value:function(e,t){var n=this,r=this.props,o=r.autoHeight,i=r.autoWidth,a=r.columnCount,s=r.height,l=r.rowCount,c=r.scrollToAlignment,u=r.scrollToColumn,d=r.scrollToRow,p=r.width,h=this.state,g=h.scrollLeft,f=h.scrollPositionChangeReason,m=h.scrollTop,A=h.instanceProps;this._handleInvalidatedGridSize();var M=a>0&&0===e.columnCount||l>0&&0===e.rowCount;f===$e&&(!i&&g>=0&&(g!==this._scrollingContainer.scrollLeft||M)&&(this._scrollingContainer.scrollLeft=g),!o&&m>=0&&(m!==this._scrollingContainer.scrollTop||M)&&(this._scrollingContainer.scrollTop=m));var w=(0===e.width||0===e.height)&&s>0&&p>0;if(this._recomputeScrollLeftFlag?(this._recomputeScrollLeftFlag=!1,this._updateScrollLeftForScrollToColumn(this.props)):z({cellSizeAndPositionManager:A.columnSizeAndPositionManager,previousCellsCount:e.columnCount,previousCellSize:e.columnWidth,previousScrollToAlignment:e.scrollToAlignment,previousScrollToIndex:e.scrollToColumn,previousSize:e.width,scrollOffset:g,scrollToAlignment:c,scrollToIndex:u,size:p,sizeJustIncreasedFromZero:w,updateScrollIndexCallback:function(){return n._updateScrollLeftForScrollToColumn(n.props)}}),this._recomputeScrollTopFlag?(this._recomputeScrollTopFlag=!1,this._updateScrollTopForScrollToRow(this.props)):z({cellSizeAndPositionManager:A.rowSizeAndPositionManager,previousCellsCount:e.rowCount,previousCellSize:e.rowHeight,previousScrollToAlignment:e.scrollToAlignment,previousScrollToIndex:e.scrollToRow,previousSize:e.height,scrollOffset:m,scrollToAlignment:c,scrollToIndex:d,size:s,sizeJustIncreasedFromZero:w,updateScrollIndexCallback:function(){return n._updateScrollTopForScrollToRow(n.props)}}),this._invokeOnGridRenderedHelper(),g!==t.scrollLeft||m!==t.scrollTop){var v=A.rowSizeAndPositionManager.getTotalSize(),b=A.columnSizeAndPositionManager.getTotalSize();this._invokeOnScrollMemoizer({scrollLeft:g,scrollTop:m,totalColumnsWidth:b,totalRowsHeight:v})}this._maybeCallOnScrollbarPresenceChange()}},{key:\"componentWillUnmount\",value:function(){this._disablePointerEventsTimeoutId&&Je(this._disablePointerEventsTimeoutId)}},{key:\"render\",value:function(){var e=this.props,t=e.autoContainerWidth,r=e.autoHeight,o=e.autoWidth,i=e.className,a=e.containerProps,s=e.containerRole,c=e.containerStyle,d=e.height,p=e.id,h=e.noContentRenderer,g=e.role,f=e.style,m=e.tabIndex,A=e.width,M=this.state,w=M.instanceProps,v=M.needToResetStyleCache,b=this._isScrolling(),y={boxSizing:\"border-box\",direction:\"ltr\",height:r?\"auto\":d,position:\"relative\",width:o?\"auto\":A,WebkitOverflowScrolling:\"touch\",willChange:\"transform\"};v&&(this._styleCache={}),this.state.isScrolling||this._resetStyleCache(),this._calculateChildrenToRender(this.props,this.state);var x=w.columnSizeAndPositionManager.getTotalSize(),T=w.rowSizeAndPositionManager.getTotalSize(),C=T>d?w.scrollbarSize:0,N=x>A?w.scrollbarSize:0;N===this._horizontalScrollBarSize&&C===this._verticalScrollBarSize||(this._horizontalScrollBarSize=N,this._verticalScrollBarSize=C,this._scrollbarPresenceChanged=!0),y.overflowX=A>=x+C?\"hidden\":\"auto\",y.overflowY=d>=T+N?\"hidden\":\"auto\";var I=this._childrenToDisplay,E=0===I.length&&d>0&&A>0;return n.createElement(\"div\",l({ref:this._setScrollingContainerRef},a,{\"aria-label\":this.props[\"aria-label\"],\"aria-readonly\":this.props[\"aria-readonly\"],className:L(\"ReactVirtualized__Grid\",i),id:p,onScroll:this._onScroll,role:g,style:u({},y,{},f),tabIndex:m}),0<I.length&&n.createElement(\"div\",{className:\"ReactVirtualized__Grid__innerScrollContainer\",role:s,style:u({width:t?\"auto\":x,height:T,maxWidth:x,maxHeight:T,overflow:\"hidden\",pointerEvents:b?\"none\":\"\",position:\"relative\"},c)},I),E&&h())}},{key:\"_calculateChildrenToRender\",value:function(e,t){var n=0<arguments.length&&void 0!==e?e:this.props,r=1<arguments.length&&void 0!==t?t:this.state,o=n.cellRenderer,i=n.cellRangeRenderer,a=n.columnCount,s=n.deferredMeasurementCache,l=n.height,c=n.overscanColumnCount,u=n.overscanIndicesGetter,d=n.overscanRowCount,p=n.rowCount,h=n.width,g=n.isScrollingOptOut,f=r.scrollDirectionHorizontal,m=r.scrollDirectionVertical,A=r.instanceProps,M=0<this._initialScrollTop?this._initialScrollTop:r.scrollTop,w=0<this._initialScrollLeft?this._initialScrollLeft:r.scrollLeft,v=this._isScrolling(n,r);if(this._childrenToDisplay=[],l>0&&h>0){var b=A.columnSizeAndPositionManager.getVisibleCellRange({containerSize:h,offset:w}),y=A.rowSizeAndPositionManager.getVisibleCellRange({containerSize:l,offset:M}),x=A.columnSizeAndPositionManager.getOffsetAdjustment({containerSize:h,offset:w}),T=A.rowSizeAndPositionManager.getOffsetAdjustment({containerSize:l,offset:M});this._renderedColumnStartIndex=b.start,this._renderedColumnStopIndex=b.stop,this._renderedRowStartIndex=y.start,this._renderedRowStopIndex=y.stop;var C=u({direction:\"horizontal\",cellCount:a,overscanCellsCount:c,scrollDirection:f,startIndex:\"number\"==typeof b.start?b.start:0,stopIndex:\"number\"==typeof b.stop?b.stop:-1}),N=u({direction:\"vertical\",cellCount:p,overscanCellsCount:d,scrollDirection:m,startIndex:\"number\"==typeof y.start?y.start:0,stopIndex:\"number\"==typeof y.stop?y.stop:-1}),I=C.overscanStartIndex,E=C.overscanStopIndex,D=N.overscanStartIndex,S=N.overscanStopIndex;if(s){if(!s.hasFixedHeight())for(var L=D;S>=L;L++)if(!s.has(L,0)){I=0,E=a-1;break}if(!s.hasFixedWidth())for(var k=I;E>=k;k++)if(!s.has(0,k)){D=0,S=p-1;break}}this._childrenToDisplay=i({cellCache:this._cellCache,cellRenderer:o,columnSizeAndPositionManager:A.columnSizeAndPositionManager,columnStartIndex:I,columnStopIndex:E,deferredMeasurementCache:s,horizontalOffsetAdjustment:x,isScrolling:v,isScrollingOptOut:g,parent:this,rowSizeAndPositionManager:A.rowSizeAndPositionManager,rowStartIndex:D,rowStopIndex:S,scrollLeft:w,scrollTop:M,styleCache:this._styleCache,verticalOffsetAdjustment:T,visibleColumnIndices:b,visibleRowIndices:y}),this._columnStartIndex=I,this._columnStopIndex=E,this._rowStartIndex=D,this._rowStopIndex=S}}},{key:\"_debounceScrollEnded\",value:function(){var e=this.props.scrollingResetTimeInterval;this._disablePointerEventsTimeoutId&&Je(this._disablePointerEventsTimeoutId),this._disablePointerEventsTimeoutId=Ke(this._debounceScrollEndedCallback,e)}},{key:\"_handleInvalidatedGridSize\",value:function(){if(\"number\"==typeof this._deferredInvalidateColumnIndex&&\"number\"==typeof this._deferredInvalidateRowIndex){var e=this._deferredInvalidateColumnIndex,t=this._deferredInvalidateRowIndex;this._deferredInvalidateColumnIndex=null,this._deferredInvalidateRowIndex=null,this.recomputeGridSize({columnIndex:e,rowIndex:t})}}},{key:\"_invokeOnScrollMemoizer\",value:function(e){var t=this,n=e.scrollLeft,r=e.scrollTop,o=e.totalColumnsWidth,i=e.totalRowsHeight;this._onScrollMemoizer({callback:function(e){var n=e.scrollLeft,r=e.scrollTop,a=t.props,s=a.height;a.onScroll({clientHeight:s,clientWidth:a.width,scrollHeight:i,scrollLeft:n,scrollTop:r,scrollWidth:o})},indices:{scrollLeft:n,scrollTop:r}})}},{key:\"_isScrolling\",value:function(e,t){var n=0<arguments.length&&void 0!==e?e:this.props,r=1<arguments.length&&void 0!==t?t:this.state;\nreturn Object.hasOwnProperty.call(n,\"isScrolling\")?Boolean(n.isScrolling):Boolean(r.isScrolling)}},{key:\"_maybeCallOnScrollbarPresenceChange\",value:function(){if(this._scrollbarPresenceChanged){var e=this.props.onScrollbarPresenceChange;this._scrollbarPresenceChanged=!1,e({horizontal:0<this._horizontalScrollBarSize,size:this.state.instanceProps.scrollbarSize,vertical:0<this._verticalScrollBarSize})}}},{key:\"scrollToPosition\",value:function(t){var n=t.scrollLeft,r=t.scrollTop,o=e._getScrollToPositionStateUpdate({prevState:this.state,scrollLeft:n,scrollTop:r});o&&(o.needToResetStyleCache=!1,this.setState(o))}},{key:\"_getCalculatedScrollLeft\",value:function(t,n){var r=0<arguments.length&&void 0!==t?t:this.props,o=1<arguments.length&&void 0!==n?n:this.state;return e._getCalculatedScrollLeft(r,o)}},{key:\"_updateScrollLeftForScrollToColumn\",value:function(t,n){var r=0<arguments.length&&void 0!==t?t:this.props,o=1<arguments.length&&void 0!==n?n:this.state,i=e._getScrollLeftForScrollToColumnStateUpdate(r,o);i&&(i.needToResetStyleCache=!1,this.setState(i))}},{key:\"_getCalculatedScrollTop\",value:function(t,n){var r=0<arguments.length&&void 0!==t?t:this.props,o=1<arguments.length&&void 0!==n?n:this.state;return e._getCalculatedScrollTop(r,o)}},{key:\"_resetStyleCache\",value:function(){var e=this._styleCache,t=this._cellCache,n=this.props.isScrollingOptOut;this._cellCache={},this._styleCache={};for(var r=this._rowStartIndex;r<=this._rowStopIndex;r++)for(var o=this._columnStartIndex;o<=this._columnStopIndex;o++){var i=\"\".concat(r,\"-\").concat(o);this._styleCache[i]=e[i],n&&(this._cellCache[i]=t[i])}}},{key:\"_updateScrollTopForScrollToRow\",value:function(t,n){var r=0<arguments.length&&void 0!==t?t:this.props,o=1<arguments.length&&void 0!==n?n:this.state,i=e._getScrollTopForScrollToRowStateUpdate(r,o);i&&(i.needToResetStyleCache=!1,this.setState(i))}}],[{key:\"getDerivedStateFromProps\",value:function(t,n){var r={};0===t.columnCount&&0!==n.scrollLeft||0===t.rowCount&&0!==n.scrollTop?(r.scrollLeft=0,r.scrollTop=0):(t.scrollLeft!==n.scrollLeft&&t.scrollToColumn<0||t.scrollTop!==n.scrollTop&&t.scrollToRow<0)&&Object.assign(r,e._getScrollToPositionStateUpdate({prevState:n,scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}));var o,i,a=n.instanceProps;return r.needToResetStyleCache=!1,t.columnWidth===a.prevColumnWidth&&t.rowHeight===a.prevRowHeight||(r.needToResetStyleCache=!0),a.columnSizeAndPositionManager.configure({cellCount:t.columnCount,estimatedCellSize:e._getEstimatedColumnSize(t),cellSizeGetter:e._wrapSizeGetter(t.columnWidth)}),a.rowSizeAndPositionManager.configure({cellCount:t.rowCount,estimatedCellSize:e._getEstimatedRowSize(t),cellSizeGetter:e._wrapSizeGetter(t.rowHeight)}),0!==a.prevColumnCount&&0!==a.prevRowCount||(a.prevColumnCount=0,a.prevRowCount=0),t.autoHeight&&!1===t.isScrolling&&!0===a.prevIsScrolling&&Object.assign(r,{isScrolling:!1}),B({cellCount:a.prevColumnCount,cellSize:\"number\"==typeof a.prevColumnWidth?a.prevColumnWidth:null,computeMetadataCallback:function(){return a.columnSizeAndPositionManager.resetCell(0)},computeMetadataCallbackProps:t,nextCellsCount:t.columnCount,nextCellSize:\"number\"==typeof t.columnWidth?t.columnWidth:null,nextScrollToIndex:t.scrollToColumn,scrollToIndex:a.prevScrollToColumn,updateScrollOffsetForScrollToIndex:function(){o=e._getScrollLeftForScrollToColumnStateUpdate(t,n)}}),B({cellCount:a.prevRowCount,cellSize:\"number\"==typeof a.prevRowHeight?a.prevRowHeight:null,computeMetadataCallback:function(){return a.rowSizeAndPositionManager.resetCell(0)},computeMetadataCallbackProps:t,nextCellsCount:t.rowCount,nextCellSize:\"number\"==typeof t.rowHeight?t.rowHeight:null,nextScrollToIndex:t.scrollToRow,scrollToIndex:a.prevScrollToRow,updateScrollOffsetForScrollToIndex:function(){i=e._getScrollTopForScrollToRowStateUpdate(t,n)}}),a.prevColumnCount=t.columnCount,a.prevColumnWidth=t.columnWidth,a.prevIsScrolling=!0===t.isScrolling,a.prevRowCount=t.rowCount,a.prevRowHeight=t.rowHeight,a.prevScrollToColumn=t.scrollToColumn,a.prevScrollToRow=t.scrollToRow,a.scrollbarSize=t.getScrollbarSize(),void 0===a.scrollbarSize?(a.scrollbarSizeMeasured=!1,a.scrollbarSize=0):a.scrollbarSizeMeasured=!0,r.instanceProps=a,u({},r,{},o,{},i)}},{key:\"_getEstimatedColumnSize\",value:function(e){return\"number\"==typeof e.columnWidth?e.columnWidth:e.estimatedColumnSize}},{key:\"_getEstimatedRowSize\",value:function(e){return\"number\"==typeof e.rowHeight?e.rowHeight:e.estimatedRowSize}},{key:\"_getScrollToPositionStateUpdate\",value:function(e){var t=e.prevState,n=e.scrollLeft,r=e.scrollTop,o={scrollPositionChangeReason:$e};return\"number\"==typeof n&&n>=0&&(o.scrollDirectionHorizontal=n>t.scrollLeft?1:-1,o.scrollLeft=n),\"number\"==typeof r&&r>=0&&(o.scrollDirectionVertical=r>t.scrollTop?1:-1,o.scrollTop=r),\"number\"==typeof n&&n>=0&&n!==t.scrollLeft||\"number\"==typeof r&&r>=0&&r!==t.scrollTop?o:{}}},{key:\"_wrapSizeGetter\",value:function(e){return\"function\"==typeof e?e:function(){return e}}},{key:\"_getCalculatedScrollLeft\",value:function(e,t){var n=e.columnCount,r=e.height,o=e.scrollToAlignment,i=e.scrollToColumn,a=e.width,s=t.scrollLeft,l=t.instanceProps;if(n>0){var c=n-1,u=0>i?c:Math.min(c,i),d=l.rowSizeAndPositionManager.getTotalSize(),p=l.scrollbarSizeMeasured&&d>r?l.scrollbarSize:0;return l.columnSizeAndPositionManager.getUpdatedOffsetForIndex({align:o,containerSize:a-p,currentOffset:s,targetIndex:u})}return 0}},{key:\"_getScrollLeftForScrollToColumnStateUpdate\",value:function(t,n){var r=n.scrollLeft,o=e._getCalculatedScrollLeft(t,n);return\"number\"==typeof o&&o>=0&&r!==o?e._getScrollToPositionStateUpdate({prevState:n,scrollLeft:o,scrollTop:-1}):{}}},{key:\"_getCalculatedScrollTop\",value:function(e,t){var n=e.height,r=e.rowCount,o=e.scrollToAlignment,i=e.scrollToRow,a=e.width,s=t.scrollTop,l=t.instanceProps;if(r>0){var c=r-1,u=0>i?c:Math.min(c,i),d=l.columnSizeAndPositionManager.getTotalSize(),p=l.scrollbarSizeMeasured&&d>a?l.scrollbarSize:0;return l.rowSizeAndPositionManager.getUpdatedOffsetForIndex({align:o,containerSize:n-p,currentOffset:s,targetIndex:u})}return 0}},{key:\"_getScrollTopForScrollToRowStateUpdate\",value:function(t,n){var r=n.scrollTop,o=e._getCalculatedScrollTop(t,n);return\"number\"==typeof o&&o>=0&&r!==o?e._getScrollToPositionStateUpdate({prevState:n,scrollLeft:-1,scrollTop:o}):{}}}]),e}();s(et,\"defaultProps\",{\"aria-label\":\"grid\",\"aria-readonly\":!0,autoContainerWidth:!1,autoHeight:!1,autoWidth:!1,cellRangeRenderer:Q,containerRole:\"rowgroup\",containerStyle:{},estimatedColumnSize:100,estimatedRowSize:30,getScrollbarSize:j,noContentRenderer:function(){return null},onScroll:function(){},onScrollbarPresenceChange:function(){},onSectionRendered:function(){},overscanColumnCount:0,overscanIndicesGetter:R,overscanRowCount:10,role:\"grid\",scrollingResetTimeInterval:150,scrollToAlignment:\"auto\",scrollToColumn:-1,scrollToRow:-1,style:{},tabIndex:0,isScrollingOptOut:!1}),y(et);var tt=function(){function e(t,n){var r;return o(this,e),(r=m(this,p(e).call(this,t,n)))._loadMoreRowsMemoizer=k(),r._onRowsRendered=r._onRowsRendered.bind(f(r)),r._registerChild=r._registerChild.bind(f(r)),r}return d(e,n.PureComponent),a(e,[{key:\"resetLoadMoreRowsCache\",value:function(e){this._loadMoreRowsMemoizer=k(),e&&this._doStuff(this._lastRenderedStartIndex,this._lastRenderedStopIndex)}},{key:\"render\",value:function(){return this.props.children({onRowsRendered:this._onRowsRendered,registerChild:this._registerChild})}},{key:\"_loadUnloadedRanges\",value:function(e){var t=this,n=this.props.loadMoreRows;e.forEach(function(e){var r=n(e);r&&r.then(function(){(function(e){var t=e.lastRenderedStartIndex,n=e.lastRenderedStopIndex,r=e.startIndex,o=e.stopIndex;return!(r>n||t>o)})({lastRenderedStartIndex:t._lastRenderedStartIndex,lastRenderedStopIndex:t._lastRenderedStopIndex,startIndex:e.startIndex,stopIndex:e.stopIndex})&&t._registeredChild&&function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:0,n=\"function\"==typeof e.recomputeGridSize?e.recomputeGridSize:e.recomputeRowHeights;n?n.call(e,t):e.forceUpdate()}(t._registeredChild,t._lastRenderedStartIndex)})})}},{key:\"_onRowsRendered\",value:function(e){var t=e.startIndex,n=e.stopIndex;this._lastRenderedStartIndex=t,this._lastRenderedStopIndex=n,this._doStuff(t,n)}},{key:\"_doStuff\",value:function(e,t){var n,r=this,o=this.props,i=o.isRowLoaded,a=o.minimumBatchSize,s=o.rowCount,l=o.threshold,c=function(e){for(var t=e.isRowLoaded,n=e.minimumBatchSize,r=e.rowCount,o=e.startIndex,i=e.stopIndex,a=[],s=null,l=null,c=o;i>=c;c++)t({index:c})?null!==l&&(a.push({startIndex:s,stopIndex:l}),s=l=null):(l=c,null===s&&(s=c));if(null!==l){for(var u=Math.min(Math.max(l,s+n-1),r-1),d=l+1;u>=d&&!t({index:d});d++)l=d;a.push({startIndex:s,stopIndex:l})}if(a.length)for(var p=a[0];p.stopIndex-p.startIndex+1<n&&0<p.startIndex;){var h=p.startIndex-1;if(t({index:h}))break;p.startIndex=h}return a}({isRowLoaded:i,minimumBatchSize:a,rowCount:s,startIndex:Math.max(0,e-l),stopIndex:Math.min(s-1,t+l)}),u=(n=[]).concat.apply(n,M(c.map(function(e){return[e.startIndex,e.stopIndex]})));this._loadMoreRowsMemoizer({callback:function(){r._loadUnloadedRanges(c)},indices:{squashedUnloadedRanges:u}})}},{key:\"_registerChild\",value:function(e){this._registeredChild=e}}]),e}();s(tt,\"propTypes\",{children:je.func.isRequired,isRowLoaded:je.func.isRequired,loadMoreRows:je.func.isRequired,minimumBatchSize:je.number.isRequired,rowCount:je.number.isRequired,threshold:je.number.isRequired}),s(tt,\"defaultProps\",{minimumBatchSize:10,rowCount:0,threshold:15});var nt=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"Grid\",void 0),s(f(n),\"_cellRenderer\",function(e){var t=e.parent,r=e.rowIndex,o=e.style,i=e.isScrolling,a=e.isVisible,s=e.key,l=n.props.rowRenderer,c=Object.getOwnPropertyDescriptor(o,\"width\");return c&&c.writable&&(o.width=\"100%\"),l({index:r,style:o,isScrolling:i,isVisible:a,key:s,parent:t})}),s(f(n),\"_setRef\",function(e){n.Grid=e}),s(f(n),\"_onScroll\",function(e){var t=e.clientHeight,r=e.scrollHeight,o=e.scrollTop;n.props.onScroll({clientHeight:t,scrollHeight:r,scrollTop:o})}),s(f(n),\"_onSectionRendered\",function(e){var t=e.rowOverscanStartIndex,r=e.rowOverscanStopIndex,o=e.rowStartIndex,i=e.rowStopIndex;n.props.onRowsRendered({overscanStartIndex:t,overscanStopIndex:r,startIndex:o,stopIndex:i})}),n}return d(e,n.PureComponent),a(e,[{key:\"forceUpdateGrid\",value:function(){this.Grid&&this.Grid.forceUpdate()}},{key:\"getOffsetForRow\",value:function(e){var t=e.alignment,n=e.index;return this.Grid?this.Grid.getOffsetForCell({alignment:t,rowIndex:n,columnIndex:0}).scrollTop:0}},{key:\"invalidateCellSizeAfterRender\",value:function(e){var t=e.columnIndex,n=e.rowIndex;this.Grid&&this.Grid.invalidateCellSizeAfterRender({rowIndex:n,columnIndex:t})}},{key:\"measureAllRows\",value:function(){this.Grid&&this.Grid.measureAllCells()}},{key:\"recomputeGridSize\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.columnIndex,r=void 0===n?0:n,o=t.rowIndex,i=void 0===o?0:o;this.Grid&&this.Grid.recomputeGridSize({rowIndex:i,columnIndex:r})}},{key:\"recomputeRowHeights\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.recomputeGridSize({rowIndex:t,columnIndex:0})}},{key:\"scrollToPosition\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.scrollToPosition({scrollTop:t})}},{key:\"scrollToRow\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.scrollToCell({columnIndex:0,rowIndex:t})}},{key:\"render\",value:function(){var e=this.props,t=e.className,r=e.noRowsRenderer,o=e.scrollToIndex,i=e.width,a=L(\"ReactVirtualized__List\",t);return n.createElement(et,l({},this.props,{autoContainerWidth:!0,cellRenderer:this._cellRenderer,className:a,columnWidth:i,columnCount:1,noContentRenderer:r,onScroll:this._onScroll,onSectionRendered:this._onSectionRendered,ref:this._setRef,scrollToRow:o}))}}]),e}();s(nt,\"defaultProps\",{autoHeight:!1,estimatedRowSize:30,onScroll:function(){},noRowsRenderer:function(){return null},onRowsRendered:function(){},overscanIndicesGetter:H,overscanRowCount:10,scrollToAlignment:\"auto\",scrollToIndex:-1,style:{}});var rt={ge:function(e,t,n,r,o){return\"function\"==typeof n?function(e,t,n,r,o){for(var i=n+1;n>=t;){var a=t+n>>>1;0<=o(e[a],r)?n=(i=a)-1:t=1+a}return i}(e,void 0===r?0:0|r,void 0===o?e.length-1:0|o,t,n):function(e,t,n,r){for(var o=n+1;n>=t;){var i=t+n>>>1;r<=e[i]?n=(o=i)-1:t=1+i}return o}(e,void 0===n?0:0|n,void 0===r?e.length-1:0|r,t)},gt:function(e,t,n,r,o){return\"function\"==typeof n?function(e,t,n,r,o){for(var i=n+1;n>=t;){var a=t+n>>>1;0<o(e[a],r)?n=(i=a)-1:t=1+a}return i}(e,void 0===r?0:0|r,void 0===o?e.length-1:0|o,t,n):function(e,t,n,r){for(var o=n+1;n>=t;){var i=t+n>>>1;r<e[i]?n=(o=i)-1:t=1+i}return o}(e,void 0===n?0:0|n,void 0===r?e.length-1:0|r,t)},lt:function(e,t,n,r,o){return\"function\"==typeof n?function(e,t,n,r,o){for(var i=t-1;n>=t;){var a=t+n>>>1;o(e[a],r)<0?t=1+(i=a):n=a-1}return i}(e,void 0===r?0:0|r,void 0===o?e.length-1:0|o,t,n):function(e,t,n,r){for(var o=t-1;n>=t;){var i=t+n>>>1;e[i]<r?t=1+(o=i):n=i-1}return o}(e,void 0===n?0:0|n,void 0===r?e.length-1:0|r,t)},le:function(e,t,n,r,o){return\"function\"==typeof n?function(e,t,n,r,o){for(var i=t-1;n>=t;){var a=t+n>>>1;o(e[a],r)<=0?t=1+(i=a):n=a-1}return i}(e,void 0===r?0:0|r,void 0===o?e.length-1:0|o,t,n):function(e,t,n,r){for(var o=t-1;n>=t;){var i=t+n>>>1;e[i]<=r?t=1+(o=i):n=i-1}return o}(e,void 0===n?0:0|n,void 0===r?e.length-1:0|r,t)},eq:function(e,t,n,r,o){return\"function\"==typeof n?function(e,t,n,r,o){for(;n>=t;){var i=t+n>>>1,a=o(e[i],r);if(0===a)return i;0>=a?t=1+i:n=i-1}return-1}(e,void 0===r?0:0|r,void 0===o?e.length-1:0|o,t,n):function(e,t,n,r){for(;n>=t;){var o=t+n>>>1,i=e[o];if(i===r)return o;r>=i?t=1+o:n=o-1}return-1}(e,void 0===n?0:0|n,void 0===r?e.length-1:0|r,t)}},ot=F.prototype;ot.intervals=function(e){return e.push.apply(e,this.leftPoints),this.left&&this.left.intervals(e),this.right&&this.right.intervals(e),e},ot.insert=function(e){var t=this.count-this.leftPoints.length;if(this.count+=1,e[1]<this.mid)this.left?4*(this.left.count+1)>3*(1+t)?Y(this,e):this.left.insert(e):this.left=Z([e]);else if(e[0]>this.mid)this.right?4*(this.right.count+1)>3*(1+t)?Y(this,e):this.right.insert(e):this.right=Z([e]);else{var n=rt.ge(this.leftPoints,e,J),r=rt.ge(this.rightPoints,e,K);this.leftPoints.splice(n,0,e),this.rightPoints.splice(r,0,e)}},ot.remove=function(e){var t=this.count-this.leftPoints;if(e[1]<this.mid)return this.left?3*(t-1)<4*(this.right?this.right.count:0)?G(this,e):2===(i=this.left.remove(e))?(this.left=null,this.count-=1,1):(1===i&&(this.count-=1),i):0;if(e[0]>this.mid)return this.right?3*(t-1)<4*(this.left?this.left.count:0)?G(this,e):2===(i=this.right.remove(e))?(this.right=null,this.count-=1,1):(1===i&&(this.count-=1),i):0;if(1===this.count)return this.leftPoints[0]===e?2:0;if(1===this.leftPoints.length&&this.leftPoints[0]===e){if(this.left&&this.right){for(var n=this,r=this.left;r.right;)r=(n=r).right;if(n===this)r.right=this.right;else{var o=this.left;i=this.right,n.count-=r.count,n.right=r.left,r.left=o,r.right=i}V(this,r),this.count=(this.left?this.left.count:0)+(this.right?this.right.count:0)+this.leftPoints.length}else this.left?V(this,this.left):V(this,this.right);return 1}for(o=rt.ge(this.leftPoints,e,J);o<this.leftPoints.length&&this.leftPoints[o][0]===e[0];++o)if(this.leftPoints[o]===e)for(this.count-=1,this.leftPoints.splice(o,1),i=rt.ge(this.rightPoints,e,K);i<this.rightPoints.length&&this.rightPoints[i][1]===e[1];++i)if(this.rightPoints[i]===e)return this.rightPoints.splice(i,1),1;return 0;var i},ot.queryPoint=function(e,t){if(e<this.mid)return this.left&&(n=this.left.queryPoint(e,t))?n:W(this.leftPoints,e,t);if(e>this.mid){var n;return this.right&&(n=this.right.queryPoint(e,t))?n:X(this.rightPoints,e,t)}return _(this.leftPoints,t)},ot.queryInterval=function(e,t,n){var r;return e<this.mid&&this.left&&(r=this.left.queryInterval(e,t,n))?r:t>this.mid&&this.right&&(r=this.right.queryInterval(e,t,n))?r:t<this.mid?W(this.leftPoints,t,n):e>this.mid?X(this.rightPoints,e,n):_(this.leftPoints,n)};var it=$.prototype;it.insert=function(e){this.root?this.root.insert(e):this.root=new F(e[0],null,null,[e],[e])},it.remove=function(e){if(this.root){var t=this.root.remove(e);return 2===t&&(this.root=null),0!==t}return!1},it.queryPoint=function(e,t){return this.root?this.root.queryPoint(e,t):void 0},it.queryInterval=function(e,t,n){return t>=e&&this.root?this.root.queryInterval(e,t,n):void 0},Object.defineProperty(it,\"count\",{get:function(){return this.root?this.root.count:0}}),Object.defineProperty(it,\"intervals\",{get:function(){return this.root?this.root.intervals([]):[]}});var at=function(){function e(){o(this,e),s(this,\"_columnSizeMap\",{}),s(this,\"_intervalTree\",function(e){return new $(e&&0!==e.length?Z(e):null)}()),s(this,\"_leftMap\",{})}return a(e,[{key:\"estimateTotalHeight\",value:function(e,t,n){var r=e-this.count;return this.tallestColumnSize+Math.ceil(r/t)*n}},{key:\"range\",value:function(e,t,n){var r=this;this._intervalTree.queryInterval(e,e+t,function(e){var t=A(e,3),o=t[0],i=(t[1],t[2]);return n(i,r._leftMap[i],o)})}},{key:\"setPosition\",value:function(e,t,n,r){this._intervalTree.insert([n,n+r,e]),this._leftMap[e]=t;var o=this._columnSizeMap,i=o[t];o[t]=void 0===i?n+r:Math.max(i,n+r)}},{key:\"count\",get:function(){return this._intervalTree.count}},{key:\"shortestColumnSize\",get:function(){var e=this._columnSizeMap,t=0;for(var n in e){var r=e[n];t=0===t?r:Math.min(t,r)}return t}},{key:\"tallestColumnSize\",get:function(){var e=this._columnSizeMap,t=0;for(var n in e){var r=e[n];t=Math.max(t,r)}return t}}]),e}(),st=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"state\",{isScrolling:!1,scrollTop:0}),s(f(n),\"_debounceResetIsScrollingId\",void 0),s(f(n),\"_invalidateOnUpdateStartIndex\",null),s(f(n),\"_invalidateOnUpdateStopIndex\",null),s(f(n),\"_positionCache\",new at),s(f(n),\"_startIndex\",null),s(f(n),\"_startIndexMemoized\",null),s(f(n),\"_stopIndex\",null),s(f(n),\"_stopIndexMemoized\",null),s(f(n),\"_debounceResetIsScrollingCallback\",function(){n.setState({isScrolling:!1})}),s(f(n),\"_setScrollingContainerRef\",function(e){n._scrollingContainer=e}),s(f(n),\"_onScroll\",function(e){var t=n.props.height,r=e.currentTarget.scrollTop,o=Math.min(Math.max(0,n._getEstimatedTotalHeight()-t),r);r===o&&(n._debounceResetIsScrolling(),n.state.scrollTop!==o&&n.setState({isScrolling:!0,scrollTop:o}))}),n}return d(e,n.PureComponent),a(e,[{key:\"clearCellPositions\",value:function(){this._positionCache=new at,this.forceUpdate()}},{key:\"invalidateCellSizeAfterRender\",value:function(e){var t=e.rowIndex;null===this._invalidateOnUpdateStartIndex?(this._invalidateOnUpdateStartIndex=t,this._invalidateOnUpdateStopIndex=t):(this._invalidateOnUpdateStartIndex=Math.min(this._invalidateOnUpdateStartIndex,t),this._invalidateOnUpdateStopIndex=Math.max(this._invalidateOnUpdateStopIndex,t))}},{key:\"recomputeCellPositions\",value:function(){var e=this._positionCache.count-1;this._positionCache=new at,this._populatePositionCache(0,e),this.forceUpdate()}},{key:\"componentDidMount\",value:function(){this._checkInvalidateOnUpdate(),this._invokeOnScrollCallback(),this._invokeOnCellsRenderedCallback()}},{key:\"componentDidUpdate\",value:function(e){this._checkInvalidateOnUpdate(),this._invokeOnScrollCallback(),this._invokeOnCellsRenderedCallback(),this.props.scrollTop!==e.scrollTop&&this._debounceResetIsScrolling()}},{key:\"componentWillUnmount\",value:function(){this._debounceResetIsScrollingId&&Je(this._debounceResetIsScrollingId)}},{key:\"render\",value:function(){var e,t=this,r=this.props,o=r.autoHeight,i=r.cellCount,a=r.cellMeasurerCache,l=r.cellRenderer,c=r.className,d=r.height,p=r.id,h=r.keyMapper,g=r.overscanByPixels,f=r.role,m=r.style,A=r.tabIndex,M=r.width,w=r.rowDirection,v=this.state,b=v.isScrolling,y=v.scrollTop,x=[],T=this._getEstimatedTotalHeight(),C=this._positionCache.shortestColumnSize,N=this._positionCache.count,I=0;if(this._positionCache.range(Math.max(0,y-g),d+2*g,function(n,r,o){var i;e=void 0===e?I=n:(I=Math.min(I,n),Math.max(e,n)),x.push(l({index:n,isScrolling:b,key:h(n),parent:t,style:(i={height:a.getHeight(n)},s(i,\"ltr\"===w?\"left\":\"right\",r),s(i,\"position\",\"absolute\"),s(i,\"top\",o),s(i,\"width\",a.getWidth(n)),i)}))}),y+d+g>C&&i>N)for(var E=Math.min(i-N,Math.ceil((y+d+g-C)/a.defaultHeight*M/a.defaultWidth)),D=N;N+E>D;D++)e=D,x.push(l({index:D,isScrolling:b,key:h(D),parent:this,style:{width:a.getWidth(D)}}));return this._startIndex=I,this._stopIndex=e,n.createElement(\"div\",{ref:this._setScrollingContainerRef,\"aria-label\":this.props[\"aria-label\"],className:L(\"ReactVirtualized__Masonry\",c),id:p,onScroll:this._onScroll,role:f,style:u({boxSizing:\"border-box\",direction:\"ltr\",height:o?\"auto\":d,overflowX:\"hidden\",overflowY:d>T?\"hidden\":\"auto\",position:\"relative\",width:M,WebkitOverflowScrolling:\"touch\",willChange:\"transform\"},m),tabIndex:A},n.createElement(\"div\",{className:\"ReactVirtualized__Masonry__innerScrollContainer\",style:{width:\"100%\",height:T,maxWidth:\"100%\",maxHeight:T,overflow:\"hidden\",pointerEvents:b?\"none\":\"\",position:\"relative\"}},x))}},{key:\"_checkInvalidateOnUpdate\",value:function(){if(\"number\"==typeof this._invalidateOnUpdateStartIndex){var e=this._invalidateOnUpdateStartIndex,t=this._invalidateOnUpdateStopIndex;this._invalidateOnUpdateStartIndex=null,this._invalidateOnUpdateStopIndex=null,this._populatePositionCache(e,t),this.forceUpdate()}}},{key:\"_debounceResetIsScrolling\",value:function(){var e=this.props.scrollingResetTimeInterval;this._debounceResetIsScrollingId&&Je(this._debounceResetIsScrollingId),this._debounceResetIsScrollingId=Ke(this._debounceResetIsScrollingCallback,e)}},{key:\"_getEstimatedTotalHeight\",value:function(){var e=this.props,t=e.cellCount,n=e.cellMeasurerCache,r=e.width,o=Math.max(1,Math.floor(r/n.defaultWidth));return this._positionCache.estimateTotalHeight(t,o,n.defaultHeight)}},{key:\"_invokeOnScrollCallback\",value:function(){var e=this.props,t=e.height,n=e.onScroll,r=this.state.scrollTop;this._onScrollMemoized!==r&&(n({clientHeight:t,scrollHeight:this._getEstimatedTotalHeight(),scrollTop:r}),this._onScrollMemoized=r)}},{key:\"_invokeOnCellsRenderedCallback\",value:function(){this._startIndexMemoized===this._startIndex&&this._stopIndexMemoized===this._stopIndex||(this.props.onCellsRendered({startIndex:this._startIndex,stopIndex:this._stopIndex}),this._startIndexMemoized=this._startIndex,this._stopIndexMemoized=this._stopIndex)}},{key:\"_populatePositionCache\",value:function(e,t){for(var n=this.props,r=n.cellMeasurerCache,o=n.cellPositioner,i=e;t>=i;i++){var a=o(i),s=a.left,l=a.top;this._positionCache.setPosition(i,s,l,r.getHeight(i))}}}],[{key:\"getDerivedStateFromProps\",value:function(e,t){return void 0!==e.scrollTop&&t.scrollTop!==e.scrollTop?{isScrolling:!0,scrollTop:e.scrollTop}:null}}]),e}();s(st,\"defaultProps\",{autoHeight:!1,keyMapper:function(e){return e},onCellsRendered:ee,onScroll:ee,overscanByPixels:20,role:\"grid\",scrollingResetTimeInterval:150,style:{},tabIndex:0,rowDirection:\"ltr\"}),y(st);var lt=function(){function e(){var t=this,n=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};o(this,e),s(this,\"_cellMeasurerCache\",void 0),s(this,\"_columnIndexOffset\",void 0),s(this,\"_rowIndexOffset\",void 0),s(this,\"columnWidth\",function(e){var n=e.index;t._cellMeasurerCache.columnWidth({index:n+t._columnIndexOffset})}),s(this,\"rowHeight\",function(e){var n=e.index;t._cellMeasurerCache.rowHeight({index:n+t._rowIndexOffset})});var r=n.cellMeasurerCache,i=n.columnIndexOffset,a=void 0===i?0:i,l=n.rowIndexOffset,c=void 0===l?0:l;this._cellMeasurerCache=r,this._columnIndexOffset=a,this._rowIndexOffset=c}return a(e,[{key:\"clear\",value:function(e,t){this._cellMeasurerCache.clear(e+this._rowIndexOffset,t+this._columnIndexOffset)}},{key:\"clearAll\",value:function(){this._cellMeasurerCache.clearAll()}},{key:\"hasFixedHeight\",value:function(){return this._cellMeasurerCache.hasFixedHeight()}},{key:\"hasFixedWidth\",value:function(){return this._cellMeasurerCache.hasFixedWidth()}},{key:\"getHeight\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0;return this._cellMeasurerCache.getHeight(e+this._rowIndexOffset,n+this._columnIndexOffset)}},{key:\"getWidth\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0;return this._cellMeasurerCache.getWidth(e+this._rowIndexOffset,n+this._columnIndexOffset)}},{key:\"has\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:0;return this._cellMeasurerCache.has(e+this._rowIndexOffset,n+this._columnIndexOffset)}},{key:\"set\",value:function(e,t,n,r){this._cellMeasurerCache.set(e+this._rowIndexOffset,t+this._columnIndexOffset,n,r)}},{key:\"defaultHeight\",get:function(){return this._cellMeasurerCache.defaultHeight}},{key:\"defaultWidth\",get:function(){return this._cellMeasurerCache.defaultWidth}}]),e}(),ct=function(){function e(t,r){var i;o(this,e),s(f(i=m(this,p(e).call(this,t,r))),\"state\",{scrollLeft:0,scrollTop:0,scrollbarSize:0,showHorizontalScrollbar:!1,showVerticalScrollbar:!1}),s(f(i),\"_deferredInvalidateColumnIndex\",null),s(f(i),\"_deferredInvalidateRowIndex\",null),s(f(i),\"_bottomLeftGridRef\",function(e){i._bottomLeftGrid=e}),s(f(i),\"_bottomRightGridRef\",function(e){i._bottomRightGrid=e}),s(f(i),\"_cellRendererBottomLeftGrid\",function(e){var t=e.rowIndex,r=g(e,[\"rowIndex\"]),o=i.props,a=o.cellRenderer,s=o.fixedRowCount;return t===o.rowCount-s?n.createElement(\"div\",{key:r.key,style:u({},r.style,{height:20})}):a(u({},r,{parent:f(i),rowIndex:t+s}))}),s(f(i),\"_cellRendererBottomRightGrid\",function(e){var t=e.columnIndex,n=e.rowIndex,r=g(e,[\"columnIndex\",\"rowIndex\"]),o=i.props,a=o.cellRenderer,s=o.fixedColumnCount,l=o.fixedRowCount;return a(u({},r,{columnIndex:t+s,parent:f(i),rowIndex:n+l}))}),s(f(i),\"_cellRendererTopRightGrid\",function(e){var t=e.columnIndex,r=g(e,[\"columnIndex\"]),o=i.props,a=o.cellRenderer,s=o.columnCount,l=o.fixedColumnCount;return t===s-l?n.createElement(\"div\",{key:r.key,style:u({},r.style,{width:20})}):a(u({},r,{columnIndex:t+l,parent:f(i)}))}),s(f(i),\"_columnWidthRightGrid\",function(e){var t=e.index,n=i.props,r=n.columnCount,o=n.fixedColumnCount,a=n.columnWidth,s=i.state,l=s.scrollbarSize;return s.showHorizontalScrollbar&&t===r-o?l:\"function\"==typeof a?a({index:t+o}):a}),s(f(i),\"_onScroll\",function(e){var t=e.scrollLeft,n=e.scrollTop;i.setState({scrollLeft:t,scrollTop:n});var r=i.props.onScroll;r&&r(e)}),s(f(i),\"_onScrollbarPresenceChange\",function(e){var t=e.horizontal,n=e.size,r=e.vertical,o=i.state,a=o.showHorizontalScrollbar,s=o.showVerticalScrollbar;if(t!==a||r!==s){i.setState({scrollbarSize:n,showHorizontalScrollbar:t,showVerticalScrollbar:r});var l=i.props.onScrollbarPresenceChange;\"function\"==typeof l&&l({horizontal:t,size:n,vertical:r})}}),s(f(i),\"_onScrollLeft\",function(e){var t=e.scrollLeft;i._onScroll({scrollLeft:t,scrollTop:i.state.scrollTop})}),s(f(i),\"_onScrollTop\",function(e){var t=e.scrollTop;i._onScroll({scrollTop:t,scrollLeft:i.state.scrollLeft})}),s(f(i),\"_rowHeightBottomGrid\",function(e){var t=e.index,n=i.props,r=n.fixedRowCount,o=n.rowCount,a=n.rowHeight,s=i.state,l=s.scrollbarSize;return s.showVerticalScrollbar&&t===o-r?l:\"function\"==typeof a?a({index:t+r}):a}),s(f(i),\"_topLeftGridRef\",function(e){i._topLeftGrid=e}),s(f(i),\"_topRightGridRef\",function(e){i._topRightGrid=e});var a=t.deferredMeasurementCache,l=t.fixedColumnCount,c=t.fixedRowCount;return i._maybeCalculateCachedStyles(!0),a&&(i._deferredMeasurementCacheBottomLeftGrid=c>0?new lt({cellMeasurerCache:a,columnIndexOffset:0,rowIndexOffset:c}):a,i._deferredMeasurementCacheBottomRightGrid=l>0||c>0?new lt({cellMeasurerCache:a,columnIndexOffset:l,rowIndexOffset:c}):a,i._deferredMeasurementCacheTopRightGrid=l>0?new lt({cellMeasurerCache:a,columnIndexOffset:l,rowIndexOffset:0}):a),i}return d(e,n.PureComponent),a(e,[{key:\"forceUpdateGrids\",value:function(){this._bottomLeftGrid&&this._bottomLeftGrid.forceUpdate(),this._bottomRightGrid&&this._bottomRightGrid.forceUpdate(),this._topLeftGrid&&this._topLeftGrid.forceUpdate(),this._topRightGrid&&this._topRightGrid.forceUpdate()}},{key:\"invalidateCellSizeAfterRender\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.columnIndex,r=void 0===n?0:n,o=t.rowIndex,i=void 0===o?0:o;this._deferredInvalidateColumnIndex=\"number\"==typeof this._deferredInvalidateColumnIndex?Math.min(this._deferredInvalidateColumnIndex,r):r,this._deferredInvalidateRowIndex=\"number\"==typeof this._deferredInvalidateRowIndex?Math.min(this._deferredInvalidateRowIndex,i):i}},{key:\"measureAllCells\",value:function(){this._bottomLeftGrid&&this._bottomLeftGrid.measureAllCells(),this._bottomRightGrid&&this._bottomRightGrid.measureAllCells(),this._topLeftGrid&&this._topLeftGrid.measureAllCells(),this._topRightGrid&&this._topRightGrid.measureAllCells()}},{key:\"recomputeGridSize\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.columnIndex,r=void 0===n?0:n,o=t.rowIndex,i=void 0===o?0:o,a=this.props,s=a.fixedColumnCount,l=a.fixedRowCount,c=Math.max(0,r-s),u=Math.max(0,i-l);this._bottomLeftGrid&&this._bottomLeftGrid.recomputeGridSize({columnIndex:r,rowIndex:u}),this._bottomRightGrid&&this._bottomRightGrid.recomputeGridSize({columnIndex:c,rowIndex:u}),this._topLeftGrid&&this._topLeftGrid.recomputeGridSize({columnIndex:r,rowIndex:i}),this._topRightGrid&&this._topRightGrid.recomputeGridSize({columnIndex:c,rowIndex:i}),this._leftGridWidth=null,this._topGridHeight=null,this._maybeCalculateCachedStyles(!0)}},{key:\"componentDidMount\",value:function(){var e=this.props,t=e.scrollLeft,n=e.scrollTop;if(t>0||n>0){var r={};t>0&&(r.scrollLeft=t),n>0&&(r.scrollTop=n),this.setState(r)}this._handleInvalidatedGridSize()}},{key:\"componentDidUpdate\",value:function(){this._handleInvalidatedGridSize()}},{key:\"render\",value:function(){var e=this.props,t=e.onScroll,r=e.onSectionRendered,o=(e.onScrollbarPresenceChange,e.scrollLeft,e.scrollToColumn),i=(e.scrollTop,e.scrollToRow),a=g(e,[\"onScroll\",\"onSectionRendered\",\"onScrollbarPresenceChange\",\"scrollLeft\",\"scrollToColumn\",\"scrollTop\",\"scrollToRow\"]);if(this._prepareForRender(),0===this.props.width||0===this.props.height)return null;var s=this.state,l=s.scrollLeft,c=s.scrollTop;return n.createElement(\"div\",{style:this._containerOuterStyle},n.createElement(\"div\",{style:this._containerTopStyle},this._renderTopLeftGrid(a),this._renderTopRightGrid(u({},a,{onScroll:t,scrollLeft:l}))),n.createElement(\"div\",{style:this._containerBottomStyle},this._renderBottomLeftGrid(u({},a,{onScroll:t,scrollTop:c})),this._renderBottomRightGrid(u({},a,{onScroll:t,onSectionRendered:r,scrollLeft:l,scrollToColumn:o,scrollToRow:i,scrollTop:c}))))}},{key:\"_getBottomGridHeight\",value:function(e){return e.height-this._getTopGridHeight(e)}},{key:\"_getLeftGridWidth\",value:function(e){var t=e.fixedColumnCount,n=e.columnWidth;if(null==this._leftGridWidth)if(\"function\"==typeof n){for(var r=0,o=0;t>o;o++)r+=n({index:o});this._leftGridWidth=r}else this._leftGridWidth=n*t;return this._leftGridWidth}},{key:\"_getRightGridWidth\",value:function(e){return e.width-this._getLeftGridWidth(e)}},{key:\"_getTopGridHeight\",value:function(e){var t=e.fixedRowCount,n=e.rowHeight;if(null==this._topGridHeight)if(\"function\"==typeof n){for(var r=0,o=0;t>o;o++)r+=n({index:o});this._topGridHeight=r}else this._topGridHeight=n*t;return this._topGridHeight}},{key:\"_handleInvalidatedGridSize\",value:function(){if(\"number\"==typeof this._deferredInvalidateColumnIndex){var e=this._deferredInvalidateColumnIndex,t=this._deferredInvalidateRowIndex;this._deferredInvalidateColumnIndex=null,this._deferredInvalidateRowIndex=null,this.recomputeGridSize({columnIndex:e,rowIndex:t}),this.forceUpdate()}}},{key:\"_maybeCalculateCachedStyles\",value:function(e){var t=this.props,n=t.columnWidth,r=t.enableFixedColumnScroll,o=t.enableFixedRowScroll,i=t.height,a=t.fixedColumnCount,s=t.fixedRowCount,l=t.rowHeight,c=t.style,d=t.styleBottomLeftGrid,p=t.styleBottomRightGrid,h=t.styleTopLeftGrid,g=t.styleTopRightGrid,f=t.width,m=e||i!==this._lastRenderedHeight||f!==this._lastRenderedWidth,A=e||n!==this._lastRenderedColumnWidth||a!==this._lastRenderedFixedColumnCount,M=e||s!==this._lastRenderedFixedRowCount||l!==this._lastRenderedRowHeight;\n(e||m||c!==this._lastRenderedStyle)&&(this._containerOuterStyle=u({height:i,overflow:\"visible\",width:f},c)),(e||m||M)&&(this._containerTopStyle={height:this._getTopGridHeight(this.props),position:\"relative\",width:f},this._containerBottomStyle={height:i-this._getTopGridHeight(this.props),overflow:\"visible\",position:\"relative\",width:f}),!e&&d===this._lastRenderedStyleBottomLeftGrid||(this._bottomLeftGridStyle=u({left:0,overflowX:\"hidden\",overflowY:r?\"auto\":\"hidden\",position:\"absolute\"},d)),(e||A||p!==this._lastRenderedStyleBottomRightGrid)&&(this._bottomRightGridStyle=u({left:this._getLeftGridWidth(this.props),position:\"absolute\"},p)),!e&&h===this._lastRenderedStyleTopLeftGrid||(this._topLeftGridStyle=u({left:0,overflowX:\"hidden\",overflowY:\"hidden\",position:\"absolute\",top:0},h)),(e||A||g!==this._lastRenderedStyleTopRightGrid)&&(this._topRightGridStyle=u({left:this._getLeftGridWidth(this.props),overflowX:o?\"auto\":\"hidden\",overflowY:\"hidden\",position:\"absolute\",top:0},g)),this._lastRenderedColumnWidth=n,this._lastRenderedFixedColumnCount=a,this._lastRenderedFixedRowCount=s,this._lastRenderedHeight=i,this._lastRenderedRowHeight=l,this._lastRenderedStyle=c,this._lastRenderedStyleBottomLeftGrid=d,this._lastRenderedStyleBottomRightGrid=p,this._lastRenderedStyleTopLeftGrid=h,this._lastRenderedStyleTopRightGrid=g,this._lastRenderedWidth=f}},{key:\"_prepareForRender\",value:function(){this._lastRenderedColumnWidth===this.props.columnWidth&&this._lastRenderedFixedColumnCount===this.props.fixedColumnCount||(this._leftGridWidth=null),this._lastRenderedFixedRowCount===this.props.fixedRowCount&&this._lastRenderedRowHeight===this.props.rowHeight||(this._topGridHeight=null),this._maybeCalculateCachedStyles(),this._lastRenderedColumnWidth=this.props.columnWidth,this._lastRenderedFixedColumnCount=this.props.fixedColumnCount,this._lastRenderedFixedRowCount=this.props.fixedRowCount,this._lastRenderedRowHeight=this.props.rowHeight}},{key:\"_renderBottomLeftGrid\",value:function(e){var t=e.enableFixedColumnScroll,r=e.fixedColumnCount,o=e.fixedRowCount,i=e.rowCount,a=e.hideBottomLeftGridScrollbar,s=this.state.showVerticalScrollbar;if(!r)return null;var c=s?1:0,d=this._getBottomGridHeight(e),p=this._getLeftGridWidth(e),h=this.state.showVerticalScrollbar?this.state.scrollbarSize:0,g=a?p+h:p,f=n.createElement(et,l({},e,{cellRenderer:this._cellRendererBottomLeftGrid,className:this.props.classNameBottomLeftGrid,columnCount:r,deferredMeasurementCache:this._deferredMeasurementCacheBottomLeftGrid,height:d,onScroll:t?this._onScrollTop:void 0,ref:this._bottomLeftGridRef,rowCount:Math.max(0,i-o)+c,rowHeight:this._rowHeightBottomGrid,style:this._bottomLeftGridStyle,tabIndex:null,width:g}));return a?n.createElement(\"div\",{className:\"BottomLeftGrid_ScrollWrapper\",style:u({},this._bottomLeftGridStyle,{height:d,width:p,overflowY:\"hidden\"})},f):f}},{key:\"_renderBottomRightGrid\",value:function(e){var t=e.columnCount,r=e.fixedColumnCount,o=e.fixedRowCount,i=e.rowCount,a=e.scrollToColumn,s=e.scrollToRow;return n.createElement(et,l({},e,{cellRenderer:this._cellRendererBottomRightGrid,className:this.props.classNameBottomRightGrid,columnCount:Math.max(0,t-r),columnWidth:this._columnWidthRightGrid,deferredMeasurementCache:this._deferredMeasurementCacheBottomRightGrid,height:this._getBottomGridHeight(e),onScroll:this._onScroll,onScrollbarPresenceChange:this._onScrollbarPresenceChange,ref:this._bottomRightGridRef,rowCount:Math.max(0,i-o),rowHeight:this._rowHeightBottomGrid,scrollToColumn:a-r,scrollToRow:s-o,style:this._bottomRightGridStyle,width:this._getRightGridWidth(e)}))}},{key:\"_renderTopLeftGrid\",value:function(e){var t=e.fixedColumnCount,r=e.fixedRowCount;return t&&r?n.createElement(et,l({},e,{className:this.props.classNameTopLeftGrid,columnCount:t,height:this._getTopGridHeight(e),ref:this._topLeftGridRef,rowCount:r,style:this._topLeftGridStyle,tabIndex:null,width:this._getLeftGridWidth(e)})):null}},{key:\"_renderTopRightGrid\",value:function(e){var t=e.columnCount,r=e.enableFixedRowScroll,o=e.fixedColumnCount,i=e.fixedRowCount,a=e.scrollLeft,s=e.hideTopRightGridScrollbar,c=this.state,d=c.showHorizontalScrollbar,p=c.scrollbarSize;if(!i)return null;var h=d?1:0,g=this._getTopGridHeight(e),f=this._getRightGridWidth(e),m=d?p:0,A=g,M=this._topRightGridStyle;s&&(A=g+m,M=u({},this._topRightGridStyle,{left:0}));var w=n.createElement(et,l({},e,{cellRenderer:this._cellRendererTopRightGrid,className:this.props.classNameTopRightGrid,columnCount:Math.max(0,t-o)+h,columnWidth:this._columnWidthRightGrid,deferredMeasurementCache:this._deferredMeasurementCacheTopRightGrid,height:A,onScroll:r?this._onScrollLeft:void 0,ref:this._topRightGridRef,rowCount:i,scrollLeft:a,style:M,tabIndex:null,width:f}));return s?n.createElement(\"div\",{className:\"TopRightGrid_ScrollWrapper\",style:u({},this._topRightGridStyle,{height:g,width:f,overflowX:\"hidden\"})},w):w}}],[{key:\"getDerivedStateFromProps\",value:function(e,t){return e.scrollLeft!==t.scrollLeft||e.scrollTop!==t.scrollTop?{scrollLeft:null!=e.scrollLeft&&0<=e.scrollLeft?e.scrollLeft:t.scrollLeft,scrollTop:null!=e.scrollTop&&0<=e.scrollTop?e.scrollTop:t.scrollTop}:null}}]),e}();s(ct,\"propTypes\",{classNameBottomLeftGrid:je.string.isRequired,classNameBottomRightGrid:je.string.isRequired,classNameTopLeftGrid:je.string.isRequired,classNameTopRightGrid:je.string.isRequired,enableFixedColumnScroll:je.bool.isRequired,enableFixedRowScroll:je.bool.isRequired,fixedColumnCount:je.number.isRequired,fixedRowCount:je.number.isRequired,onScrollbarPresenceChange:je.func,style:je.object.isRequired,styleBottomLeftGrid:je.object.isRequired,styleBottomRightGrid:je.object.isRequired,styleTopLeftGrid:je.object.isRequired,styleTopRightGrid:je.object.isRequired,hideTopRightGridScrollbar:je.bool,hideBottomLeftGridScrollbar:je.bool}),s(ct,\"defaultProps\",{classNameBottomLeftGrid:\"\",classNameBottomRightGrid:\"\",classNameTopLeftGrid:\"\",classNameTopRightGrid:\"\",enableFixedColumnScroll:!1,enableFixedRowScroll:!1,fixedColumnCount:0,fixedRowCount:0,scrollToColumn:-1,scrollToRow:-1,style:{},styleBottomLeftGrid:{},styleBottomRightGrid:{},styleTopLeftGrid:{},styleTopRightGrid:{},hideTopRightGridScrollbar:!1,hideBottomLeftGridScrollbar:!1}),y(ct);var ut=function(){function e(t,n){var r;return o(this,e),(r=m(this,p(e).call(this,t,n))).state={clientHeight:0,clientWidth:0,scrollHeight:0,scrollLeft:0,scrollTop:0,scrollWidth:0},r._onScroll=r._onScroll.bind(f(r)),r}return d(e,n.PureComponent),a(e,[{key:\"render\",value:function(){var e=this.props.children,t=this.state,n=t.clientHeight,r=t.clientWidth,o=t.scrollHeight,i=t.scrollLeft,a=t.scrollTop,s=t.scrollWidth;return e({clientHeight:n,clientWidth:r,onScroll:this._onScroll,scrollHeight:o,scrollLeft:i,scrollTop:a,scrollWidth:s})}},{key:\"_onScroll\",value:function(e){var t=e.clientHeight,n=e.clientWidth,r=e.scrollHeight,o=e.scrollLeft,i=e.scrollTop,a=e.scrollWidth;this.setState({clientHeight:t,clientWidth:n,scrollHeight:r,scrollLeft:o,scrollTop:i,scrollWidth:a})}}]),e}();s(ut,\"propTypes\",{children:je.func.isRequired});var dt={ASC:\"ASC\",DESC:\"DESC\"};oe.propTypes={sortDirection:je.oneOf([dt.ASC,dt.DESC])};var pt=function(){function e(){return o(this,e),m(this,p(e).apply(this,arguments))}return d(e,n.Component),e}();s(pt,\"propTypes\",{\"aria-label\":je.string,cellDataGetter:je.func,cellRenderer:je.func,className:je.string,columnData:je.object,dataKey:je.any.isRequired,defaultSortDirection:je.oneOf([dt.ASC,dt.DESC]),disableSort:je.bool,flexGrow:je.number,flexShrink:je.number,headerClassName:je.string,headerRenderer:je.func.isRequired,headerStyle:je.object,id:je.string,label:je.node,maxWidth:je.number,minWidth:je.number,style:je.object,width:je.number.isRequired}),s(pt,\"defaultProps\",{cellDataGetter:te,cellRenderer:ne,defaultSortDirection:dt.ASC,flexGrow:0,flexShrink:1,headerRenderer:ie,style:{}});var ht=function(){function e(t){var n;return o(this,e),(n=m(this,p(e).call(this,t))).state={scrollbarWidth:0},n._createColumn=n._createColumn.bind(f(n)),n._createRow=n._createRow.bind(f(n)),n._onScroll=n._onScroll.bind(f(n)),n._onSectionRendered=n._onSectionRendered.bind(f(n)),n._setRef=n._setRef.bind(f(n)),n}return d(e,n.PureComponent),a(e,[{key:\"forceUpdateGrid\",value:function(){this.Grid&&this.Grid.forceUpdate()}},{key:\"getOffsetForRow\",value:function(e){var t=e.alignment,n=e.index;return this.Grid?this.Grid.getOffsetForCell({alignment:t,rowIndex:n}).scrollTop:0}},{key:\"invalidateCellSizeAfterRender\",value:function(e){var t=e.columnIndex,n=e.rowIndex;this.Grid&&this.Grid.invalidateCellSizeAfterRender({rowIndex:n,columnIndex:t})}},{key:\"measureAllRows\",value:function(){this.Grid&&this.Grid.measureAllCells()}},{key:\"recomputeGridSize\",value:function(e){var t=0<arguments.length&&void 0!==e?e:{},n=t.columnIndex,r=void 0===n?0:n,o=t.rowIndex,i=void 0===o?0:o;this.Grid&&this.Grid.recomputeGridSize({rowIndex:i,columnIndex:r})}},{key:\"recomputeRowHeights\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.recomputeGridSize({rowIndex:t})}},{key:\"scrollToPosition\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.scrollToPosition({scrollTop:t})}},{key:\"scrollToRow\",value:function(e){var t=0<arguments.length&&void 0!==e?e:0;this.Grid&&this.Grid.scrollToCell({columnIndex:0,rowIndex:t})}},{key:\"getScrollbarWidth\",value:function(){if(this.Grid){var e=r.findDOMNode(this.Grid),t=e.clientWidth||0;return(e.offsetWidth||0)-t}return 0}},{key:\"componentDidMount\",value:function(){this._setScrollbarWidth()}},{key:\"componentDidUpdate\",value:function(){this._setScrollbarWidth()}},{key:\"render\",value:function(){var e=this,t=this.props,r=t.children,o=t.className,i=t.disableHeader,a=t.gridClassName,s=t.gridStyle,c=t.headerHeight,d=t.headerRowRenderer,p=t.height,h=t.id,g=t.noRowsRenderer,f=t.rowClassName,m=t.rowStyle,A=t.scrollToIndex,M=t.style,w=t.width,v=this.state.scrollbarWidth,b=i?p:p-c,y=\"function\"==typeof f?f({index:-1}):f,x=\"function\"==typeof m?m({index:-1}):m;return this._cachedColumnStyles=[],n.Children.toArray(r).forEach(function(t,n){var r=e._getFlexStyleForColumn(t,t.props.style);e._cachedColumnStyles[n]=u({overflow:\"hidden\"},r)}),n.createElement(\"div\",{\"aria-label\":this.props[\"aria-label\"],\"aria-labelledby\":this.props[\"aria-labelledby\"],\"aria-colcount\":n.Children.toArray(r).length,\"aria-rowcount\":this.props.rowCount,className:L(\"ReactVirtualized__Table\",o),id:h,role:\"grid\",style:M},!i&&d({className:L(\"ReactVirtualized__Table__headerRow\",y),columns:this._getHeaderColumns(),style:u({height:c,overflow:\"hidden\",paddingRight:v,width:w},x)}),n.createElement(et,l({},this.props,{\"aria-readonly\":null,autoContainerWidth:!0,className:L(\"ReactVirtualized__Table__Grid\",a),cellRenderer:this._createRow,columnWidth:w,columnCount:1,height:b,id:void 0,noContentRenderer:g,onScroll:this._onScroll,onSectionRendered:this._onSectionRendered,ref:this._setRef,role:\"rowgroup\",scrollbarWidth:v,scrollToRow:A,style:u({},s,{overflowX:\"hidden\"})})))}},{key:\"_createColumn\",value:function(e){var t=e.column,r=e.columnIndex,o=e.isScrolling,i=e.parent,a=e.rowData,s=e.rowIndex,l=this.props.onColumnClick,c=t.props,u=c.cellDataGetter,d=c.cellRenderer,p=c.className,h=c.columnData,g=c.dataKey,f=c.id,m=d({cellData:u({columnData:h,dataKey:g,rowData:a}),columnData:h,columnIndex:r,dataKey:g,isScrolling:o,parent:i,rowData:a,rowIndex:s}),A=this._cachedColumnStyles[r],M=\"string\"==typeof m?m:null;return n.createElement(\"div\",{\"aria-colindex\":r+1,\"aria-describedby\":f,className:L(\"ReactVirtualized__Table__rowColumn\",p),key:\"Row\"+s+\"-Col\"+r,onClick:function(e){l&&l({columnData:h,dataKey:g,event:e})},role:\"gridcell\",style:A,title:M},m)}},{key:\"_createHeader\",value:function(e){var t,r,o,i,a,s=e.column,l=e.index,c=this.props,d=c.headerClassName,p=c.headerStyle,h=c.onHeaderClick,g=c.sort,f=c.sortBy,m=c.sortDirection,A=s.props,M=A.columnData,w=A.dataKey,v=A.defaultSortDirection,b=A.disableSort,y=A.headerRenderer,x=A.id,T=A.label,C=!b&&g,N=L(\"ReactVirtualized__Table__headerColumn\",d,s.props.headerClassName,{ReactVirtualized__Table__sortableHeaderColumn:C}),I=this._getFlexStyleForColumn(s,u({},p,{},s.props.headerStyle)),E=y({columnData:M,dataKey:w,disableSort:b,label:T,sortBy:f,sortDirection:m});if(C||h){var D=f!==w?v:m===dt.DESC?dt.ASC:dt.DESC,S=function(e){C&&g({defaultSortDirection:v,event:e,sortBy:w,sortDirection:D}),h&&h({columnData:M,dataKey:w,event:e})};a=s.props[\"aria-label\"]||T||w,i=\"none\",o=0,t=S,r=function(e){\"Enter\"!==e.key&&\" \"!==e.key||S(e)}}return f===w&&(i=m===dt.ASC?\"ascending\":\"descending\"),n.createElement(\"div\",{\"aria-label\":a,\"aria-sort\":i,className:N,id:x,key:\"Header-Col\"+l,onClick:t,onKeyDown:r,role:\"columnheader\",style:I,tabIndex:o},E)}},{key:\"_createRow\",value:function(e){var t=this,r=e.rowIndex,o=e.isScrolling,i=e.key,a=e.parent,s=e.style,l=this.props,c=l.children,d=l.onRowClick,p=l.onRowDoubleClick,h=l.onRowRightClick,g=l.onRowMouseOver,f=l.onRowMouseOut,m=l.rowClassName,A=l.rowGetter,M=l.rowRenderer,w=l.rowStyle,v=this.state.scrollbarWidth,b=\"function\"==typeof m?m({index:r}):m,y=\"function\"==typeof w?w({index:r}):w,x=A({index:r}),T=n.Children.toArray(c).map(function(e,n){return t._createColumn({column:e,columnIndex:n,isScrolling:o,parent:a,rowData:x,rowIndex:r,scrollbarWidth:v})}),C=L(\"ReactVirtualized__Table__row\",b),N=u({},s,{height:this._getRowHeight(r),overflow:\"hidden\",paddingRight:v},y);return M({className:C,columns:T,index:r,isScrolling:o,key:i,onRowClick:d,onRowDoubleClick:p,onRowRightClick:h,onRowMouseOver:g,onRowMouseOut:f,rowData:x,style:N})}},{key:\"_getFlexStyleForColumn\",value:function(e,t){var n=1<arguments.length&&void 0!==t?t:{},r=\"\".concat(e.props.flexGrow,\" \").concat(e.props.flexShrink,\" \").concat(e.props.width,\"px\"),o=u({},n,{flex:r,msFlex:r,WebkitFlex:r});return e.props.maxWidth&&(o.maxWidth=e.props.maxWidth),e.props.minWidth&&(o.minWidth=e.props.minWidth),o}},{key:\"_getHeaderColumns\",value:function(){var e=this,t=this.props,r=t.children;return(t.disableHeader?[]:n.Children.toArray(r)).map(function(t,n){return e._createHeader({column:t,index:n})})}},{key:\"_getRowHeight\",value:function(e){var t=this.props.rowHeight;return\"function\"==typeof t?t({index:e}):t}},{key:\"_onScroll\",value:function(e){var t=e.clientHeight,n=e.scrollHeight,r=e.scrollTop;this.props.onScroll({clientHeight:t,scrollHeight:n,scrollTop:r})}},{key:\"_onSectionRendered\",value:function(e){var t=e.rowOverscanStartIndex,n=e.rowOverscanStopIndex,r=e.rowStartIndex,o=e.rowStopIndex;this.props.onRowsRendered({overscanStartIndex:t,overscanStopIndex:n,startIndex:r,stopIndex:o})}},{key:\"_setRef\",value:function(e){this.Grid=e}},{key:\"_setScrollbarWidth\",value:function(){var e=this.getScrollbarWidth();this.setState({scrollbarWidth:e})}}]),e}();s(ht,\"propTypes\",{\"aria-label\":je.string,\"aria-labelledby\":je.string,autoHeight:je.bool,children:function(e){for(var t=n.Children.toArray(e.children),r=0;r<t.length;r++){var o=t[r].type;if(o!==pt&&!(o.prototype instanceof pt))return new Error(\"Table only accepts children of type Column\")}},className:je.string,disableHeader:je.bool,estimatedRowSize:je.number.isRequired,gridClassName:je.string,gridStyle:je.object,headerClassName:je.string,headerHeight:je.number.isRequired,headerRowRenderer:je.func,headerStyle:je.object,height:je.number.isRequired,id:je.string,noRowsRenderer:je.func,onColumnClick:je.func,onHeaderClick:je.func,onRowClick:je.func,onRowDoubleClick:je.func,onRowMouseOut:je.func,onRowMouseOver:je.func,onRowRightClick:je.func,onRowsRendered:je.func,onScroll:je.func.isRequired,overscanIndicesGetter:je.func.isRequired,overscanRowCount:je.number.isRequired,rowClassName:je.oneOfType([je.string,je.func]),rowGetter:je.func.isRequired,rowHeight:je.oneOfType([je.number,je.func]).isRequired,rowCount:je.number.isRequired,rowRenderer:je.func,rowStyle:je.oneOfType([je.object,je.func]).isRequired,scrollToAlignment:je.oneOf([\"auto\",\"end\",\"start\",\"center\"]).isRequired,scrollToIndex:je.number.isRequired,scrollTop:je.number,sort:je.func,sortBy:je.string,sortDirection:je.oneOf([dt.ASC,dt.DESC]),style:je.object,tabIndex:je.number,width:je.number.isRequired}),s(ht,\"defaultProps\",{disableHeader:!1,estimatedRowSize:30,headerHeight:0,headerStyle:{},noRowsRenderer:function(){return null},onRowsRendered:function(){return null},onScroll:function(){return null},overscanIndicesGetter:H,overscanRowCount:10,rowRenderer:ae,headerRowRenderer:re,rowStyle:{},scrollToAlignment:\"auto\",scrollToIndex:-1,style:{}});var gt=[],ft=null,mt=null,At=function(e){return e===window},Mt=function(e){return e.getBoundingClientRect()},wt=function(){return\"undefined\"!=typeof window?window:void 0},vt=function(){function e(){var t,n;o(this,e);for(var r=arguments.length,i=new Array(r),a=0;r>a;a++)i[a]=arguments[a];return s(f(n=m(this,(t=p(e)).call.apply(t,[this].concat(i)))),\"_window\",wt()),s(f(n),\"_isMounted\",!1),s(f(n),\"_positionFromTop\",0),s(f(n),\"_positionFromLeft\",0),s(f(n),\"_detectElementResize\",void 0),s(f(n),\"_child\",void 0),s(f(n),\"state\",u({},pe(n.props.scrollElement,n.props),{isScrolling:!1,scrollLeft:0,scrollTop:0})),s(f(n),\"_registerChild\",function(e){!e||e instanceof Element||console.warn(\"WindowScroller registerChild expects to be passed Element or null\"),n._child=e,n.updatePosition()}),s(f(n),\"_onChildScroll\",function(e){var t=e.scrollTop;if(n.state.scrollTop!==t){var r=n.props.scrollElement;r&&(\"function\"==typeof r.scrollTo?r.scrollTo(0,t+n._positionFromTop):r.scrollTop=t+n._positionFromTop)}}),s(f(n),\"_registerResizeListener\",function(e){e===window?window.addEventListener(\"resize\",n._onResize,!1):n._detectElementResize.addResizeListener(e,n._onResize)}),s(f(n),\"_unregisterResizeListener\",function(e){e===window?window.removeEventListener(\"resize\",n._onResize,!1):e&&n._detectElementResize.removeResizeListener(e,n._onResize)}),s(f(n),\"_onResize\",function(){n.updatePosition()}),s(f(n),\"__handleWindowScrollEvent\",function(){if(n._isMounted){var e=n.props.onScroll,t=n.props.scrollElement;if(t){var r=he(t),o=Math.max(0,r.left-n._positionFromLeft),i=Math.max(0,r.top-n._positionFromTop);n.setState({isScrolling:!0,scrollLeft:o,scrollTop:i}),e({scrollLeft:o,scrollTop:i})}}}),s(f(n),\"__resetIsScrolling\",function(){n.setState({isScrolling:!1})}),n}return d(e,n.PureComponent),a(e,[{key:\"updatePosition\",value:function(e){var t=0<arguments.length&&void 0!==e?e:this.props.scrollElement,n=this.props.onResize,o=this.state,i=o.height,a=o.width,s=this._child||r.findDOMNode(this);if(s instanceof Element&&t){var l=function(e,t){if(At(t)&&document.documentElement){var n=document.documentElement,r=Mt(e),o=Mt(n);return{top:r.top-o.top,left:r.left-o.left}}var i=he(t),a=Mt(e),s=Mt(t);return{top:a.top+i.top-s.top,left:a.left+i.left-s.left}}(s,t);this._positionFromTop=l.top,this._positionFromLeft=l.left}var c=pe(t,this.props);i===c.height&&a===c.width||(this.setState({height:c.height,width:c.width}),n({height:c.height,width:c.width}))}},{key:\"componentDidMount\",value:function(){var e=this.props.scrollElement;this._detectElementResize=x(),this.updatePosition(e),e&&(ue(this,e),this._registerResizeListener(e)),this._isMounted=!0}},{key:\"componentDidUpdate\",value:function(e){var t=this.props.scrollElement,n=e.scrollElement;n!==t&&null!=n&&null!=t&&(this.updatePosition(t),de(this,n),ue(this,t),this._unregisterResizeListener(n),this._registerResizeListener(t))}},{key:\"componentWillUnmount\",value:function(){var e=this.props.scrollElement;e&&(de(this,e),this._unregisterResizeListener(e)),this._isMounted=!1}},{key:\"render\",value:function(){var e=this.props.children,t=this.state,n=t.isScrolling,r=t.scrollTop,o=t.scrollLeft,i=t.height,a=t.width;return e({onChildScroll:this._onChildScroll,registerChild:this._registerChild,height:i,isScrolling:n,scrollLeft:o,scrollTop:r,width:a})}}]),e}();s(vt,\"defaultProps\",{onResize:function(){},onScroll:function(){},scrollingResetTimeInterval:150,scrollElement:wt(),serverHeight:0,serverWidth:0}),t.ArrowKeyStepper=ge,t.AutoSizer=fe,t.CellMeasurer=me,t.CellMeasurerCache=Ae,t.Collection=He,t.Column=pt,t.ColumnSizer=Fe,t.Grid=et,t.InfiniteLoader=tt,t.List=nt,t.Masonry=st,t.MultiGrid=ct,t.ScrollSync=ut,t.SortDirection=dt,t.SortIndicator=oe,t.Table=ht,t.WindowScroller=vt,t.accessibilityOverscanIndicesGetter=H,t.createMasonryCellPositioner=function(e){function t(e){for(var t=0,n=1;n<r.length;n++)r[n]<r[t]&&(t=n);var i=t*(a+l),s=r[t]||0;return r[t]=s+o.getHeight(e)+l,{left:i,top:s}}function n(){r=[];for(var e=0;i>e;e++)r[e]=0}var r,o=e.cellMeasurerCache,i=e.columnCount,a=e.columnWidth,s=e.spacer,l=void 0===s?0:s;return n(),t.reset=function(e){i=e.columnCount,a=e.columnWidth,l=e.spacer,n()},t},t.createTableMultiSort=function(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=t.defaultSortBy,r=t.defaultSortDirection,o=void 0===r?{}:r;if(!e)throw Error('Required parameter \"sortCallback\" not specified');var i=n||[],a={};return i.forEach(function(e){a[e]=void 0!==o[e]?o[e]:\"ASC\"}),{sort:function(t){var n=t.defaultSortDirection,r=t.event,o=t.sortBy;if(r.shiftKey)void 0!==a[o]?a[o]=\"ASC\"===a[o]?\"DESC\":\"ASC\":(a[o]=n,i.push(o));else if(r.ctrlKey||r.metaKey){var s=i.indexOf(o);s>=0&&(i.splice(s,1),delete a[o])}else i.length=0,i.push(o),Object.keys(a).forEach(function(e){e!==o&&delete a[e]}),void 0!==a[o]?a[o]=\"ASC\"===a[o]?\"DESC\":\"ASC\":a[o]=n;e({sortBy:i,sortDirection:a})},sortBy:i,sortDirection:a}},t.defaultCellRangeRenderer=Q,t.defaultOverscanIndicesGetter=R,t.defaultTableCellDataGetter=te,t.defaultTableCellRenderer=ne,t.defaultTableHeaderRenderer=ie,t.defaultTableHeaderRowRenderer=re,t.defaultTableRowRenderer=ae,Object.defineProperty(t,\"__esModule\",{value:!0})})}).call(t,function(){return this}())},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(522),a=n(213),s=n(214),l=r.createClass({displayName:\"QRCodeDialog\",getInitialState:function(){return{}},shouldComponentUpdate:function(){return this.refs.qrcodeDialog.isVisible()},show:function(e){this.refs.qrcodeDialog.show(),this.setState({url:e})},render:function(){var e=this.state.url;return r.createElement(o,{ref:\"qrcodeDialog\",wstyle:\"w-qrcode-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(\"h4\",null,\"QR Code\"),r.createElement(s,null),r.createElement(\"div\",{className:\"w-qrcode-url-wrap\"},r.createElement(\"input\",{readOnly:!0,value:e}),r.createElement(a,{name:\"copy\",className:\"w-copy-text-with-tips\",\"data-clipboard-text\":e})),r.createElement(i,{url:this.state.url})),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))}});e.exports=l},function(e,t,n){\"use strict\";function r(e){return e>0?e:320}function o(e,t){a.toDataURL(e.url,{width:r(e.width),height:r(e.height),margin:0},function(e,n){t(e,n)})}var i=n(24),a=n(301),s=i.createClass({displayName:\"QRCodeImg\",getInitialState:function(){return{}},renderQRCode:function(){var e=this,t=e.state,n=e.props;return n.url?void(n.url!=t.url&&o(n,function(r,o){t.url=n.url,t.error=r,t.dataURL=o,e.setState({})})):(t.dataURL=null,void(t.error=null))},renderError:function(){var e=this.state.error;return e?i.createElement(\"div\",{className:\"w-qrcode-error\"},e.message||\"Error occurred\"):this.props.url?\"Generating...\":null},render:function(){this.renderQRCode();var e=this.state,t=this.props,n=e.dataURL,o={width:r(t.width),height:r(t.width)};return i.createElement(\"div\",{className:\"w-qrcode-wrap\",style:o},n?i.createElement(\"img\",{src:n}):this.renderError())}});e.exports=s},function(e,t,n){\"use strict\";function r(){return[{title:\"APP\",name:\"app\",className:\"app\",selected:!0,width:50,getIcon:function(e){var t,n=e.appName||\"browser\";return t=/\\//.test(n)?n:\"whistle\"===n?\"img/whistle.png\":\"img/app/\"+(\"ipad\"===n?\"iphone\":n)+\".png\",A.createElement(\"img\",{src:t,title:n,className:\"w-cell-img\"})}},{title:\"Date\",name:\"date\",className:\"date\",showTitle:!0,width:160},{title:\"Result\",name:\"result\",className:\"result\",selected:!0,width:75},{title:\"Method\",name:\"method\",className:\"method\",showTitle:!0,selected:!0,width:75},{title:\"Protocol\",name:\"protocol\",className:\"protocol\",selected:!0,showTitle:!0,width:95},{title:\"ClientIP\",name:\"clientIp\",className:\"clientIp\",showTitle:!0,width:110},{title:\"ServerIP\",name:\"hostIp\",className:\"hostIp\",selected:!0,showTitle:!0,width:110},{title:\"ClientPort\",name:\"clientPort\",className:\"clientPort\",width:90},{title:\"ServerPort\",name:\"serverPort\",className:\"serverPort\",width:90},{title:\"Host\",name:\"hostname\",className:\"hostname\",selected:!0,showTitle:!0,width:150},{title:\"URL\",name:\"path\",className:\"path\",selected:!0,showTitle:!0,locked:!0,minWidth:60},{title:\"Type\",name:\"type\",className:\"type\",selected:!0,showTitle:!0,width:125},{title:\"Body\",showTitle:!0,name:\"body\",className:\"body\",width:90},{title:\"Encoding\",name:\"contentEncoding\",className:\"contentEncoding\",width:90},{title:\"DNS\",name:\"dns\",className:\"dns\",width:70},{title:\"Request\",name:\"request\",className:\"request\",width:90},{title:\"Response\",name:\"response\",className:\"response\",width:90},{title:\"Download\",name:\"download\",className:\"download\",width:90},{title:\"Time\",name:\"time\",className:\"time\",selected:!0,width:70},{title:\"Custom1\",name:\"custom1\",className:\"custom1\",showTitle:!0,width:120},{title:\"Custom2\",name:\"custom2\",className:\"custom2\",showTitle:!0,width:160}]}function o(e,t){g=g||r(),h=g.concat(T),h.forEach(function(e){for(var t,n=[],r=f[e.name],o=e.minWidth||e.width,i=e.minWidth?\">= \":\"\",a=o%2?5:0,s=0;11>s;s++){var l=o+60*s+a,c=r===l;t=t||c,s?n.push({name:i+l+\"px\",action:l,selected:c}):n.push({name:i+o+\"px (Default)\",action:l,selected:c})}p[e.name]=e,e.menus=n,t||(n[0].selected=!0,f[e.name]=n[0].action)}),e&&(x=h),a(t)}function i(e){if(p={},e){try{f=JSON.parse(v.get(\"networkColumnsWidth\"))}catch(t){}f=f||{}}else g=null,f={};o(!e,e),e||(s(),v.set(\"networkColumnsWidth\"))}function a(e){var t=[],n=[];x.forEach(function(n){var r=n&&n.name,o=r&&p[r];e&&o&&(o.selected=!!n.selected),o&&-1!==h.indexOf(o)&&-1===t.indexOf(o)&&t.push(o)}),h.forEach(function(e){-1===t.indexOf(e)&&(-1===T.indexOf(e)?n.push(e):t.push(e))}),h=n.concat(t),y={columns:h}}function s(){y.columns=h,M.setNetworkColumns(y)}function l(e,t){if(e!==t){var n=p[e],r=p[t];if(n&&r){var o=h.indexOf(n),i=h.indexOf(r);h.splice(o,1),h.splice(i,0,n),s()}}}function c(e){var t=e.target,n=t.nodeName;return\"TH\"===n||\"LABEL\"===n?t:(t=t.parentNode,t&&(n=t.nodeName,\"TH\"===n||\"LABEL\"===n)?t:void 0)}function u(e){var t=c(e),n=t&&t.getAttribute(\"data-name\");if(n){var r=d(e);return r&&n.toLowerCase()!==r?{target:t,toName:n}:void 0}}function d(e){var t=b.findArray(e.dataTransfer.types,function(e){return 0===e.indexOf(I)?!0:void 0});return t&&t.substring(I.length)}var p,h,g,f,m=n(18),A=n(24),M=n(200),w=n(199),v=n(210),b=n(202),y=M.getNetworkColumns(),x=Array.isArray(y.columns)?y.columns:[],T=M.getPluginColumns(),C={};i(!0),t.getColumn=function(e){return p[e]},t.getAllColumns=function(){return h},t.reset=i,t.setSelected=function(e,t){var n=p[e];n&&(n.selected=t!==!1,s())},t.getSelectedColumns=function(){var e=50,t=h.filter(function(t){return t.selected||t.locked||t.isPlugin?(e+=f[t.name]||t.width||t.minWidth,!0):void 0});return{width:e,style:{minWidth:e},list:t}};var N,I=\"networkcolumn$\";m(document).on(\"drop\",function(){N&&(N.style.background=\"\"),N=null}),t.getDragger=function(){return{onDragStart:function(e){var t=c(e),n=t&&t.getAttribute(\"data-name\");e.dataTransfer.setData(I+n,1),e.dataTransfer.setData(\"-\"+I,n)},onDragEnter:function(e){var t=u(e);t&&(N=t.target,N.style.background=\"var(--b-active)\")},onDragLeave:function(e){var t=u(e);t&&(t.target.style.background=\"\")},onDrop:function(e){var t=u(e);if(t){var n=e.dataTransfer.getData(\"-\"+I);l(n,t.toName),t.target.style.background=\"\",\"function\"==typeof this.onColumnsResort&&this.onColumnsResort()}}}},t.setWidth=function(e,t){f[e]=parseInt(t),v.set(\"networkColumnsWidth\",JSON.stringify(f))},t.getWidth=function(e){return f[e.name]||e.width||e.minWidth},w.on(\"pluginColumnsChange\",function(){var e={};T=M.getPluginColumns().map(function(t){e[t.name]=t;var n=C[t.name];return n?(n.title=t.title,n.key=t.key,n.iconKey=t.iconKey,n.width=t.width,n):t}),C=e,o(),w.trigger(\"onColumnsChanged\")})},function(e,t,n){\"use strict\";n(525);var r=n(18),o=n(24),i=n(57),a=n(199),s=n(527),l=n(530),c=n(533),u=n(579),d=n(582),p=n(592),h=n(599),g=n(202),f=n(213),m=i.findDOMNode,A=o.createClass({displayName:\"ReqData\",getInitialState:function(){return{tabs:[{name:\"Overview\",icon:\"eye-open\"},{name:\"Inspectors\",icon:\"search\"},{name:\"Timeline\",icon:\"time\"},{name:\"Composer\",icon:\"send\"},{name:\"Tools\",icon:\"heart\"},{name:\"Saved\",icon:\"saved\"}],initedOverview:!1,initedInspectors:!1,initedFrames:!1,initedTimeline:!1,initedComposer:!1,initedTools:!1,initedSaved:!1}},componentDidMount:function(){if(!this.props.data){var e,t=this,n=t.state.tabs,r=function(){t.setState({})};a.on(\"showOverview\",function(){a.trigger(\"overviewScrollTop\"),t.toggleTab(n[0])}).on(\"showInspectors\",function(){t.toggleTab(n[1])}).on(\"showTimeline\",function(){t.toggleTab(n[2])}).on(\"showLog\",function(){t.toggleTab(n[4])}).on(\"composer\",function(e,n){var r=t.props.modal;t.showComposer(n||r&&r.getActive()),setTimeout(function(){t.shakeComposerTab()},100)}).on(\"showComposerTab\",function(){t.showComposer()}).on(\"networkStateChange\",function(){clearTimeout(e),e=setTimeout(r,100)}).on(\"toggleDetailTab\",function(){var e=t.state.tab;e===n[0]?t.toggleTab(n[1]):e===n[1]?t.toggleTab(n[2]):t.toggleTab(n[0])}).on(\"shakeSavedTab\",t.shakeSavedTab)}},shakeSavedTab:function(){g.shakeElem(r(m(this.refs.tabs)).find('button[data-name=\"Saved\"]'))},showComposer:function(e){e&&(this.state.activeItem=e),this.toggleTab(this.state.tabs[3],function(){e&&a.trigger(\"setComposer\")})},isShowingSaved:function(){var e=this.state.tab;return e&&\"Saved\"===e.name},onDragEnter:function(e){-1!=e.dataTransfer.types.indexOf(\"reqdataid\")&&(!this.isShowingSaved()&&this.showComposer(),e.preventDefault())},getItemById:function(e,t){for(var n=0,r=t.length;r>n;n++){var o=t[n];if(o&&o.id===e)return o}},onDrop:function(e){var t=this.props.modal,n=e.dataTransfer.getData(\"reqDataId\"),r=t&&t.list,o=r&&r.length;if(n&&o){var i=this.getItemById(n,r);return this.isShowingSaved()?a.trigger(\"saveSessions\",[i]):void(i&&this.showComposer(i))}},onDoubleClick:function(){a.trigger(\"ensureSelectedItemVisible\")},toggleTab:function(e,t){if(\"Inspectors\"===e.name&&this.state.initedInspectors){var n=r(\".w-detail-inspectors\");if(n.length){var o=r(\".w-detail\");o.find(\">.fill\").addClass(\"hide\"),n.removeClass(\"hide\");var i=o.find(\">.w-tabs-sm>button\");i.removeClass(\"active\"),i.eq(1).addClass(\"active\")}}this.selectTab(e),this.setState({tab:e},t)},selectTab:function(e){this.state.tabs.forEach(function(e){e.active=!1}),e.active=!0,this.state.tab=e,this.state[\"inited\"+e.name]=!0},shakeComposerTab:function(){g.shakeElem(r(m(this.refs.tabs)).find('button[data-name=\"Composer\"]'))},render:function(){var e,t,n=this.props.modal,r=this.state,i=this.props.data,a=r.tabs,g=!i&&n&&n.getSelectedList();g&&g.length>1?(t={req:{size:0,unzipSize:0,headers:{}},res:{size:0,unzipSize:0,headers:{}}},g.forEach(function(e){(null==t.startTime||t.startTime>e.startTime)&&(t.startTime=e.startTime),(null==t.endTime||t.endTime<e.endTime)&&(t.endTime=e.endTime);var n=e.req;n.size>0&&(t.req.size+=n.size,t.req.unzipSize+=null==n.unzipSize?n.size:n.unzipSize);var r=e.res;r.size>0&&(t.res.size+=r.size,t.res.unzipSize+=null==r.unzipSize?r.size:r.unzipSize)})):i?t=e=i:(t=e=n&&n.getActive(),(!e||e.hide)&&(t=e=g&&g[0]));var m=r.tab;!m&&t&&(m=a[0],a.forEach(function(e){e.active=!1}),this.selectTab(m));var A=m&&m.name,M=e&&e.frames,w=this.props.dockToBottom;return o.createElement(\"div\",{className:\"fill v-box w-detail\"+(w?\" w-detail-bottom\":\"\"),onDragEnter:this.onDragEnter,onDrop:this.onDrop},o.createElement(s,{ref:\"tabs\",dockBtn:o.createElement(\"button\",{onClick:this.props.onDockChange,className:\"w-dock-btn\",title:\"Dock to \"+(w?\"right\":\"bottom\")+\" (F12)\"},o.createElement(f,{name:\"menu-\"+(w?\"right\":\"down\"),className:i?\"hide\":\"\"})),onDoubleClick:this.onDoubleClick,onClick:this.toggleTab,tabs:a}),r.initedOverview?o.createElement(l,{modal:t,rulesModal:this.props.rulesModal,\nhide:A!=a[0].name}):null,r.initedInspectors?o.createElement(c,{modal:e,frames:M,hide:A!=a[1].name}):null,r.initedTimeline?o.createElement(u,{data:i,modal:n,hide:A!=a[2].name}):null,r.initedComposer?o.createElement(d,{modal:r.activeItem,hide:A!=a[3].name}):null,r.initedTools?o.createElement(p,{hide:A!=a[4].name}):null,r.initedSaved?o.createElement(h,{hide:A!=a[5].name}):null)}});e.exports=A},function(e,t,n){var r=n(526);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-detail{border-left:1px solid var(--c-border);overflow-x:auto;overflow-y:hidden}.w-detail-ctn{position:relative;overflow:auto}.w-detail-divider{position:absolute;left:0;top:0;width:5px;height:100%;cursor:ew-resize}.w-detail-ctn,.w-detail>.w-order-table,.w-detail>.w-tabs-sm{min-width:35pc;border-top:none}.w-show-history .w-detail-ctn{min-width:auto}.w-inspectors-ctx-menu .w-ctx-menu-list{min-width:75pt}.w-detail-inspectors{min-width:35pc}.w-detail-bottom.w-detail{border-left:none}.w-detail-custom-tab{max-width:12.1%;max-width:calc(100% - 492px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.w-req{max-width:calc(100% - 450px)}.w-plugins-tabs-list{width:106px;border-right:1px solid var(--c-border);overflow-x:hidden;overflow-y:auto;background-color:var(--b-title)}.w-plugins-tabs-list .btn{border-radius:0;border:none;border-bottom:1px solid var(--c-border);white-space:normal;word-wrap:break-word;word-break:break-all;font-size:9pt;padding:3px 5px;line-height:18px;display:block;width:100%}.w-custom-tab-panel .w-tab-frame{min-width:35pc}div.w-detail-inspectors-tabs{padding:0!important}.w-detail-inspectors-tabs button{border:none;border-right:1px solid var(--c-border);height:20px;line-height:1pc;font-size:9pt;padding:0 9pt;border-radius:0;font-weight:700}.w-detail .glyphicon-menu-hamburger{color:var(--b-frames)}.w-detail-inspectors-tabs button.w-spec-active{background-color:var(--b-active)}.w-detail-inspectors-title.w-detail-inspectors-res{padding-left:9pt!important}.w-custom-tabs{overflow-y:hidden;overflow-x:auto;white-space:nowrap;height:20px}.w-custom-tabs button{font-weight:400!important}.w-no-frames{position:absolute;width:75pt;height:20px;line-height:20px;left:50%;top:50%;font-size:14px;margin:-10px 0 0 -50px;text-align:center;color:var(--c-gray)}\",\"\"])},function(e,t,n){\"use strict\";n(528);var r=n(24),o=n(202),i=n(213),a=r.createClass({displayName:\"BtnGroup\",handleClick:function(e){e.active||e.disabled||(this.clearSelection(),e.active=!0,(!this.props.onClick||this.props.onClick(e))&&this.setState({curBtn:e}))},clearSelection:function(){var e=this.props.tabs||this.props.btns;e.forEach(function(e){e.active=!1})},onDoubleClick:function(e){this.props.onDoubleClick&&this.props.onDoubleClick(e),e.stopPropagation()},render:function(){var e=this,t=e.props.tabs,n=\"s\"===e.props.type,a=t||e.props.btns,s=o.getBool(e.props.disabled);return r.createElement(\"div\",{onDoubleClick:e.props.onDoubleClickBar,className:\"btn-group btn-group-sm \"+(t?\"w-tabs-sm\":\"w-btn-group-sm\")+(n?\" small\":\"\")},a.map(function(t){t.disabled=s;var n=t.icon?r.createElement(i,{name:t.icon}):\"\",a=t.className?\" \"+t.className:\"\";return t.key=t.key||o.getKey(),r.createElement(\"button\",{onClick:function(){e.handleClick(t)},onDoubleClick:e.onDoubleClick,key:t.key,type:\"button\",\"data-name\":t.name,style:{display:t.hide?\"none\":void 0},title:t.title,className:\"btn btn-default\"+(t.active&&!s?\" active\":\"\")+a},n,t.display||t.name)}),e.props.appendTabs||e.props.appendBtns,e.props.dockBtn)}});e.exports=a},function(e,t,n){var r=n(529);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-tabs-sm{border-top:1px solid var(--c-border);border-bottom:1px solid var(--c-border);width:100%}.w-tabs-sm .btn{border-radius:0;border-bottom:none;border-top:none;padding:4px 10px 3px;overflow:hidden;font-size:14px}.w-tabs-sm .btn:first-child{border-left:none}.w-btn-group-sm{border-bottom:1px solid var(--c-border);width:100%}.w-btn-group-sm .btn{padding:2px 10px 1px!important;border-radius:0;border-top:none;border-bottom:none}.w-btn-group-sm .btn:first-child{border-left:none}.w-btn-group-sm.small .btn{padding:0 8px!important;font-size:9pt;line-height:1pc}.w-detail .w-addon-btn{max-width:85px;max-width:calc(100% - 475px);overflow:hidden;text-overflow:ellipsis;display:none}.w-show-addon .w-addon-btn{display:inline-block!important}.w-show-addon .w-tabs-sm .btn{padding:4px 9px 3px}\",\"\"])},function(e,t,n){\"use strict\";function r(e){return e.rawPattern+\" @\"+u(e).substring(4)+s(e)}function o(e){return e.rawPattern+\" %\"+u(e).substring(4)+s(e)}function i(e){return e?\" \"+e:\"\"}function a(e){return-1!==e.indexOf(\"important\")}function s(e){return e&&e.file?M.SOURCE_SEP+e.file+\")\":\"\"}function l(e,t){var n=i(e.filter);return(e=e.rawProps)?(t||(e=e.filter(a)),i(e.join(\" \"))+n):n}function c(e){return e.strictHtml?\" enable://strictHtml\":e.safeHtml?\" enable://safeHtml\":\"\"}function u(e){return e._matcher||e.matcher}function d(e){if(e){var t=u(e);if(e.port){var n=t.indexOf(\":\")+3,r=t.substring(0,n);-1!==t.indexOf(\":\",n)&&(t=r+\"[\"+t.substring(n)+\"]\"),t=t+\":\"+e.port}return e.rawPattern+\" \"+t+l(e,!0)}}function p(e){return\"-\"===e?\"\":e}function h(e){return-1!==S.indexOf(e)||\"skip\"===e||/^x/.test(e)}n(531);var g=n(24),f=n(57),m=n(523),A=n(199),M=n(202),w=n(210),v=n(337),b=n(200),y=n(288).getHelpUrl,x=n(511),T=[\"URL\",\"Final URL\",\"Method\",\"Http Version\",\"Status Code\",\"Status Message\",\"Client IP\",\"Client Port\",\"Client ID\",\"Server IP\",\"Server Port\",\"Request Body\",\"Response Body\",\"Content Encoding\",\"Start Date\",\"TTFB\",\"DNS\",\"Request\",\"Response\",\"Download\",\"Total Duration\"],C=[\"url\",\"realUrl\",\"req.method\",\"req.httpVersion\",\"res.statusCode\",\"res.statusMessage\",\"req.ip\",\"req.port\",\"clientId\",\"res.ip\",\"res.port\",\"req.size\",\"res.size\",\"contentEncoding\"],N={TTFB:{className:\"w-overview-timeline\",style:{\"--overview-bg\":\"var(--b-active)\"}},DNS:{className:\"w-overview-timeline\",style:{\"--overview-bg\":\"var(--b-tl-dns)\"}},Request:{className:\"w-overview-timeline\",style:{\"--overview-bg\":\"var(--b-tl-req)\"}},Response:{className:\"w-overview-timeline\",style:{\"--overview-bg\":\"var(--b-tl-res)\"}},Download:{className:\"w-overview-timeline\",style:{\"--overview-bg\":\"var(--b-tl-load)\"}}},I=n(288).PROTOCOLS,E={},D={},S=[\"socks\",\"http-proxy\",\"https-proxy\"];T.forEach(function(e){E[e]=\"\"}),I.forEach(function(e){h(e)||(D[e]=\"\")});var L=g.createClass({displayName:\"Overview\",getInitialState:function(){return{showOnlyMatchRules:1==w.get(\"showOnlyMatchRules\")}},shouldComponentUpdate:M.shouldComponentUpdate,componentDidMount:function(){var e=this,t=f.findDOMNode(e.refs.container);A.on(\"overviewScrollTop\",function(){M.getBool(e.props.hide)||(t.scrollTop=0)})},showOnlyMatchRules:function k(e){var k=e.target.checked;w.set(\"showOnlyMatchRules\",k?1:0),this.setState({showOnlyMatchRules:k})},onHelp:function(e){var t=e.target.getAttribute(\"data-name\"),n=y(t);n&&window.open(\"rule\"===t?n+\"rule/\":n)},updateCssMap:function(){Object.keys(N).forEach(function(e){N[e].style[\"--overview-width\"]=0});var e=this.props.modal;if(e&&e.url){var t=e.endTime-e.startTime;if(t>0){N.TTFB.style[\"--overview-width\"]=100*e.ttfb/t+\"%\";var n=100*(e.dnsTime-e.startTime)/t+\"%\";N.DNS.style[\"--overview-width\"]=n;var r=N.Request.style;r[\"--overview-left\"]=n,r[\"--overview-width\"]=100*(e.requestTime-e.dnsTime)/t+\"%\",r=N.Response.style,r[\"--overview-left\"]=100*(e.requestTime-e.startTime)/t+\"%\",r[\"--overview-width\"]=100*(e.responseTime-e.requestTime)/t+\"%\",r=N.Download.style,r[\"--overview-left\"]=100*(e.responseTime-e.startTime)/t+\"%\",r[\"--overview-width\"]=100*(e.endTime-e.responseTime)/t+\"%\"}}},render:function(){var e,t,n=E,i=D,a=this.props.modal,f=this.state.showOnlyMatchRules;if(a){n={};var A=M.getRawUrl(a);T.forEach(function(t,r){var o=C[r];if(o){var i=M.getProperty(a,o);i&&\"res.ip\"===o?i=M.getServerIp(a):i||\"clientId\"!==o||(i=M.getProperty(a,\"req.headers.x-whistle-client-id\"));var s=\"realUrl\"==o;null!=i?\"req.size\"==o||\"res.size\"==o?i=M.formatSize(i,i?M.getProperty(a,o.substring(0,4)+\"unzipSize\"):-1):s?(i==a.url?i=\"\":a.isHttps&&(i=\"tunnel://\"+i),e=i):a.isHttps&&\"url\"===o&&(i=\"tunnel://\"+i):\"res.statusMessage\"==o&&(i=M.getStatusMessage(a.res));var l=s&&M.getProperty(a,\"res.headers.location\");if(n[t]=i,l){var c=M.getProperty(a,\"res.statusCode\");!l||301!=c&&302!=c&&303!=c&&307!=c&&308!=c||(n[\"Redirect URL\"]=l)}}else{var u,d=T.length-1;switch(t){case T[d-6]:u=M.toLocaleString(new Date(a.startTime));break;case T[d-5]:u=a.ttfb>=0?a.ttfb+\"ms\":\"\";break;case T[d-4]:u=p(a.dns);break;case T[d-3]:if(a.requestTime){u=p(a.request);var h=a.protocol;if(\"string\"==typeof h&&-1!==h.indexOf(\">\")){var g=a.httpsTime-a.dnsTime;g>0&&(u+=\" - \"+g+\"ms(\"+h+\") = \"+(a.requestTime-a.httpsTime)+\"ms\")}}break;case T[d-2]:u=p(a.response);break;case T[d-1]:u=p(a.download);break;case T[d]:u=p(a.time),a.endTime&&(u=a.endTime-a.startTime+\"ms\")}n[t]=u}});var w=m.getColumn(\"custom1\"),y=m.getColumn(\"custom2\");a.sniPlugin&&(n[\"SNI Plugin\"]=a.sniPlugin),w.selected&&(n[(b.custom1||\"Custom1\")+\" \"]=a.custom1),y.selected&&(n[(b.custom2||\"Custom2\")+\"  \"]=a.custom2);var S=a.rules,L={};if(S){i={};var k,j,U=S.G,B=S.clientCert;U&&(k=[r(U)],j=[U.raw]);var R=S.P;R&&R.forEach(function(e){k=k||[],k.push(o(e)),j=[e.raw]}),B&&(k=k||[],j=j||[],k.push(r(B)),j.push(B.raw)),k&&(i[\"@\"]=k.join(\"\\n\"),L[\"@\"]=j.join(\"\\n\")),I.forEach(function(n){if(!h(n)){var r=n;\"reqScript\"===n?r=\"rulesFile\":\"reqMerge\"===n?r=\"params\":\"tlsOptions\"===n?r=\"cipher\":\"pathReplace\"===n&&(r=\"urlReplace\");var o=S[r],a=\"plugin\"===n&&S._pluginRule;if(a){t=!0;var p=[a.rawPattern+\" \"+u(a)+l(a)+s(a)],g=[a.raw];o&&Array.isArray(o.list)&&o.list.forEach(function(e){p.push(e.rawPattern+\" \"+u(e)+l(e)+s(e)),g.push(e.raw)}),i[n]=p.join(\"\\n\"),L[n]=g.join(\"\\n\")}else if(o&&Array.isArray(o.list)){var f=c(o);i[n]=o.list.map(function(e){return e.rawPattern+\" \"+u(e)+l(e,!0)+f+s(e)}).join(\"\\n\"),L[n]=o.list.map(function(e){return e.raw}).join(\"\\n\")}else{var m=d(o);if(i[n]=m,L[n]=o?o.raw:void 0,\"host\"===n){var A=[];m&&A.push(m+(e?\" (URL: \"+e+\")\":\"\")+s(o)),S.proxy&&S.proxy.host&&A.push(d(S.proxy.host)+\" (URL: \"+u(S.proxy)+\")\"+s(o)),i[n]=A.join(\"\\n\")}else\"proxy\"===n&&e&&m&&(i[n]+=\" (URL: \"+e+\")\"),i[n]&&(i[n]+=s(o))}}})}}return this.updateCssMap(),g.createElement(\"div\",{ref:\"container\",className:\"fill v-box w-detail-ctn w-detail-overview\"+(M.getBool(this.props.hide)?\" hide\":\"\")},g.createElement(v,{modal:n,rawName:\"Original URL\",rawValue:A,showEnableBtn:a&&!a.importedData,cssMap:N}),g.createElement(\"p\",{className:\"w-detail-overview-title\",style:{background:f?\"var(--b-filtered)\":void 0}},g.createElement(x,{docsUrl:\"rules/protocols.html\"}),\"All Rules:\",g.createElement(\"label\",null,g.createElement(\"input\",{checked:f,onChange:this.showOnlyMatchRules,type:\"checkbox\"}),\"Only show matching rules\")),g.createElement(v,{onHelp:this.onHelp,className:f?\"w-hide-no-value w-rules-overview\":\"w-rules-overview\",onClickLocate:M.handleClickLocate,modal:i,title:L,enableCopyValue:!0,name:\"Rules\",hasPluginRule:t}))}});e.exports=L},function(e,t,n){var r=n(532);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-detail-overview>.w-props-wrap:last-child{border-top:1px solid var(--c-border)}.w-detail-overview-title{margin:0;padding:6px 5px 0}.w-detail-overview-title:after{content:'';display:block;clear:both}.w-detail-overview-title label{float:right;font-weight:400;display:flex;align-items:center}.w-detail-overview-title label input{margin-right:5px;vertical-align:text-bottom}.w-overview-timeline{position:relative}.w-overview-timeline>pre{z-index:1;position:relative}.w-overview-timeline:before{position:absolute;content:'';display:block;background:var(--overview-bg,none);width:var(--overview-width,0);left:var(--overview-left,0);top:0;height:100%}.w-props-wrap td pre[data-rule-source]:hover{cursor:pointer;color:var(--c-link)}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=n(534),a=n(561),s=n(578),l=n(200),c=n(199),u=n(547),d=n(293),p=n(337),h=n(213),g=r.createClass({displayName:\"Inspectors\",getInitialState:function(){return{activeName:\"Request\",urlModal:{URL:\"\"}}},shouldComponentUpdate:o.shouldComponentUpdate,componentDidMount:function(){var e=this;c.on(\"tabsChange\",function(){e.setState({})}),c.on(\"showFrames\",function(){e.showTab(\"Frames\")})},showTab:function(e){this.state.activeName!==e&&this.setState({activeName:e})},isActive:function(e){return this.state.activeName===e},getStyle:function(e){return\"btn btn-default\"+(this.isActive(e)?\" w-spec-active\":\"\")},render:function(){var e=this,t=e.props,n=t.modal,c=e.state.urlModal,g=!e.isActive(\"Frames\"),f=o.getBool(t.hide),m=l.getTabs(),A=this.state.activeName;return c.URL=n&&(n.isHttps?\"tunnel://\":\"\")+n.url,r.createElement(\"div\",{className:\"fill v-box w-detail-inspectors\"+(f?\" hide\":\"\")},r.createElement(p,{className:\"w-detail-inspectors-url\",modal:c,showEnableBtn:!0}),r.createElement(\"div\",{className:\"box w-detail-inspectors-title w-detail-inspectors-tabs\"},r.createElement(\"button\",{type:\"button\",onClick:function(){e.showTab(\"Request\")},className:e.getStyle(\"Request\")},r.createElement(h,{name:\"arrow-right\"}),\"Request\"),r.createElement(\"button\",{type:\"button\",onClick:function(){e.showTab(\"Frames\")},className:e.getStyle(\"Frames\")},r.createElement(h,{name:\"menu-hamburger\"}),\"Frames\"),r.createElement(\"div\",{className:\"fill w-custom-tabs\"},m.map(function(t){var n=t.plugin,i=o.getTabIcon(t);return r.createElement(\"button\",{key:n,onClick:function(){e.showTab(n)},className:\"w-custom-tab-btn \"+e.getStyle(n),title:n},i?r.createElement(\"img\",{className:\"w-tab-icon\",src:i}):null,t.name)}))),r.createElement(i,{hide:!e.isActive(\"Request\"),modal:n}),r.createElement(s,{inited:!g},r.createElement(a,{hide:g,data:n,frames:t.frames})),r.createElement(u,{active:A,hide:f,tabs:m,className:\"w-custom-tab-panel\"}),r.createElement(d,{ref:\"contextMenu\"}))}});e.exports=g},function(e,t,n){\"use strict\";var r=n(24),o=n(220),i=n(535),a=n(552),s=n(202),l=n(213),c=r.createClass({displayName:\"Inspector\",shouldComponentUpdate:function(e){var t=s.getBool(this.props.hide);if(t!=s.getBool(e.hide))return!0;if(t)return!1;var n=this.props.modal,r=e.modal;return n&&n===r?!this.endTime||this.refs.divider._needReset:!0},render:function(){var e=this.props,t=e.modal;return this.endTime=t&&(t.endTime||t.lost),r.createElement(o,{ref:\"divider\",vertical:\"true\",hide:e.hide},r.createElement(\"div\",{className:\"fill v-box\"},r.createElement(i,{modal:t})),r.createElement(\"div\",{className:\"fill v-box\"},r.createElement(\"div\",{className:\"w-detail-inspectors-title w-detail-inspectors-res\"},r.createElement(l,{name:\"arrow-left\"}),\"Response\"),r.createElement(a,{modal:t})))}});e.exports=c},function(e,t,n){\"use strict\";n(536);var r=n(24),o=n(220),i=n(337),a=n(202),s=n(527),l=n(538),c=n(543),u=n(200),d=n(546),p=n(199),h=[{name:\"Raw\"},{name:\"Headers\"},{name:\"WebForms\"},{name:\"TextView\",display:\"Body\"},{name:\"JSONView\"},{name:\"HexView\"},{name:\"Cookies\"},{name:\"Plugins\",hide:!0}],g=r.createClass({displayName:\"ReqDetail\",getInitialState:function(){return{initedHeaders:!1,initedTextView:!1,initedCookies:!1,initedWebForms:!1,initedJSONView:!1,initedHexView:!1,initedRaw:!1,initPlugins:!1}},componentDidMount:function(){var e=this;p.on(\"reqTabsChange\",function(){e.setState({})})},shouldComponentUpdate:a.shouldComponentUpdate,onClickBtn:function(e){this.selectBtn(e),this.setState({})},selectBtn:function(e){e.active=!0,this.state.btn=e,this.state[\"inited\"+e.name]=!0},onEdit:function(){p.trigger(\"setComposerData\",this.props.modal)},render:function(){var e=this.state,t=e.btn;t||(t=h[0],this.selectBtn(t));var n,p,g,f,m,A,M,w,v,b,y,x,T,C,N=t&&t.name,I=this.props.modal;if(A=M=\"\",I){n=I.req,f=n.rawHeaders,x=a.getFilename(I,!0),A=a.getBody(n,!0),T=a.getHex(n),C=n.base64,p=n.headers,y=a.getJson(n,!0,decodeURIComponent),delete p.Host,m=a.parseQueryString(p.cookie,/;\\s*/g,null,decodeURIComponent);var E=a.getRealUrl(I),D=E.indexOf(\"?\");w=-1==D?\"\":E.substring(D+1),w=w&&a.parseQueryString(w,null,null,decodeURIComponent),a.isUrlEncoded(n)?(v=a.parseQueryString(a.getBody(n,!0),null,null,decodeURIComponent),window.___hasFormData||(v=null)):a.isUploadForm(n)?v=a.parseUploadBody(n,!0):y&&y.isJSONText&&(v=y),g=a.getReqRawHeaders(I),M=g+\"\\r\\n\\r\\n\"+A,I.frames?b={isFrames:!0}:I.isHttps?b={isHttps:!0}:!I.requestTime||I.useFrames===!1||A||/^ws/.test(I.url)||(n.size<5120?b={message:\"Empty request body\"}:(M+=\"(Request body exceeds display limit)\",b={message:\"Request body exceeds display limit\"}))}e.raw=M,e.body=A,C=C||\"\";var S=h[7],L=u.getReqTabs(),k=L.length;return S.display=void 0,S.title=void 0,S.className=void 0,S.hide=!k,k&&1===k?(S.display=S.title=L[0].name,S.className=\"w-detail-custom-tab w-req\"):(S.display=void 0,S.title=void 0,S.className=void 0),r.createElement(\"div\",{className:\"fill v-box w-detail-ctn w-detail-request\"+(a.getBool(this.props.hide)?\" hide\":\"\")},r.createElement(s,{onClick:this.onClickBtn,btns:h}),e.initedRaw?r.createElement(c,{reqData:I,onEdit:this.onEdit,reqType:\"reqRaw\",defaultName:x,value:M,headers:g,base64:C,className:\"fill w-detail-request-raw\",hide:N!=h[0].name}):void 0,e.initedHeaders?r.createElement(\"div\",{className:\"fill w-detail-request-headers\"+(N==h[1].name?\"\":\" hide\")},r.createElement(i,{modal:f||p,enableViewSource:\"1\"})):\"\",e.initedWebForms?r.createElement(o,{vertical:\"true\",hideRight:!v,hideLeft:!w,splitRatio:.6,className:\"w-detail-request-webforms\"+(N==h[2].name?\"\":\" hide\")},r.createElement(\"div\",{className:\"fill v-box\"},r.createElement(\"div\",{className:\"w-detail-webforms-title\"},\"Query\"),r.createElement(\"div\",{className:\"fill v-box w-detail-request-query\"},r.createElement(i,{modal:w,enableViewSource:\"1\",showJsonView:\"1\"}))),r.createElement(\"div\",{className:\"fill v-box\"},r.createElement(\"div\",{className:\"w-detail-webforms-title\"},\"Body\"),r.createElement(\"div\",{className:\"fill v-box w-detail-request-form\"},y&&y.isJSONText?r.createElement(l,{reqData:I,data:y}):r.createElement(i,{modal:v,richKey:\"1\",enableViewSource:\"1\",showJsonView:\"1\"})))):\"\",e.initedTextView?r.createElement(c,{reqData:I,reqType:\"reqBody\",defaultName:x,tips:b,base64:C,value:A,className:\"fill w-detail-request-textview\",hide:N!=h[3].name}):void 0,e.initedJSONView?r.createElement(l,{reqData:I,reqType:\"reqRaw\",defaultName:x,data:y,tips:b,hide:N!=h[4].name}):void 0,e.initedHexView?r.createElement(c,{reqData:I,reqType:\"reqBody\",defaultName:x,tips:b,isHexView:\"1\",base64:C,value:T,className:\"fill n-monospace w-detail-request-hex\",hide:N!=h[5].name}):void 0,e.initedCookies?r.createElement(\"div\",{className:\"fill w-detail-request-cookies\"+(N==h[6].name?\"\":\" hide\")},r.createElement(i,{modal:m,enableViewSource:\"1\"})):void 0,e.initedPlugins?r.createElement(d,{tabs:L,hide:N!=S.name||S.hide}):void 0)}});e.exports=g},function(e,t,n){var r=n(537);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-detail-request textarea{padding:5px;border:none}.w-detail-request-cookies,.w-detail-request-form,.w-detail-request-headers,.w-detail-request-query{overflow:auto}.w-detail-inspectors-title,.w-detail-webforms-title{line-height:18px;background:var(--b-title);font-size:9pt;padding:0 10px;border-bottom:1px solid var(--c-border)}.w-detail-inspectors-title{padding:0 5px!important;font-weight:700}.w-detail-webforms-title{line-height:1pc!important}.w-detail-inspectors-url.w-props td pre{overflow-x:hidden;overflow-y:auto;max-height:35px;padding-right:8px}.w-detail-inspectors-url.w-props th{width:60px;white-space:nowrap}.w-detail-inspectors-url.w-props td{padding-right:0}.w-custom-tabs::-webkit-scrollbar,.w-custom-tabs::-webkit-scrollbar-button{width:1px;height:1px}\",\"\"])},function(e,t,n){\"use strict\";function r(e,t){return e>t?1:-1}n(539);var o=n(24),i=n(18),a=n(57),s=n(541),l=n(340),c=n(206),u=n(293),d=n(209),p=n(542),h=a.findDOMNode,g=n(342)[\"default\"],f=n(200),m=n(202),A=n(199),M=16384,w='span[style=\"color: var(--c-jb);\"]',v=/^\"(https?:)?(\\/\\/[^/]\\S+)\"$/i,b=[{name:\"Copy Object\"},{name:\"Expand All\"},{name:\"Collapse All\"}],y=[{name:\"Search Object\"}],x=[\"root\"],T=o.createClass({displayName:\"JsonViewer\",getInitialState:function(){return{lastData:{}}},shouldComponentUpdate:m.shouldComponentUpdate,preventBlur:function(e){\"INPUT\"!=e.target.nodeName&&e.preventDefault()},getCurStr:function(){var e=this.props.data;return e&&e.str},edit:function(){var e=this.getCurStr();e&&m.openEditor(e)},onContextMenu:function(e){var t=this.props.dialog,n=this.props.data||{},r=m.getMenuPosition(e,110,t?90:120);b[0].copyText=n.str||\"\",r.list=t?b:b.concat(y),this.refs.contextMenu.show(r),e.preventDefault()},onClickContextMenu:function(e){\"Expand All\"===e?this.expandAll():\"Collapse All\"===e?this.collapseAll():\"Search Object\"===e&&this.search()},search:function(){var e=this.getCurStr();e&&A.trigger(\"showJsonViewDialog\",e)},showMockDialog:function(e){var t=this,n=t.props,r=n.reqData;return r?A.trigger(\"showMockDialog\",{type:n.reqType,item:r}):void t.showNameInput(e)},showNameInput:function(e){var t=this;t.state.showDownloadInput=/w-download/.test(e.target.className),t.state.showNameInput=!0,t.forceUpdate(function(){var e=h(t.refs.nameInput),n=!e.value&&t.props.defaultName;n&&(e.value=n),e.select(),e.focus()})},hideNameInput:function(){this.state.showNameInput=!1,this.forceUpdate(function(){var e=h(this.refs.nameInput),t=this.props.defaultName;t===e.value&&(e.value=\"\")})},submit:function(e){if(13==e.keyCode||\"click\"==e.type){var t=f.valuesModal;if(t){var n=h(this.refs.nameInput),r=n.value.trim(),o=this;if(o.state.showDownloadInput)return void o.download();if(!r)return void c.error(\"The key is required\");if(/\\s/.test(r))return void c.error(\"Spaces are not allowed in the key\");var i=function(e){if(e){var i=(o.state.lastData.str||\"\").replace(/\\r\\n|\\r/g,\"\\n\");f.values.add({name:r,value:i},function(e,o){e&&0===e.ec?(t.add(r,i),n.value=\"\",n.blur()):m.showSystemError(o)})}};return t.exists(r)?void d.confirm(\"The key '\"+r+\"' is already in use. Overwrite?\",i):i(!0)}}},download:function(){var e=h(this.refs.nameInput),t=e.value.trim();e.value=\"\";var n=this.props.data||{};h(this.refs.filename).value=t,h(this.refs.content).value=n.str||\"\",h(this.refs.downloadForm).submit(),this.hideNameInput()},toggle:function(){this.setState({viewSource:!this.state.viewSource})},componentDidMount:function(){var e=i(h(this.refs.jsonViewer));e.on(\"mouseenter\",w,function(e){if(e.ctrlKey||e.metaKey){var t=i(this);v.test(t.text())&&t.addClass(\"w-is-link\")}}).on(\"mouseleave\",w,function(){i(this).removeClass(\"w-is-link\")}).on(\"mousedown\",w,function(e){if(e.ctrlKey||e.metaKey){var t=i(this);v.test(t.text())&&window.open((RegExp.$1||\"http:\")+RegExp.$2)}})},expandAll:function(){var e=this.state.expandedStatus||0;++e,this.setState({expandedStatus:e,shouldExpandNode:function(){return e}})},collapseAll:function(){var e=this.state.expandedStatus;e=e?!1:e===!1?0:!1,this.setState({expandedStatus:e,shouldExpandNode:function(){return e}})},render:function(){var e=this.state,t=e.viewSource,n=this.props,i=n.data,a=n.tips,c=\"fill v-box w-props-wrap w-json-viewer\",d=!i;if(d){if(i=e.lastData||{},a)return o.createElement(\"div\",{className:c+(n.hide?\" hide\":\"\")},o.createElement(p,{data:a}))}else e.lastData=i;var h=i.str||\"\";if(h&&t){var m=h.length-M;m>512&&(h=h.substring(0,M)+\"...\\r\\n\\r\\n(\"+m+\" characters left, you can click on the ViewAll button in the upper right corner to view all)\\r\\n\")}return o.createElement(\"div\",{className:c+(d||n.hide?\" hide\":\"\")},o.createElement(p,{data:a}),o.createElement(\"div\",{className:\"w-textarea-bar\"},o.createElement(l,{value:i.str}),o.createElement(\"a\",{onDoubleClick:this.download,onClick:this.showNameInput,draggable:\"false\"},\"Download\"),o.createElement(\"a\",{style:{display:f.hideMockMenu?\"none\":null},className:\"w-add\",onClick:this.showMockDialog,draggable:\"false\"},n.reqData?\"Mock\":\"+Key\"),t?o.createElement(\"a\",{onClick:this.edit,draggable:\"false\"},\"ViewAll\"):n.dialog?void 0:o.createElement(\"a\",{onClick:this.search,draggable:\"false\"},\"Search\"),o.createElement(\"a\",{onClick:this.toggle},t?\"JSON\":\"Text\"),o.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:e.showNameInput?\"block\":\"none\"},className:\"w-shadow w-textarea-input\"},o.createElement(\"input\",{ref:\"nameInput\",onKeyDown:this.submit,onBlur:this.hideNameInput,type:\"text\",maxLength:\"64\",placeholder:e.showDownloadInput?\"Enter filename\":\"Enter key name\"}),o.createElement(\"button\",{type:\"button\",onClick:this.submit,className:\"btn btn-primary\"},e.showDownloadInput?\"OK\":\"+Key\")),o.createElement(\"form\",{ref:\"downloadForm\",action:\"cgi-bin/download\",style:{display:\"none\"},method:\"post\",target:\"downloadTargetFrame\"},o.createElement(\"input\",{ref:\"filename\",name:\"filename\",type:\"hidden\"}),o.createElement(\"input\",{ref:\"content\",name:\"content\",type:\"hidden\"}))),o.createElement(s,{className:\"fill w-json-viewer-str\"+(t?\"\":\" hide\"),value:h}),o.createElement(\"div\",{ref:\"jsonViewer\",onContextMenu:this.onContextMenu,className:\"fill w-json-viewer-tree\"+(t?\" hide\":\"\")},o.createElement(g,{keyPath:n.keyPath||x,data:i.json,sortObjectKeys:r,shouldExpandNode:e.shouldExpandNode,expandAll:this.expandAll,collapseAll:this.collapseAll,onSearch:n.dialog?null:this.search})),o.createElement(u,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}))}});e.exports=T},function(e,t,n){var r=n(540);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-props-wrap:hover .w-textarea-bar{display:inline-block}.w-json-viewer-str,.w-json-viewer-tree{padding:5px;overflow:auto}.w-json-viewer-tree>ul{background:none!important;font-size:9pt;margin:0!important}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(202),a=r.createClass({displayName:\"TextView\",componentDidMount:function(){this.updateValue()},componentDidUpdate:function(){this.updateValue()},shouldComponentUpdate:function(e){return this.props.value!==e.value&&(this.props.value=e.value,this.updateValue()),this.props.className!==e.className},updateValue:function(){var e=this,t=e.props.value||\"\",n=o.findDOMNode(e.refs.textarea);if(e.props.hide)return n.value=\"\",e.curValue=\"\",void clearTimeout(e._timeout);if(t!==e.curValue&&(clearTimeout(e._timeout),n.value!==t)){if(t.length<10240)return n.value=t,void(e.curValue=t);e.curValue=t,e._timeout=setTimeout(function(){n.value=t},360)}},render:function(){return r.createElement(\"textarea\",{ref:\"textarea\",onKeyDown:i.preventDefault,readOnly:\"readonly\",className:this.props.className||\"\"})}});e.exports=a},function(e,t,n){\"use strict\";var r=n(24),o=n(199),i=n(510),a=n(202),s=r.createClass({displayName:\"Tips\",showFrames:function(){var e=this.props.data||{};o.trigger(\"showFrames\"+(e.inComposer?\"InComposer\":\"\"))},render:function(){var e=this.props.data||{hide:!0},t=\"w-textview-tips\"+(e.hide?\" hide\":\"\");return e.isFrames?r.createElement(\"a\",{className:t,onClick:this.showFrames},\"View Frames\"):e.isHttps?r.createElement(\"div\",{className:t},r.createElement(\"p\",null,e.importedData?null:r.createElement(i,null),\"Tunnel\"),r.createElement(\"a\",{href:a.getDocUrl(\"gui/https.html\"),target:\"_blank\"},\"Click here for more information\")):r.createElement(\"div\",{className:t},r.createElement(\"p\",null,e.message),e.url?r.createElement(\"a\",{href:e.url,target:\"_blank\"},\"Open the URL in new window\"):void 0)}});e.exports=s},function(e,t,n){\"use strict\";n(544);var r=n(24),o=n(57),i=n(541),a=n(340),s=n(202),l=n(200),c=n(206),u=n(209),d=n(199),p=n(542),h=n(213),g=6144,f=o.findDOMNode,m=r.createClass({displayName:\"Textarea\",getInitialState:function(){return{}},shouldComponentUpdate:function(e){var t=s.getBool(this.props.hide),n=s.getBool(e.hide);return this._isCaptured!==l.isCapture?(this._isCaptured=l.isCapture,!0):t===n&&this.props.value?t?!1:this.props.value!==e.value:!0},preventBlur:function(e){\"INPUT\"!=e.target.nodeName&&e.preventDefault()},edit:function(){s.openEditor(this.props.value)},showMockDialog:function(e){var t=this,n=t.props,r=n.reqData;return r?d.trigger(\"showMockDialog\",{type:n.reqType,item:r}):void t.showNameInput(e)},showNameInput:function(e){var t=this;t.state.showDownloadInput=/w-download/.test(e.target.className),t.state.showNameInput=!0,t.forceUpdate(function(){var e=f(t.refs.nameInput),n=!e.value&&t.props.defaultName;n&&(e.value=n),e.select(),e.focus()})},download:function(){var e=f(this.refs.nameInput),t=e.value.trim();e.value=\"\";var n=this.props.base64;f(this.refs.filename).value=t,f(this.refs.type).value=n?\"base64\":\"\",f(this.refs.headers).value=this.props.headers||\"\",f(this.refs.content).value=null!=n?n:this.props.value||\"\",f(this.refs.downloadForm).submit(),this.hideNameInput()},submit:function(e){if(13==e.keyCode||\"click\"==e.type){var t=l.valuesModal;if(t){var n=f(this.refs.nameInput),r=n.value.trim(),o=this;if(o.state.showDownloadInput)return void o.download();if(!r)return void c.error(\"The key is required\");if(/\\s/.test(r))return void c.error(\"Spaces are not allowed in the key\");var i=function(e){if(e){var i=(o.props.value||\"\").replace(/\\r\\n|\\r/g,\"\\n\");l.values.add({name:r,value:i},function(e,o){e&&0===e.ec?(t.add(r,i),n.value=\"\",n.blur()):s.showSystemError(o)})}};return t.exists(r)?void u.confirm(\"The key '\"+r+\"' is already in use. Overwrite?\",i):i(!0)}}},hideNameInput:function(){this.state.showNameInput=!1,this.forceUpdate(function(){var e=f(this.refs.nameInput),t=this.props.defaultName;t===e.value&&(e.value=\"\")})},render:function(){var e=this.props,t=e.value||\"\",n=t.length-g;n>512&&(t=t.substring(0,g)+\"...\\r\\n\\r\\n(\"+n+\" characters remaining (click View All in top-right corner)\\r\\n\");var o=e.isHexView;return this.state.value=t,r.createElement(\"div\",{className:\"fill v-box w-textarea\"+(e.hide?\" hide\":\"\")},r.createElement(p,{data:e.tips}),r.createElement(\"div\",{className:\"w-textarea-bar\"+(t?\"\":\" hide\")},\"reqRaw\"===e.reqType?r.createElement(\"a\",{onClick:e.onEdit},r.createElement(h,{name:\"send\"}),\"Edit\"):void 0,r.createElement(a,{value:e.value}),o?r.createElement(a,{name:\"AsHex\",value:s.getHexText(e.value)}):void 0,r.createElement(\"a\",{onDoubleClick:this.download,onClick:this.showNameInput,draggable:\"false\"},\"Download\"),r.createElement(\"a\",{style:{display:l.hideMockMenu?\"none\":null},className:\"w-add\",onClick:this.showMockDialog,draggable:\"false\"},e.reqData?\"Mock\":\"+Key\"),r.createElement(\"a\",{onClick:this.edit,draggable:\"false\"},\"ViewAll\"),r.createElement(\"div\",{onMouseDown:this.preventBlur,style:{display:this.state.showNameInput?\"block\":\"none\"},className:\"w-shadow w-textarea-input\"},r.createElement(\"input\",{ref:\"nameInput\",onKeyDown:this.submit,onBlur:this.hideNameInput,type:\"text\",maxLength:\"64\",placeholder:this.state.showDownloadInput?\"Enter filename\":\"Enter key name\"}),r.createElement(\"button\",{type:\"button\",onClick:this.submit,className:\"btn btn-primary\"},this.state.showDownloadInput?\"OK\":\"+Key\"))),r.createElement(i,{className:e.className||\"\",value:t}),r.createElement(\"form\",{ref:\"downloadForm\",action:\"cgi-bin/download\",style:{display:\"none\"},method:\"post\",target:\"downloadTargetFrame\"},r.createElement(\"input\",{ref:\"filename\",name:\"filename\",type:\"hidden\"}),r.createElement(\"input\",{ref:\"type\",name:\"type\",type:\"hidden\"}),r.createElement(\"input\",{ref:\"headers\",name:\"headers\",type:\"hidden\"}),r.createElement(\"input\",{ref:\"content\",name:\"content\",type:\"hidden\"})))}});e.exports=m},function(e,t,n){var r=n(545);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-textarea{position:relative}.w-textarea-bar{display:none;position:absolute;z-index:1;border-radius:2px;top:2px;right:9pt;font-size:9pt;white-space:nowrap;line-height:1.5;background:var(--b-heavy)}.w-textarea:hover .w-textarea-bar{display:block}.w-textarea-bar a{display:inline-block;text-decoration:none;color:var(--c-thin);padding:0 5px}.w-textarea-bar a:hover{color:var(--c-default)}.w-textarea-input{display:block;position:absolute;background:var(--b-default);z-index:101;top:22px;right:0;display:none;white-space:nowrap}.w-textarea-input,.w-textarea-input input{border:1px solid var(--c-border);border-radius:2px}.w-textarea-input input{width:246px;height:2pc;padding:0 5px;vertical-align:middle}.w-textarea-input .btn{height:2pc;padding:0 9pt;vertical-align:middle;border-radius:0;border-top-right-radius:2px;border-bottom-right-radius:2px;margin-left:1px}.w-textview-tips{color:var(--c-disabled);text-align:center;width:520px;height:70px;position:absolute;left:50%;top:50%;margin:-50px 0 0 -260px;display:flex;justify-content:center;align-items:center;flex-direction:column}.w-textview-tips p{font-size:14px;line-height:36px;margin:0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.w-textview-tips a{font-size:9pt;color:var(--c-disabled);text-decoration:none}a.w-textview-tips{color:var(--c-link)}.w-textview-tips a:hover,a.w-textview-tips:hover{text-decoration:underline}.w-textview-tips a.glyphicon-question-sign{text-decoration:none!important}\",\"\"]);\n},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=n(547),a=r.createClass({displayName:\"PluginsTabs\",getInitialState:function(){var e=this.props.tabs[0];return{active:e&&e.plugin}},shouldComponentUpdate:o.shouldComponentUpdate,onSelect:function(e){this.setState({active:e.plugin})},render:function(){var e=this,t=e.props,n=t.tabs,o=t.hide,a=this.state.active,s=n.length<2;return s&&(a=n[0]&&n[0].plugin,a&&(this.state.active=a)),r.createElement(\"div\",{className:\"fill box w-plugins-tabs\"+(o?\" hide\":\"\")},r.createElement(\"div\",{className:\"w-plugins-tabs-list\"+(s?\" hide\":\"\")},n.map(function(t){return r.createElement(\"button\",{key:t.plugin,onClick:function(){e.onSelect(t)},className:\"btn btn-default\"+(a==t.plugin?\" active\":\"\"),title:\"[\"+t.plugin+\"] \"+t.name},t.name)})),r.createElement(i,{active:a,hide:o,tabs:n}))}});e.exports=a},function(e,t,n){\"use strict\";var r=n(24),o=n(202),i=n(548),a=n(199),s=6,l=r.createClass({displayName:\"TabMgr\",getInitialState:function(){return this.initedTabs={},{}},shouldComponentUpdate:o.shouldComponentUpdate,componentDidMount:function(){var e=this;a.on(\"setComposer\",function(){var t=!e.props.hide&&e.props.modal;if(t){var n=e.refs[e.props.active];n&&n.compose(t)}})},isInited:function(e){var t=this.initedTabs,n=e.action,r=null!=t[n];if(this.props.active!==e.plugin)return r;if(r)return t[n]=Date.now(),!0;var o=Object.keys(t);if(o.length>=s){var i,a;o.forEach(function(e){var n=t[e];(null==i||i>n)&&(i=n,a=e)}),a&&delete t[a]}return this.initedTabs[n]=Date.now(),!0},render:function(){var e=this,t=e.props,n=t.tabs,o=t.hide,a=t.active,s=!0;return n=n.map(function(t){var n=o||a!==t.plugin;return n||(s=!1),e.isInited(t)&&r.createElement(i,{ref:t.plugin,key:t.plugin,src:t.action,hide:n})}),r.createElement(\"div\",{className:\"fill v-box \"+(t.className||\"\")+(s?\" hide\":\"\")},n)}});e.exports=l},function(e,t,n){\"use strict\";function r(e,t){\"function\"==typeof e&&e(l(t))}var o=n(24),i=n(202),a=n(200),s=n(199),l=n(300),c=n(549),u=a.networkModal,d=o.createClass({displayName:\"TabFrame\",getInitialState:function(){var e=this.props.src;return e+=-1===e.indexOf(\"?\")?\"?\":\"&\",{url:e+\"???_WHISTLE_PLUGIN_INSPECTOR_TAB_\"+a.getPort()+\"???\"}},componentDidMount:function(){s.on(\"selectedSessionChange\",this.handlePush)},componentWillUnmount:function(){s.off(\"selectedSessionChange\",this.handlePush)},shouldComponentUpdate:i.shouldComponentUpdate,compose:function(e){this.handlePush(null,null,e)},handlePush:function(e,t,n){try{var r=this.refs.iframe.getWindow();r&&\"function\"==typeof r.__pushWhistle5b6af7b9884e1165SessionActive__&&(n?(r.__pushWhistle5b6af7b9884e1165SessionActive__(null,null,n),n=null):this.props.hide?r.__pushWhistle5b6af7b9884e1165SessionActive__(null,!0):r.__pushWhistle5b6af7b9884e1165SessionActive__(t||u.getActive()))}catch(o){}this.composeItem=n},componentDidUpdate:function(){this.handlePush()},onLoad:function(){this.composeItem&&(this.handlePush(null,null,this.composeItem),this.composeItem=null)},render:function(){var e=this.props.hide?\"none\":void 0;return window.onWhistleInspectorCustomTabReady=r,o.createElement(c,{onLoad:this.onLoad,ref:\"iframe\",src:this.state.url,style:{display:e}})}});e.exports=d},function(e,t,n){\"use strict\";n(550);var r=n(24),o=n(57),i=n(200),a=r.createClass({displayName:\"IFrame\",getWindow:function(){return o.findDOMNode(this.refs.iframe).contentWindow},onload:function(e){var t=this.props.onLoad;\"function\"==typeof t&&t(e),i.handleIframeLoad(e)},render:function(){var e=this.props,t=e.className;return r.createElement(\"div\",{className:\"fill box w-fix-drag w-iframe\"+(t?\" \"+t:\"\"),style:e.style},r.createElement(\"iframe\",{ref:\"iframe\",onLoad:this.onload,src:e.src,className:\"fill\"}))}});e.exports=a},function(e,t,n){var r=n(551);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,'.w-iframe{position:relative;min-width:35pc}.w-iframe>iframe.fill{display:block;width:100%;height:100%;outline:0;border:none;margin:0;padding:0}.w-iframe[allow-dragover=\"1\"]>iframe.fill{pointer-events:none}',\"\"])},function(e,t,n){\"use strict\";n(553);var r=n(24),o=n(555),i=n(337),a=n(202),s=n(527),l=n(543),c=n(558),u=n(538),d=n(200),p=n(546),h=n(199),g=[\"Name\",\"Value\",\"Domain\",\"Path\",\"Expires\",\"Max-Age\",\"HttpOnly\",\"Secure\",\"SameSite\",\"Partitioned\"],f=r.createClass({displayName:\"ResDetail\",getInitialState:function(){return{initedHeaders:!1,initedTrailers:!1,initedTextView:!1,initedPreview:!1,initedCookies:!1,initedJSONView:!1,initedHexView:!1,initedRaw:!1,initPlugins:!1,btns:[{name:\"Raw\"},{name:\"Headers\"},{name:\"Preview\"},{name:\"TextView\",display:\"Body\"},{name:\"JSONView\"},{name:\"HexView\"},{name:\"Cookies\"},{name:\"Trailers\"},{name:\"Plugins\",hide:!0}]}},componentDidMount:function(){var e=this;h.on(\"resTabsChange\",function(){e.setState({})})},shouldComponentUpdate:a.shouldComponentUpdate,onClickBtn:function(e){this.selectBtn(e),this.setState({})},selectBtn:function(e){e.active=!0,this.state.btn=e,this.state[\"inited\"+e.name]=!0},render:function(){var e=this.state,t=e.btns,n=e.btn;n||(n=t[0],this.selectBtn(n));var h,f,m,A,M,w,v,b,y,x,T,C,N,I,E,D=n&&n.name,S=this.props.modal;if(y=x=\"\",S){h=S.res,N=a.getFilename(S,!0),f=h.rawHeaders,m=h.rawTrailers,y=a.getBody(h),E=a.getHex(h),I=h.base64,w=h.headers,v=h.trailers,T=a.getJson(h),w&&w[\"set-cookie\"]&&(b=w[\"set-cookie\"],Array.isArray(b)||(b=\"string\"==typeof b?[b]:[]),b=b.map(function(e){e=a.parseQueryString(e,/;\\s*/,null,decodeURIComponent,!0);var t=[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"];for(var n in e)switch(n.toLowerCase()){case\"domain\":t[2]=e[n];break;case\"path\":t[3]=e[n];break;case\"expires\":t[4]=e[n];break;case\"max-age\":t[5]=e[n];break;case\"httponly\":t[6]=\"√\";break;case\"secure\":t[7]=\"√\";break;case\"samesite\":t[8]=e[n];break;case\"partitioned\":t[9]=\"√\";break;default:t[0]||(t[0]=n,t[1]=e[n])}return t}));var L,k,j,U=!0,B=h.statusCode,R=D===t[2].name;if(null!=B){A=a.getResRawHeaders(S),M=v?a.objectToString(v,h.rawTrailerNames):\"\",x=A+\"\\r\\n\\r\\n\"+y;var z=!S.resError&&a.getRawType(w),Q=a.getContentType(z);j=\"JSON\"===Q,\"image/svg+xml\"===z?(L=\"data:image/svg+xml;base64,\"+(h.base64||\"\"),U=!1):\"IMG\"===Q?(L=y||(h.size?S.url:void 0),U=!1):R&&h.base64&&(\"HTML\"===Q||T&&T.isJSONText&&a.likeJson(y))&&(T&&T.isJSONText?j=!0:(!y||-1!==y.indexOf(\"<\")&&-1!==y.indexOf(\">\"))&&(k=S,U=!1))}L&&(k=S),S.frames?C={isFrames:!0,inComposer:S.inComposer}:S.isHttps?C=!y&&{isHttps:!0}:h.size>=0&&w&&S.useFrames!==!1&&!y&&S.endTime&&!/^ws/.test(S.url)&&(C={url:S.url},h.size<5120?C.message=\"Empty response body\":(x+=\"(Response body exceeds display limit)\",C.message=\"Response body exceeds display limit\")),M&&(x+=\"\\r\\n\\r\\n\"+M)}e.raw=x,e.body=y,U&&\"Preview\"===D&&(R=!1,j?(D=\"JSONView\",e.initedJSONView=!0):(D=\"TextView\",e.initedTextView=!0)),I=I||\"\";var O=t[8],H=d.getResTabs(),F=this.props.inComposer?0:H.length;return O.hide=!F,F&&1===F?(O.display=O.title=H[0].name,O.className=\"w-detail-custom-tab\"):(O.display=void 0,O.title=void 0,O.className=void 0),r.createElement(\"div\",{className:\"fill v-box w-detail-ctn w-detail-res\"+(a.getBool(this.props.hide)?\" hide\":\"\")},r.createElement(s,{onClick:this.onClickBtn,btns:t}),e.initedRaw?r.createElement(l,{reqData:S,reqType:\"resRaw\",defaultName:N,value:x,headers:A,base64:I,className:\"fill w-detail-res-raw\",hide:D!=t[0].name}):void 0,e.initedHeaders?r.createElement(\"div\",{className:\"fill w-detail-res-headers\"+(D==t[1].name?\"\":\" hide\")},r.createElement(i,{modal:f||w,enableViewSource:\"1\"})):void 0,e.initedPreview?r.createElement(c,{imgSrc:L,data:k,hide:!R}):void 0,e.initedTextView?r.createElement(l,{reqData:S,reqType:\"resBody\",defaultName:N,tips:C,base64:I,value:y,className:\"fill w-detail-res-textview\",hide:D!=t[3].name}):void 0,e.initedJSONView?r.createElement(u,{reqData:S,reqType:\"resJson\",defaultName:N,data:T,tips:C,hide:D!=t[4].name}):void 0,e.initedHexView?r.createElement(l,{reqData:S,reqType:\"resBody\",defaultName:N,isHexView:\"1\",base64:I,tips:C,value:E,className:\"fill n-monospace w-detail-res-hex\",hide:D!=t[5].name}):void 0,e.initedCookies?r.createElement(\"div\",{className:\"fill w-detail-res-cookies\"+(D==t[6].name?\"\":\" hide\")},b&&b.length?r.createElement(o,{head:g,modal:b}):void 0):void 0,e.initedTrailers?r.createElement(\"div\",{className:\"fill w-detail-res-headers\"+(D==t[7].name?\"\":\" hide\")},r.createElement(i,{modal:m||v,enableViewSource:\"1\"})):void 0,e.initedPlugins?r.createElement(p,{tabs:H,hide:D!=O.name||O.hide}):void 0)}});e.exports=f},function(e,t,n){var r=n(554);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-detail-res textarea{padding:5px;border:none}.w-detail-res-cookies,.w-detail-res-headers{overflow:auto}.w-detail-res-cookies table{table-layout:auto}.w-detail-res-cookies tbody>tr>td:nth-child(7),.w-detail-res-cookies tbody>tr>td:nth-child(8),.w-detail-res-cookies tbody>tr>td:nth-child(10){text-align:center;vertical-align:middle}.w-show-history .w-btn-group-sm{height:22px;overflow:hidden}\",\"\"])},function(e,t,n){\"use strict\";n(556);var r=n(24),o=r.createClass({displayName:\"Table\",render:function(){var e=this.props.head,t=Array.isArray(e)&&e.length,n=this.props.modal||[];return r.createElement(\"table\",{className:\"table w-table\"},t?r.createElement(\"thead\",null,r.createElement(\"tr\",null,e.map(function(e){return r.createElement(\"th\",{key:e},e)}))):\"\",r.createElement(\"tbody\",{className:\"w-hover-body\"},n.map(function(e,t){return r.createElement(\"tr\",{key:t},e.map(function(e,n){return r.createElement(\"td\",{key:t+\".\"+n},e)}))})))}});e.exports=o},function(e,t,n){var r=n(557);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-table>thead>tr>th{white-space:nowrap;background:var(--b-bar);border-bottom:1px;font-weight:500;color:var(--c-default)}.w-table td,.w-table th{font-size:9pt;padding:3px 5px!important;border-top:none;border-bottom:1px solid var(--c-border);word-break:break-word}.w-order-table table tr>th:first-child{width:50px}.w-order-table-head{background:var(--b-bar)}.w-order-table .table td,.w-order-table .table th{border-top:none;border-bottom:1px solid var(--c-border);vertical-align:middle}.w-order-table .w-order-table-head th{padding:4px 8px!important}.w-order-table-body{max-height:calc(100% - 28px);overflow-y:auto}.w-order-table-body td,.w-order-table-body th{font-size:13px;font-weight:400}.w-table-cell-middle{vertical-align:middle!important}.w-order-table-operation{white-space:nowrap}.w-order-table-operation a{margin-right:20px}.w-order-table-operation a:last-child{margin-right:0}\",\"\"])},function(e,t,n){\"use strict\";n(559);var r=n(24),o=n(202),i=\"client\"===o.getQuery().mode,a=function(){return i&&window.WebView&&\"WebViewElement\"===window.WebView.name},s=r.createClass({displayName:\"ImageView\",shouldComponentUpdate:o.shouldComponentUpdate,preview:function(){o.openPreview(this.props.data)},getPreviewUrl:function(){var e=!this.props.imgSrc&&a()&&this.props.data;if(e&&e.res.base64)return this._curData!==e&&(this._curData=e,this._previewUrl=o.getPreviewUrl(e)),this._previewUrl},getPreviewElem:function(e){if(e)return r.createElement(\"webview\",{src:e,className:\"fill\"});var t=this.props;return t.imgSrc?r.createElement(\"img\",{src:t.imgSrc}):t.data?r.createElement(\"a\",{className:\"w-image-link\",onClick:this.preview},\"Preview page in new window\"):void 0},render:function(){var e=this.props,t=this.getPreviewUrl(e.data),n=e.imgSrc&&!t;return r.createElement(\"div\",{className:\"fill w-image-view\"+(t?\" w-image-webview\":\"\")+(e.hide?\" hide\":\"\")+(n?\" w-image-bg\":\"\")},t||e.imgSrc?r.createElement(\"div\",{className:\"w-textarea-bar\"},r.createElement(\"a\",{onClick:this.preview},\"Open in new window\")):void 0,this.getPreviewElem(t))}});e.exports=s},function(e,t,n){var r=n(560);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-image-view{font-size:0;white-space:nowrap;overflow:auto;text-align:center;position:relative}.w-image-bg{background:var(--b-img);display:flex;align-items:center;justify-content:center}.w-image-view.w-image-webview{padding:0;display:flex}.w-image-view img{max-width:100%}.w-image-view .w-image-link{display:inline-block;width:300px;line-height:30px;position:absolute;top:50%;left:50%;margin:-15px 0 0 -150px;font-size:14px}.w-image-view:hover .w-textarea-bar{display:block}.w-image-webview .fill{width:100%}\",\"\"])},function(e,t,n){\"use strict\";n(562);var r=n(24),o=n(202),i=n(220),a=n(564),s=n(575),l=n(577),c=n(200),u=n(578),d=n(18),p=r.createClass({displayName:\"Frames\",getInitialState:function(){return{modal:new l,framesCtx:d({})}},componentDidMount:function(){var e=this;c.on(\"framesUpdate\",function(){e.setState({})})},shouldComponentUpdate:o.shouldComponentUpdate,onClickFrame:function(e){var t=this.state.modal;t.setActive(e),this.setState({})},onUpdate:function(){this.setState({})},onDOMReady:function(){this.refs.frameList.autoRefresh()},render:function(){var e=this.props,t=this.state.modal,n=this.state.framesCtx,o=t.reset(e.frames),l=e.data||\"\",c=t.getActive(),d=!o||e.hide;return c&&c.hide&&(c=null),r.createElement(\"div\",{className:\"fill v-box w-frames\"+(e.hide?\" hide\":\"\")},r.createElement(u,{inited:!d},r.createElement(i,{hide:d,vertical:\"true\",rightWidth:\"250\",onDOMReady:this.onDOMReady},r.createElement(a,{ref:\"frameList\",framesCtx:n,reqData:l,modal:t,onUpdate:this.onUpdate,onClickFrame:this.onClickFrame}),r.createElement(s,{framesCtx:n,data:l,frame:c}))),r.createElement(\"div\",{className:\"w-no-frames\"+(o?\" hide\":\"\")},\"No Frames\"))}});e.exports=p},function(e,t,n){var r=n(563);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,'.w-frames{font-size:9pt;min-width:35pc}.w-frames-action{background:var(--b-title);position:relative;border-bottom:1px solid var(--c-border);height:22px;line-height:20px;min-width:35pc}.w-frames-action>.w-menu-wrapper:first-child>a:first-child{margin-left:10px}.w-frames-action a{margin-left:15px;text-decoration:none!important;color:var(--c-default);vertical-align:super}.w-frames-action .w-menu-item{top:23px}.w-frames-action .w-menu-wrapper{display:inline-block}.w-frames-action .w-connect-abort{right:157px}.w-frames-action a:last-child{right:10px}.w-frames-action a:hover{color:var(--c-link)}.w-frames-list{overflow-x:hidden;overflow-y:auto}.w-frames-list ul{list-style:none;margin:0;padding:0 0 5px}.w-frames-list ul .glyphicon{margin-right:10px}.w-frames-list li{cursor:default;margin:0;padding:0 5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:24px;line-height:23px;list-style:none;border-bottom:1px solid var(--c-border);background:var(--b-default)}.w-frames-list li[class=\"\"]:hover{background:var(--b-hover)}.w-frames-list .w-frames-send:hover{background:var(--b-req-hover)}.w-frames-list .w-conn-error:hover{background:var(--b-error-hover)}.w-frames-list .w-conn-closed:hover{background:var(--b-gray)}.w-frames-action .w-switch-btn .w-menu-item{left:10px}.w-frames-action .w-dropdown{margin-left:15px}.w-not-decompressed{font-style:italic}.w-not-decompressed em{color:var(--c-error);margin-right:5px}.w-frames-action .w-filter-con{margin-right:10pc;border-right:1px solid var(--c-border)}.w-frames-list .w-frames-send{background:var(--b-req)}.w-frames-list .w-conn-error{background:var(--b-error);color:var(--c-error)}.w-frames-list .w-conn-closed{background:var(--b-active);color:var(--c-error)}.w-conn-closed span{color:var(--c-error)}.w-frames-list .w-frames-selected{background:var(--c-link)!important;color:var(--c-active)!important}.w-frames-list .w-frames-selected .glyphicon{color:var(--c-active)}.w-frames-com-action{border-bottom:1px solid var(--c-border);position:relative;text-indent:10px;line-height:22px;height:23px;background:var(--b-title)}.w-frames-com-action .btn-group,.w-frames-com-action .w-format-json-btn{margin:1px 15px 1px 0;float:right;font-size:9pt!important}.w-frames-com-action .btn{padding:0 5px!important;height:20px;line-height:19px}.w-frames-com-action .dropdown-menu{min-width:0;right:0;font-size:9pt}.w-frames-com-action .dropdown-menu a{padding:0}.w-com-tabs .glyphicon-arrow-right,.w-detail-inspectors .glyphicon-arrow-right,.w-frames-com-action .glyphicon-arrow-right,.w-frames-list .glyphicon-arrow-right{color:var(--c-req)}.w-com-tabs .glyphicon-arrow-left,.w-detail-inspectors .glyphicon-arrow-left,.w-frames-com-action .glyphicon-arrow-left,.w-frames-list .glyphicon-arrow-left{color:var(--c-res)}.w-frames-data textarea{border:none}.w-frames-list .w-frames-ignore,.w-frames-list .w-frames-ignore .glyphicon{color:var(--c-disabled)!important;font-style:italic!important}.w-frames-list .w-frames-ignore.w-frames-selected,.w-frames-list .w-frames-ignore.w-frames-selected .glyphicon{color:var(--c-active)!important}.w-frames-com-action .w-frames-hex-data{margin-left:-10px;font-weight:400}.w-frames-crlf{font-weight:400;margin-left:1px}.w-frames-com-action .w-frames-checked{font-weight:700}.w-frames-com-action input[type=checkbox]{vertical-align:text-bottom;margin-right:5px}.w-frames-com textarea{padding:5px}.w-frames-bin{font-weight:500}',\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(18),a=n(202),s=n(290),l=n(565),c=n(200),u=n(568),d=n(293),p=n(213),h=o.findDOMNode,g=[{value:0,icon:\"arrow-right\",text:\"Send\"},{value:1,icon:\"arrow-right\",text:\"Pause\"},{value:2,icon:\"arrow-right\",text:\"Ignore\"}],f=[{value:0,icon:\"arrow-left\",text:\"Receive\"},{value:1,icon:\"arrow-left\",text:\"Pause\"},{value:2,icon:\"arrow-left\",text:\"Ignore\"}],m=[{name:\"Copy\"},{name:\"Replay\"},{name:\"Edit\"},{name:\"Abort\"},{name:\"Clear\"}],A=\"invalid compressed data\",M=function(e){return e.closed?e.lastErr!==A:e.err&&e.err!==A},w=r.createClass({displayName:\"FrameList\",getInitialState:function(){return{}},onFilterChange:function(e){var t=this;t.props.modal.search(e),clearTimeout(t.filterTimer),t.filterTimer=setTimeout(function(){t.filterTimer=null,t.setState({keyword:e.trim()})},500)},componentDidMount:function(){var e=this,t=this.props.framesCtx;t.on(\"autoRefreshFrames\",e.autoRefresh),t.on(\"composeFrameId\",function(n,r){var o=r&&e.props.modal,i=o&&o.list;if(i)for(var a=0,s=i.length;s>a;a++){var l=i[a];if(l&&l.frameId===r)return t.trigger(\"composeFrame\",l)}}),t.on(\"enableRecordFrame\",function(){e.refs.recordBtn.enable()})},onDoubleClick:function(){this.props.framesCtx.trigger(\"toggleFramesInspectors\")},componentWillUpdate:function(){this.atBottom=this.shouldScrollToBottom()},componentDidUpdate:function(){this.atBottom&&this.autoRefresh();var e=this.props.reqData;e&&(e.pauseRecordFrames?this.refs.recordBtn.enable(\"pause\"):e.stopRecordFrames?this.refs.recordBtn.enable(\"stop\"):this.refs.recordBtn.enable())},replay:function(){var e=this.props.reqData;return!e||e.closed||e.err?void this.autoRefresh():void this.props.framesCtx.trigger(\"replayFrame\",this.props.modal.getActive())},stopRefresh:function(){this.container.scrollTop=this.container.scrollTop-10},autoRefresh:function(){this.container.scrollTop=1e8},clear:function(){this.props.modal.clear(),this.setState({}),this.props.onUpdate&&this.props.onUpdate()},compose:function(){this.props.framesCtx.trigger(\"composeFrame\",this.props.modal.getActive())},checkActive:function(){var e=this.props.reqData;return!e||e.closed||e.err?void this.autoRefresh():e},abort:function(){var e=this,t=e.props.reqData;t&&!M(t)&&c.socket.abort({reqId:t.id},function(n,r){n?(delete t.lastErr,t.closed=!0,e.autoRefresh(),e.setState({})):a.showSystemError(r)})},changeStatus:function(e,t,n){var r=this,o={reqId:e.id};n?o.sendStatus=t.value:o.receiveStatus=t.value,c.socket.changeStatus(o,function(o,i){o?(n?e.sendStatus=t.value:e.receiveStatus=t.value,r.setState({})):a.showSystemError(i)})},onSendStatusChange:function(e){var t=this.checkActive();t&&this.changeStatus(t,e,!0)},onReceiveStatusChange:function(e){var t=this.checkActive();t&&this.changeStatus(t,e)},onClear:function(e){if(e.ctrlKey||e.metaKey)if(e.stopPropagation(),88===e.keyCode){if(!a.hasShortcut(\"clearNetworkFrames\"))return;this.clear()}else if(13===e.keyCode){if(e.stopPropagation(),!a.hasShortcut(\"replaySelectedFrame\"))return;this.replay()}},shouldScrollToBottom:function(){var e=this.container,t=this.content,n=this.props.modal,r=e.scrollTop+e.offsetHeight+5>t.offsetHeight;return r&&n.update(),r},setContainer:function(e){this.container=h(e)},setContent:function(e){this.content=h(e)},handleAction:function(e){if(\"top\"===e)return void(this.container.scrollTop=0);if(\"bottom\"===e)return this.autoRefresh();var t=\"refresh\"===e,n=this.props.reqData;if(n){if(\"pause\"===e)return n.stopRecordFrames=!0,void(n.pauseRecordFrames=!0);n.pauseRecordFrames=!1,n.stopRecordFrames=!t}return t?this.autoRefresh():void 0},onDragStart:function(e){var t=i(e.target).closest(\"li\").attr(\"data-id\");t&&e.dataTransfer.setData(\"frameDataId\",t)},onContextMenu:function(e){e.preventDefault();var t=i(e.target).closest(\"li\").attr(\"data-id\"),n=this.props.modal,r=n.getItem(t),o=!!M(this.props.reqData);this.currentFocusItem=r,m[0].disabled=!r,m[0].copyText=r&&r.data||\"\",m[1].disabled=!r||o,m[2].disabled=!r,m[3].disabled=o,m[4].disabled=!n.list.length;var s=a.getMenuPosition(e,130,130);s.list=m,this.refs.contextMenu.show(s)},onClickContextMenu:function(e){var t=this.currentFocusItem,n=this.props.framesCtx;switch(this.currentFocusItem=null,e){case\"Replay\":t&&n.trigger(\"replayFrame\",t);break;case\"Edit\":t&&n.trigger(\"composeFrame\",t);break;case\"Abort\":this.abort();break;case\"Clear\":this.clear()}},render:function(){var e=this,t=e.props,n=this.state,o=t.reqData||{},i=t.onClickFrame,c=e.props.modal,h=n.keyword,m=c.getActive(),A=c.getList();return a.socketIsClosed(o),r.createElement(\"div\",{className:\"fill v-box w-frames-list\"},r.createElement(\"div\",{className:\"w-frames-action\",onMouseDown:a.preventBlur},r.createElement(u,{ref:\"recordBtn\",onClick:this.handleAction,disabledRecord:o.closed}),r.createElement(\"a\",{onClick:e.clear,className:\"w-remove-menu\",draggable:\"false\"},r.createElement(p,{name:\"remove\"}),\"Clear\"),r.createElement(\"a\",{onClick:e.replay,className:\"w-remove-menu\"+(!m||o.closed?\" w-disabled\":\"\"),draggable:\"false\"},r.createElement(p,{name:\"repeat\"}),\"Replay\"),r.createElement(\"a\",{onClick:e.compose,className:\"w-remove-menu\"+(m?\"\":\" w-disabled\"),draggable:\"false\"},r.createElement(p,{name:\"send\"}),\"Edit\"),r.createElement(\"a\",{onClick:e.abort,className:\"w-remove-menu\"+(M(o)?\" w-disabled\":\"\"),draggable:\"false\"},r.createElement(p,{name:\"ban-circle\"}),\"Abort\"),r.createElement(l,{disabled:o.closed,value:o.sendStatus||0,onChange:e.onSendStatusChange,options:g}),r.createElement(l,{disabled:o.closed,value:o.receiveStatus||0,onChange:e.onReceiveStatusChange,options:f})),r.createElement(\"div\",{tabIndex:\"0\",onKeyDown:this.onClear,style:{background:h?\"var(--b-filtered)\":void 0},onScroll:e.shouldScrollToBottom,ref:e.setContainer,className:\"fill w-frames-list\",onContextMenu:this.onContextMenu},r.createElement(\"ul\",{ref:e.setContent,onDragStart:e.onDragStart},A.map(function(t){var n=\"\";if((t.closed||t.err||t.isError)&&(o.closed=o.closed||t.closed,o.err=t.err||t.data,n=t.closed?\" w-conn-closed\":\" w-conn-error\",t.title=t.title||\"Date: \"+a.toLocaleString(new Date(parseInt(t.frameId,10)))),null==t.data&&(t.data=a.getBody(t,!0),t.data.length>500&&(t.data=t.data.substring(0,500)+\"...\")),!t.title&&!t.closed){t.title=\"Date: \"+a.toLocaleString(new Date(parseInt(t.frameId,10)))+\"\\nPath: \"+(t.isClient?\"Client -> Server\":\"Server -> Client\"),t.opcode&&(t.title+=\"\\nOpcode: \"+t.opcode,t.title+=\"\\nType: \"+(1==t.opcode?\"Text\":\"Binary\")),t.compressed&&(t.title+=\"\\nCompressed: \"+(t.compressed?\"Yes\":\"No\")),t.mask&&(t.title+=\"\\nMask: \"+(t.mask?\"Yes\":\"No\"));var s=t.length;s>=0&&(t.title+=\"\\nLength: \"+a.formatSize(s,t.unzipLen))}var l=\"arrow-left\";t.closed?l=\"minus-sign\":t.isClient&&(l=\"arrow-right\");var c=t.notDecompressed&&t.compressed;return r.createElement(\"li\",{draggable:!0,key:t.frameId,\"data-id\":t.frameId,title:t.title,style:{display:t.hide?\"none\":void 0},onClick:function(){i&&i(t)},onDoubleClick:e.onDoubleClick,className:(t.isClient?\"w-frames-send\":\"\")+(t.ignore?\" w-frames-ignore\":\"\")+(t.active?\"  w-frames-selected\":\"\")+(2==t.opcode?\" w-frames-bin\":\"\")+(c?\" w-not-decompressed\":\"\")+n},r.createElement(p,{name:l}),c?r.createElement(\"em\",null,\"[Not decompressed]\"):null,t.data)}))),r.createElement(s,{onChange:e.onFilterChange}),r.createElement(d,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}))}});e.exports=w},function(e,t,n){\"use strict\";n(566);var r=n(24),o=n(202),i=n(213),a=r.createClass({displayName:\"DropDown\",getInitialState:function(){return{}},onChange:function s(e){var s=this.props.onChange;s&&s(e),null==this.props.value&&this.setState({selectedOption:e})},onMouseEnter:function(){var e=this.props.onBeforeShow;e&&e(),this.setState({hover:!0})},onMouseLeave:function(){this.setState({hover:!1})},getSelectedOption:function(){var e=this.props,t=e.value;return null!=t?o.findArray(e.options,function(e){return e===t||e.value===t}):void 0},render:function(){var e=this,t=e.props.help,n=e.props.options||[],o=n[0]||{},a=e.props.disabled,s=this.getSelectedOption()||e.state.selectedOption||o;return r.createElement(\"div\",{className:\"dropdown w-dropdown\",onMouseEnter:e.onMouseEnter,onMouseLeave:e.onMouseLeave},r.createElement(\"div\",{style:{color:s===o?void 0:s.color||\"var(--c-error)\"},title:s.text,className:\"dropdown-toggle w-dropdown-text\"+(a?\" w-disabled\":\"\")},s.icon?r.createElement(i,{name:s.icon}):void 0,s.text,r.createElement(\"span\",{className:\"caret\"})),r.createElement(\"ul\",{style:{display:!a&&e.state.hover?\"block\":\"none\",padding:t?void 0:0},className:\"dropdown-menu\"},n.map(function(t){return r.createElement(\"li\",{key:t.value,title:t.text,\"data-value\":t.value,onClick:function(){e.onMouseLeave(),t!==s&&e.onChange(t)}},t.icon?r.createElement(i,{name:t.icon}):void 0,t.text)}),t?r.createElement(\"li\",{role:\"separator\",className:\"divider\"}):void 0,t?r.createElement(\"li\",{style:{padding:0}},r.createElement(\"a\",{href:t,target:\"_blank\"},r.createElement(i,{name:\"question-sign\"}),\"Help\")):void 0))}});e.exports=a},function(e,t,n){var r=n(567);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-dropdown-text{max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.w-dropdown .dropdown-menu .divider{margin:0!important}.w-dropdown .dropdown-menu{width:auto!important;max-width:300px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:auto!important;display:none;max-height:360px;overflow-x:hidden;overflow-y:auto}.w-dropdown{display:inline-block}.w-dropdown .dropdown-menu li{padding:0 10px;cursor:pointer;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.w-dropdown .dropdown-menu li:hover{background:var(--b-hover)}.w-dropdown .dropdown-menu a{padding:0 10px;line-height:20px;display:block}\",\"\"])},function(e,t,n){\"use strict\";var r=n(199),o=n(24),i=n(569),a=n(213);n(573);var s={name:\"Pause Record\",icon:\"minus-sign\",id:\"pause\"},l={name:\"Stop Record\",icon:\"stop\",id:\"stop\"},c=[s,{name:\"Scroll To Top\",icon:\"arrow-up\",id:\"top\"},{name:\"Scroll To Bottom\",icon:\"arrow-down\",id:\"bottom\"}],u=o.createClass({displayName:\"RecordBtn\",getInitialState:function(){return{stop:!1}},onClick:function(){var e=!this.state.stop;this.state.pause=!1,this.state.stop=e,c[0]=s,this.props.onClick(e?\"stop\":\"refresh\"),this.setState({})},componentDidMount:function(){r.on(\"toggleNetworkState\",this.onClick)},enable:function(e){var t=this.state,n=t.pause,r=t.stop;if(\"stop\"===e){if(r&&!n)return}else{if(\"pause\"!==e)return void((r||n)&&this.onClick());if(n)return}this.onClickOption({id:e})},showActionOptions:function(){this.setState({showActionOptions:!0})},hideActionOptions:function(){this.setState({showActionOptions:!1})},onClickOption:function(e){\"pause\"===e.id?(c[0]=l,this.state.pause=!0,this.state.stop=!0):\"stop\"===e.id&&(c[0]=s,this.state.pause=!1,this.state.stop=!0),this.props.onClick(e.id),this.hideActionOptions()},render:function(){var e=this.state,t=this.props.hide,n=e.pause,r=e.stop,s=r||n,l=\"Click to \"+(s?\"start\":\"stop\")+\" record\";return o.createElement(\"div\",{onMouseEnter:this.showActionOptions,onMouseLeave:this.hideActionOptions,className:\"w-menu-wrapper w-switch-btn w-menu-auto\"+(e.showActionOptions?\" w-menu-wrapper-show\":\"\")+(t?\" hide\":\"\")},o.createElement(\"a\",{onClick:this.onClick,draggable:\"false\",className:\"w-scroll-menu\"+(s?\" w-pause\":\"\")+(this.props.disabledRecord?\" w-disabled\":\"\"),title:l},o.createElement(a,{name:n?\"minus-sign\":\"stop\"}),\"Record\"),o.createElement(i,{options:c,className:\"w-remove-menu-item\",onClickOption:this.onClickOption}))}});e.exports=u},function(e,t,n){\"use strict\";n(570);var r=n(24),o=n(202),i=n(572),a=n(213),s=r.createClass({displayName:\"MenuItem\",preventBlur:function(e){e.preventDefault()},stopPropagation:function(e){e.stopPropagation()},render:function(){var e=this,t=e.props.options;t&&!t.length&&(t=null);var n=e.props.name,s=e.props.onClick||o.noop,l=e.props.onClickOption||o.noop,c=e.props.onDoubleClickOption||o.noop,u=e.props.checkedOptions||{},d=e.props.disabled;return r.createElement(\"div\",{onBlur:e.props.onBlur,tabIndex:\"0\",onMouseDown:e.preventBlur,style:{display:o.getBool(e.props.hide)?\"none\":\"block\"},className:\"w-menu-item \"+(e.props.className||\"\")+(d?\" w-disabled\":\"\")},t?r.createElement(\"div\",{className:\"w-menu-options\",style:{border:n?null:\"none\"}},t.map(function(t){return r.createElement(\"a\",{key:t.name,className:t.disabled?\"w-disabled\":void 0,title:t.title,onClick:function(e){t.disabled||l(t,e)},onDoubleClick:function(){c(t)},href:t.href||void 0,target:t.href?t.target||\"_blank\":void 0,draggable:\"false\"},\"checkbox\"==t.icon?r.createElement(\"input\",{type:\"checkbox\",disabled:d,\"data-name\":t.name,onClick:e.stopPropagation,onChange:e.props.onChange,checked:!u[t.name]}):t.icon===!1?void 0:\"github\"===t.icon?r.createElement(i,null):r.createElement(a,{name:t.icon||\"asterisk\",className:t.icon?\"\":\"w-hidden\"}),t.name)})):\"\",n?\"string\"==typeof n?r.createElement(\"a\",{onClick:s,className:\"w-menu-open\",draggable:\"false\"},r.createElement(a,{name:e.props.icon||\"folder-open\"}),n):n:\"\")}});e.exports=s},function(e,t,n){var r=n(571);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-menu-item{position:absolute;background:var(--b-default);border:1px solid var(--c-border);z-index:var(--z-menu);top:31px;border-radius:2px;outline:0}.w-menu-item .glyphicon-cloud{vertical-align:text-bottom}.w-menu-item a{display:block;max-width:10pc;text-overflow:ellipsis;overflow:hidden;padding:0 6px;font-weight:400;white-space:nowrap;margin:0!important}.w-menu-auto .w-menu-item a{max-width:inherit}.w-menu-item .w-menu-options{border-bottom:1px dashed var(--c-border);max-height:500px;overflow-x:hidden;overflow-y:auto}.w-menu-item a .glyphicon,.w-menu-item input{margin-right:8px;font-size:9pt;vertical-align:text-bottom}.w-menu-item a .glyphicon{vertical-align:initial}.w-menu-item a .glyphicon-ok,.w-menu-item a .glyphicon-plus{color:var(--c-ok)}.w-create-menu-item a .glyphicon-plus{color:var(--c-default)}.w-network-menu-item .w-menu-options a{max-width:20pc}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o={\"margin-right\":\"5px\",\"margin-left\":\"-2px\",\"vertical-align\":\"text-bottom\"},i=r.createClass({displayName:\"GitHub\",render:function(){return r.createElement(\"svg\",{viewBox:\"0 0 30 30\",width:\"17px\",height:\"17px\",style:o,fill:\"currentColor\"},r.createElement(\"path\",{d:\"M15,3C8.373,3,3,8.373,3,15c0,5.623,3.872,10.328,9.092,11.63C12.036,26.468,12,26.28,12,26.047v-2.051 c-0.487,0-1.303,0-1.508,0c-0.821,0-1.551-0.353-1.905-1.009c-0.393-0.729-0.461-1.844-1.435-2.526 c-0.289-0.227-0.069-0.486,0.264-0.451c0.615,0.174,1.125,0.596,1.605,1.222c0.478,0.627,0.703,0.769,1.596,0.769 c0.433,0,1.081-0.025,1.691-0.121c0.328-0.833,0.895-1.6,1.588-1.962c-3.996-0.411-5.903-2.399-5.903-5.098 c0-1.162,0.495-2.286,1.336-3.233C9.053,10.647,8.706,8.73,9.435,8c1.798,0,2.885,1.166,3.146,1.481C13.477,9.174,14.461,9,15.495,9 c1.036,0,2.024,0.174,2.922,0.483C18.675,9.17,19.763,8,21.565,8c0.732,0.731,0.381,2.656,0.102,3.594 c0.836,0.945,1.328,2.066,1.328,3.226c0,2.697-1.904,4.684-5.894,5.097C18.199,20.49,19,22.1,19,23.313v2.734 c0,0.104-0.023,0.179-0.035,0.268C23.641,24.676,27,20.236,27,15C27,8.373,21.627,3,15,3z\"\n}))}});e.exports=i},function(e,t,n){var r=n(574);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-switch-btn .glyphicon-stop{color:var(--c-risk)}.w-switch-btn .w-disabled .glyphicon-minus-sign,.w-switch-btn .w-disabled .glyphicon-stop,.w-switch-btn .w-menu-item .glyphicon-minus-sign,.w-switch-btn .w-menu-item .glyphicon-stop,.w-switch-btn .w-pause .glyphicon-minus-sign,.w-switch-btn .w-pause .glyphicon-stop,.w-switch-btn .w-pause.glyphicon-stop{color:var(--c-disabled)!important}\",\"\"])},function(e,t,n){\"use strict\";function r(e){return e.active}var o=n(24),i=n(57),a=n(18),s=n(527),l=n(538),c=n(543),u=n(576),d=n(202),p=n(337),h=o.createClass({displayName:\"FrameClient\",getInitialState:function(){return{btns:[{name:\"Overview\"},{name:\"TextView\"},{name:\"JSONView\"},{name:\"HexView\"},{name:\"Composer\",icon:\"send\"}]}},showTab:function(e){var t=this.state.btns;t.forEach(function(e){e.active=!1}),this.selectBtn(t[e]),this.setState({})},componentDidMount:function(){var e=this,t=e.state.btns,n=e.props.framesCtx;n.on(\"composeFrame\",function(t,n){n&&(e.showTab(4),setTimeout(function(){d.shakeElem(a(i.findDOMNode(e.refs.tabs)).find('button[data-name=\"Composer\"]'))},100))}),n.on(\"toggleFramesInspectors\",function(){var n=e.state.btn;t.forEach(function(e){e.active=!1}),n&&n!==t[0]?n===t[1]?e.onClickBtn(t[2]):n===t[2]?e.onClickBtn(t[3]):e.onClickBtn(t[0]):e.onClickBtn(t[1])})},onDragEnter:function(e){-1!=e.dataTransfer.types.indexOf(\"framedataid\")&&(this.showTab(4),e.preventDefault())},onDrop:function(e){var t=e.dataTransfer.getData(\"frameDataId\");t&&this.props.framesCtx.trigger(\"composeFrameId\",t)},onClickBtn:function(e){this.selectBtn(e),this.setState({})},selectBtn:function(e){e.active=!0,this.state.btn=e},render:function(){var e=this.state,t=this.state.btns,n=e.btn;-1===t.indexOf(n)&&(n=d.findArray(t,r)||t[0],this.selectBtn(n));var i,a,h,g,f,m=this.props.frame;if(m){if(m.closed)f={Date:d.toLocaleString(new Date(parseInt(m.frameId,10)))};else{var A=m.length;f={Date:d.toLocaleString(new Date(parseInt(m.frameId,10))),Path:m.isClient?\"Client -> Server\":\"Server -> Client\",Opcode:m.opcode,Type:1==m.opcode?\"Text\":\"Binary\",Compressed:m.compressed?\"Yes\":\"No\",Mask:m.mask?\"Yes\":\"No\",Length:A>=0?d.formatSize(A,m.unzipLen):\"\"}}i=d.getBody(m,!0),h=d.getHex(m),a=d.getJson(m,!0),g=m.base64}return g=g||\"\",o.createElement(\"div\",{className:\"fill v-box w-frames-data\"+(this.props.hide?\" hide\":\"\"),onDragEnter:this.onDragEnter,onDrop:this.onDrop},o.createElement(s,{ref:\"tabs\",onClick:this.onClickBtn,btns:e.btns}),o.createElement(p,{modal:f,hide:\"Overview\"!==n.name}),o.createElement(c,{className:\"fill\",base64:g,value:i,hide:\"TextView\"!==n.name}),o.createElement(l,{data:a,hide:\"JSONView\"!==n.name}),o.createElement(c,{className:\"fill n-monospace\",isHexView:\"1\",base64:g,value:h,hide:\"HexView\"!==n.name}),o.createElement(u,{framesCtx:this.props.framesCtx,data:this.props.data,hide:\"Composer\"!==n.name}))}});e.exports=h},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(200),a=n(202),s=n(206),l=n(210),c=n(209),u=n(213),d=1049600,p=65536,h=o.findDOMNode,g=r.createClass({displayName:\"FrameComposer\",getInitialState:function(){return{isHexText:!!l.get(\"showHexTextFrame\"),isCRLF:!!l.get(\"useCRLFrame\")}},componentDidMount:function(){var e=this,t=this.props.framesCtx;e.dataField=h(e.refs.uploadData),e.dataForm=h(e.refs.uploadDataForm),t.on(\"composeFrame\",function(t,n){if(n){var r;r=e.state.isHexText?a.getHexText(a.getHex(n)):a.getBody(n,!0),e.setTextarea(r),setTimeout(function(){h(e.refs.textarea).focus()},60)}}),t.on(\"replayFrame\",function(n,r){r&&(t.trigger(\"enableRecordFrame\"),e.send({target:r.isClient?\"server\":\"client\",type:1==r.opcode?\"text\":\"bin\",base64:r.base64},function(){t.trigger(\"autoRefreshFrames\")}))});var n=l.get(\"composeFrameData\");this.setTextarea(String(n||\"\"))},shouldComponentUpdate:a.shouldComponentUpdate,uploadTextToServer:function(){this.target=\"server\",this.dataType=\"text\",this.dataField.click()},uploadBinToServer:function(){this.target=\"server\",this.dataType=\"bin\",this.dataField.click()},uploadTextToClient:function(){this.target=\"client\",this.dataType=\"text\",this.dataField.click()},uploadBinToClient:function(){this.target=\"client\",this.dataType=\"bin\",this.dataField.click()},onFormChange:function(){this.uploadForm(new FormData(this.dataForm)),this.dataField.value=\"\"},uploadForm:function(e){var t=e.get(\"uploadData\");if(t.size>d)return c.alert(\"Maximum file size: 1MB\");var n=this,r={target:n.target,type:n.dataType};a.readFileAsBase64(t,function(e){r.base64=e,n.send(r),n.dataField.value=\"\"})},send:function(e,t){var n=this.props.data;n&&(e.reqId=n.id,this.props.framesCtx.trigger(\"enableRecordFrame\"),i.socket.send(e,function(e,n){return e?0!==e.ec?s.error(\"Server temporarily unavailable. Please try again shortly\"):void(t&&t()):a.showSystemError(n)}))},onSend:function(e){var t=this.state.text,n=this;if(t&&!n.sendTimer){var r,o=e.target;this.state.isHexText?(r=a.getBase64FromHexText(t),t=void 0):this.state.isCRLF&&(t=t.replace(/\\r\\n|\\r|\\n/g,\"\\r\\n\"));var i={type:\"A\"===o.nodeName?\"bin\":\"text\",target:o.getAttribute(\"data-target\")?\"server\":\"client\",text:t,base64:r};n.setState({}),n.sendTimer=setTimeout(function(){n.sendTimer=null,n.setState({})},5e3),n.send(i,function(){clearTimeout(n.sendTimer),n.sendTimer=null,n.props.framesCtx.trigger(\"autoRefreshFrames\"),n.setState({})})}},format:function(){var e=a.parseRawJson(this.state.text);e&&this.setState({text:JSON.stringify(e,null,\"  \")})},onForamt:function(e){a.handleFormat(e,this.format),a.handleTab(e)},setTextarea:function(e){this.setState({text:e}),clearTimeout(this.timer),this.timer=setTimeout(function(){l.set(\"composeFrameData\",e)},600)},onTextareaChange:function(e){this.setTextarea(e.target.value)},preventDefault:function(e){e.preventDefault()},onTypeChange:function(e){var t=e.target.checked;l.set(\"showHexTextFrame\",t?1:\"\"),this.setState({isHexText:t})},onCRLFChange:function(e){var t=e.target.checked;l.set(\"useCRLFrame\",t?1:\"\"),this.setState({isCRLF:t})},render:function(){var e=this.props.data||\"\";a.socketIsClosed(e);var t=this.state,n=t.text||\"\",o=t.isHexText,i=t.isCRLF,s=e.closed,l=e.isHttps,c=l?{left:0}:void 0,d=l?{display:\"none\"}:void 0,h=s?\"The connection is closed\":void 0,g=s||this.sendTimer;return r.createElement(\"div\",{onDrop:this.onDrop,className:\"fill v-box w-frames-com\"+(this.props.hide?\" hide\":\"\")},r.createElement(\"div\",{className:\"w-frames-com-action\"},r.createElement(\"label\",{className:\"w-frames-hex-data\"+(o?\" w-frames-checked\":\"\")},r.createElement(\"input\",{checked:o,onChange:this.onTypeChange,type:\"checkbox\"}),\"HexText\"),r.createElement(\"label\",{className:\"w-frames-crlf\"+(o?\" hide\":\"\")+(i?\" w-frames-checked\":\"\")},r.createElement(\"input\",{checked:i,onChangeCapture:this.onCRLFChange,type:\"checkbox\"}),\"\\\\r\\\\n\"),r.createElement(\"div\",{className:\"btn-group\"},r.createElement(\"button\",{disabled:g,title:h,onMouseDown:this.preventDefault,onClick:this.onSend,type:\"button\",className:\"btn btn-default btn-sm\"},r.createElement(u,{name:\"arrow-left\"}),\"Send To Client\"),r.createElement(\"button\",{disabled:g,title:h,type:\"button\",className:\"btn btn-default dropdown-toggle\",\"data-toggle\":\"dropdown\",\"aria-haspopup\":\"true\",\"aria-expanded\":\"false\"},r.createElement(\"span\",{className:\"caret\"})),r.createElement(\"ul\",{style:c,className:\"dropdown-menu\"+(s?\" hide\":\"\")},r.createElement(\"li\",{style:d},r.createElement(\"a\",{onClick:this.onSend},\"Send Binary Data\")),r.createElement(\"li\",null,r.createElement(\"a\",{onClick:this.uploadTextToClient},l?\"Upload \":\"Upload Text Data\")),r.createElement(\"li\",{style:d},r.createElement(\"a\",{onClick:this.uploadBinToClient},\"Upload Binary Data\")))),r.createElement(\"div\",{className:\"btn-group\"},r.createElement(\"button\",{disabled:g,title:h,onMouseDown:this.preventDefault,\"data-target\":\"server\",onClick:this.onSend,type:\"button\",className:\"btn btn-default btn-sm\"},r.createElement(u,{name:\"arrow-right\"}),\"Send To Server\"),r.createElement(\"button\",{disabled:g,title:h,type:\"button\",className:\"btn btn-default dropdown-toggle\",\"data-toggle\":\"dropdown\",\"aria-haspopup\":\"true\",\"aria-expanded\":\"false\"},r.createElement(\"span\",{className:\"caret\"})),r.createElement(\"ul\",{style:c,className:\"dropdown-menu\"+(s?\" hide\":\"\")},r.createElement(\"li\",{style:d},r.createElement(\"a\",{\"data-target\":\"server\",onClick:this.onSend},\"Send Binary Data\")),r.createElement(\"li\",null,r.createElement(\"a\",{onClick:this.uploadTextToServer},l?\"Upload \":\"Upload Text Data\")),r.createElement(\"li\",{style:d},r.createElement(\"a\",{onClick:this.uploadBinToServer},\"Upload Binary Data\")))),r.createElement(\"button\",{type:\"button\",title:\"Format\",onClick:this.format,className:\"btn btn-default w-format-json-btn\"},\"Format\")),r.createElement(\"textarea\",{ref:\"textarea\",style:{fontFamily:o?\"monospace\":void 0},maxLength:p,value:n,onKeyDown:this.onForamt,onChange:this.onTextareaChange,placeholder:\"Enter \"+(o?\"hex \":\"\")+\"text\",className:\"fill\"}),r.createElement(\"form\",{ref:\"uploadDataForm\",method:\"post\",encType:\"multipart/form-data\",style:{display:\"none\"}},r.createElement(\"input\",{ref:\"uploadData\",onChange:this.onFormChange,type:\"file\",name:\"uploadData\"})))}});e.exports=g},function(e,t,n){\"use strict\";function r(){this.list=[]}function o(e){for(var t=0,n=e.length;n>t;t++){var r=e[t];if(r.active)return r}}function i(e,t){var n=o(e);e.splice(0,t),n&&-1===e.indexOf(n)&&(e[0]=n)}var a=n(202),s=n(200).MAX_FRAMES_LENGTH,l=r.prototype;l.getItem=function(e){if(e)for(var t=0,n=this.list.length;n>t;t++){var r=this.list[t];if(r.frameId===e)return r}};var c=/^(c|s):(\\S*)$/i;l.search=function(e){if(e=\"string\"!=typeof e?\"\":e.trim(),!e)return void(this._keyword=\"\");var t=this._keyword={};e.split(/\\s+/).forEach(function(e){var n=\"\",r=\"!\"===e[0];r&&(e=e.substring(1).trim()),c.test(e)&&(n=RegExp.$1.toLowerCase(),e=RegExp.$2,\"!\"===e[0]&&(r=!0,e=e.substring(1)));var o=t[n]||(t[n]=[]);e&&o.length<3&&o.push({not:r,keyword:e.toLowerCase(),regexp:a.toRegExp(e)})})},l.filter=function(){var e=this._keyword,t=this.list;if(!e)return void t.forEach(function(e){e.hide=!1});var n,r=function(t,r){var o=e[r||\"\"];if(!o)return!1;if(\"s\"===r){if(t.isClient)return!0}else if(\"c\"===r&&!t.isClient)return!0;if(0===o.length)return!1;for(var i=0,s=o.length;s>i;i++){var l=o[i];null==n&&(n=a.getBody(t,!0).toLowerCase());var c=l.regexp?l.regexp.test(n):-1!==n.indexOf(l.keyword);if(l.not?!c:c)return!1}return!0};t.forEach(function(e){n=null,e.hide=r(e)||r(e,\"s\")||r(e,\"c\")})},l.setActive=function(e,t){this.list.forEach(function(e){e.active=!1}),e.active=t!==!1},l.getActive=function(){return o(this.list)},l.getList=function(){return this.filter(),this.list},l.update=function(){var e=this.list,t=e.length,n=t-s;if(n>0){if(this._keyword)for(var r=0;t>r&&n>0;){var o=e[r];o.hide&&!o.active?(--t,--n,e.splice(r,1)):++r}n>0&&i(e,n)}},l.clear=function(){return this.list.splice(0,this.list.length),this},l.reset=function(e){return e&&this.list!==e?(this.list=e,this.filter(),e):e},e.exports=r},function(e,t,n){\"use strict\";var r=n(24),o=r.createClass({displayName:\"LazyInit\",render:function(){return this.props.inited||this._inited?(this._inited=!0,this.props.children):null}});e.exports=o},function(e,t,n){\"use strict\";n(580);var r=n(24),o=n(202),i=82,a=r.createClass({displayName:\"Timeline\",shouldComponentUpdate:o.shouldComponentUpdate,render:function(){var e,t=this.props.modal,n=this.props.data,a=n?[n]:t?t.getSelectedList():[],s=1;a.forEach(function(t){(!e||t.startTime<e)&&(e=t.startTime)}),a.forEach(function(t){var n=(t.endTime||Math.max(t.responseTime||0,t.requestTime||0)||t.dnsTime)-e;n>s&&(s=n)});var l=a.length;return r.createElement(\"div\",{className:\"fill v-box w-detail-ctn w-timeline\"+(o.getBool(this.props.hide)?\" hide\":\"\")},r.createElement(\"ul\",null,a.map(function(t){var n,a,c,u,d,p,h,g,f,m,A,M=t.startTime-e;t.dnsTime?(M=t.startTime-e,n=M*i/s+\"%\",M+=\"ms\"):(M=\"-\",n=0),t.ttfb>=0?(a=t.ttfb,c=a*i/s+\"%\",a+=\"ms\"):a=\"-\",t.dnsTime?(u=t.dnsTime-t.startTime,d=u*i/s+\"%\",u+=\"ms\"):(u=\"-\",d=0);var w;if(t.responseTime?(w=!t.requestTime||t.requestTime>t.responseTime,g=t.responseTime-(w?t.dnsTime:t.requestTime),f=g*i/s+\"%\",g+=\"ms\"):(g=\"-\",f=0),t.requestTime){p=t.requestTime-t.dnsTime,h=p*i/s+\"%\",p+=\"ms\";var v=t.protocol;if(\"string\"==typeof v&&-1!==v.indexOf(\">\")){var b=t.httpsTime-t.startTime;b>0&&(p+=\" - \"+b+\"ms(\"+v+\") = \"+(t.requestTime-t.httpsTime)+\"ms\")}}else p=\"-\",h=0;t.endTime?(m=t.endTime-t.responseTime,A=m*i/s+\"%\",m+=\"ms\"):(m=\"-\",A=0);var y=t.endTime?t.endTime-t.startTime+\"ms\":\"-\";if(1===l)return r.createElement(\"li\",{key:\"w-timeline-one\",className:\"w-timeline-one\"},r.createElement(\"ul\",null,r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"URL:\"),r.createElement(\"span\",{className:\"w-timeline-full-url\",title:t.url},t.url)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"TTFB:\"),r.createElement(\"span\",{style:{width:c},className:\"w-timeline-ttfb\"}),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},a)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"DNS:\"),r.createElement(\"span\",{style:{width:d},className:\"w-timeline-dns\"}),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},u)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"Request:\"),r.createElement(\"span\",{style:{width:d}}),r.createElement(\"span\",{style:{width:h},className:\"w-timeline-request\"},\" \"),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},p)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"Response:\"),r.createElement(\"span\",{style:{width:d}}),w?null:r.createElement(\"span\",{style:{width:h}}),r.createElement(\"span\",{style:{width:f},className:\"w-timeline-response\"}),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},g)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"Download:\"),r.createElement(\"span\",{style:{width:d}}),w?null:r.createElement(\"span\",{style:{width:h}}),r.createElement(\"span\",{style:{width:f}}),r.createElement(\"span\",{style:{width:A},className:\"w-timeline-load\"}),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},m)),r.createElement(\"li\",null,r.createElement(\"span\",{className:\"w-timeline-url\"},\"Total Duration:\"),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},y))));var x=\"URL: \"+t.url+\"\\nStalled: \"+M+\"\\nTFFB: \"+a+\"\\nDNS: \"+u+\"\\nRequest: \"+p+\"\\nResponse: \"+g+\"\\nContent: \"+m+\"\\nTotal: \"+y,T=w&&h,C=w&&f;return r.createElement(\"li\",{key:t.id,title:x,className:\"w-timeline-multi\"+(C?\" w-timeline-stream\":\"\")},r.createElement(\"span\",{title:t.url,className:\"w-timeline-url\"},o.getFilename(t)),r.createElement(\"span\",{style:{width:n},className:\"w-timeline-stalled\"}),r.createElement(\"span\",{style:{width:d},className:\"w-timeline-dns\"}),r.createElement(\"span\",{style:{width:h,marginBottom:C?\"5px\":void 0},className:\"w-timeline-request\"}),r.createElement(\"span\",{style:{width:f,marginLeft:T?\"-\"+h:void 0,marginBottom:T?\"-5px\":void 0,height:T?\"15px\":void 0},className:\"w-timeline-response\"}),r.createElement(\"span\",{style:{width:A,marginLeft:C?\"-\"+f:void 0,marginBottom:T?\"-5px\":void 0,height:T?\"15px\":void 0},className:\"w-timeline-load\"}),r.createElement(\"span\",{title:x,className:\"w-timeline-time\"},y))})))}});e.exports=a},function(e,t,n){var r=n(581);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-timeline li,.w-timeline ul{list-style:none;margin:0;padding:0;white-space:nowrap;font-size:0;width:100%;display:block}.w-timeline li{padding:2px 0;position:relative;white-space:nowrap;margin-bottom:9pt}.w-timeline span{display:inline-block;font-size:9pt;height:20px;line-height:20px;overflow:hidden;text-overflow:ellipsis;cursor:default}.w-timeline-url{padding-right:5px;width:18%;max-width:200px;text-align:right}.w-timeline-stalled{background:var(--c-disabled)}.w-timeline-ttfb{background:var(--b-active)}.w-timeline-dns{background:var(--b-tl-dns)}.w-timeline-request{background:var(--b-tl-req)}.w-timeline-response{background:var(--b-tl-res)}.w-timeline-load{background:var(--b-tl-load)}.w-timeline-multi:hover,.w-timeline-one>ul>li:hover{background:var(--b-hover)}.w-timeline-time{position:absolute;top:2px;right:0;padding-right:5px}.w-timeline-stream{padding-bottom:5px!important}.w-timeline-one .w-timeline-url{text-align:right;padding-right:10px;font-weight:700}.w-timeline-one .w-timeline-time{left:18%!important}.w-timeline-full-url{width:82%}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(583),i=n(202),a=n(578),s=n(200),l=n(199),c=n(547),u=n(210),d=n(213),p=\" \",h=r.createClass({displayName:\"ComposerList\",getInitialState:function(){return{activeName:u.get(\"activeComposerTab\")||p}},shouldComponentUpdate:i.shouldComponentUpdate,componentDidMount:function(){var e=this;l.on(\"comTabsChange\",function(){e.setState({})}),l.on(\"_setComposerData\",function(){e.showTab(p)})},showTab:function(e){this.state.activeName!==e&&(u.set(\"activeComposerTab\",e),this.setState({activeName:e}))},render:function(){var e=this,t=e.props.hide,n=e.props.modal,l=s.getComTabs(),u=e.state.activeName,h=u===p,g=h,f=l.map(function(t){var n,o=t.plugin;u==t.plugin&&(n=!0,g=!0);var a=i.getTabIcon(t);return r.createElement(\"button\",{key:o,onClick:function(){e.showTab(o)},className:\"w-custom-tab-btn btn btn-default\"+(n?\" active\":\"\"),title:o},a?r.createElement(\"img\",{className:\"w-tab-icon\",src:a}):null,t.name)});return g||(h=!0,u=e.state.activeName=p),r.createElement(\"div\",{className:\"fill v-box w-com-list\"+(t?\" hide\":\"\")},l.length?r.createElement(\"div\",{className:\"box w-com-tab-list\"},r.createElement(\"button\",{type:\"button\",onClick:function(){e.showTab(p)},className:\"btn btn-default\"+(h?\" active\":\"\")},r.createElement(d,{name:\"send\"}),\"Default\"),r.createElement(\"div\",{className:\"fill w-custom-tabs\"},f)):null,r.createElement(a,{inited:!t},r.createElement(o,{modal:n,disabled:!h,hide:t||!h})),r.createElement(c,{modal:n,active:u,hide:t,tabs:l,className:\"w-custom-tab-panel\"}))}});e.exports=h},function(e,t,n){\"use strict\";function r(e,t){return\"string\"!=typeof e?\"\":(t=t||K,e.length>t?e.substring(0,t):e)}function o(e,t,n){return\"CONNECT\"===e||v.hasRequestBody(e)||W.test(t)?!0:n&&X.test(n)&&_.test(n)}function i(e){return e=v.getBase64FromHexText(e),v.base64Decode(e)}function a(e){var t=v.toBase64(e);return v.getHexText(v.getHexFromBase64(t))}function s(e){for(var t,n=Object.keys(e),r=0,o=n.length;o>r;r++){var i=n[r];if(\"content-type\"===i.toLowerCase()){if(t)return\"custom\";var a=e[i];if(!a||\"string\"!=typeof a)return\"custom\";a=a.split(\";\")[0].trim().toLowerCase(),t=q[a]||\"custom\"}}return t||\"custom\"}function l(e){return\"upload\"===e||\"upload\"===s(e)}function c(e){return e=e.join(\"\\n\"),e&&encodeURIComponent(e)}function u(e,t){return e&&(e=(e+\"\").replace(/;?\\s*boundary=.*$/,\"\")),(e||\"multipart/form-data\")+\"; boundary=\"+t}function d(e){return 403==e?\"forbidden\":e&&(!/^\\d+$/.test(e)||e>=400)?\"error\":\"\"}function p(e){e&&\"object\"===(\"undefined\"==typeof e?\"undefined\":f(e))&&(e.data=v.base64ToByteArray(e.base64)||v.EMPTY_BUF,delete e.base64)}function h(e){return e&&e.replace(/\\r\\n|\\r|\\n/g,\"\\r\\n\")}function g(e){if(e&&e.endTime){var t=e.endTime-e.startTime;return t>=1e3?t/1e3+\"s\":t+\"ms\"}}var f=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};n(584);var m=n(24),A=n(57),M=n(18),w=n(200),v=n(202),b=n(199),y=n(210),x=n(220),T=n(552),C=n(337),N=n(586),I=n(206),E=n(293),D=n(589),S=n(198),L=n(209),k=n(590),j=n(578),U=n(561),B=n(213),R=n(214),z=n(591),Q=[\"GET\",\"POST\",\"PUT\",\"HEAD\",\"TRACE\",\"DELETE\",\"SEARCH\",\"QUERY\",\"CONNECT\",\"UPGRADE\",\"WEBSOCKET\",\"PROPFIND\",\"PROPPATCH\",\"MKCOL\",\"COPY\",\"MOVE\",\"LOCK\",\"UNLOCK\",\"OPTIONS\",\"PURGE\",\"ACL\",\"BIND\",\"CHECKOUT\",\"LINK\",\"M-SEARCH\",\"MERGE\",\"MKACTIVITY\",\"MKCALENDAR\",\"NOTIFY\",\"PATCH\",\"PRI\",\"REBIND\",\"REPORT\",\"SOURCE\",\"SUBSCRIBE\",\"UNBIND\",\"UNLINK\",\"UNSUBSCRIBE\"],O=[{name:\"Send Body Via File\",action:\"file\"},{name:\"Replay Times\"},{name:\"Show History\",action:\"history\"},{name:\"Service\",list:[{name:\"Create API Test\",action:\"createApiTest\"},{name:\"Copy As Script\",action:\"copyAsScript\"},{name:\"Share Via URL\",action:\"Export\"}]}],H=A.findDOMNode,F=20971520,V=3145728,P={form:\"application/x-www-form-urlencoded\",upload:\"multipart/form-data\",text:\"text/plain\",json:\"application/json\",custom:\"\"},Y=\"Requests cannot bring rules in strict mode\",G=/;.+$/,W=/^wss?:\\/\\//i,X=/^\\s*connection\\s*:\\s*upgrade\\s*$/im,_=/^\\s*upgrade\\s*:\\s*websocket\\s*$/im,q={},J=131072,K=262144,Z=2*K,$=64,ee=100,te=\"x-whistle-rule-value\";Object.keys(P).forEach(function(e){q[P[e]]=e});var ne=function(e){return e?\"w-tab-btn w-active\":\"w-tab-btn\"},re=m.createClass({displayName:\"Composer\",getInitialState:function(){var e=y.get(\"composerRules\"),t=v.parseJSON(y.get(\"composerData\"))||{},n=\"1\"==y.get(\"showPretty\"),o=\"1\"==y.get(\"useH2InComposer\"),i=\"1\"==y.get(\"disableComposerRules\"),a=t.method,c=r(t.body);c&&c!==t.body&&I.warn(\"Body content limited to 256KB (excess will be truncated)\");var u=v.parseHeaders(t.headers),d=s(u);if(t.base64&&l(d))this.uploadBodyData=t.base64&&this.parseUploadModal({headers:u,base64:t.base64});else{var h=v.parseRawJson(y.get(\"composerUploadBody\"),!0);h&&(Object.keys(h).forEach(function(e){var t=h[e];Array.isArray(t)?t.forEach(p):p(t)}),this.uploadBodyData=h)}return{loading:!0,repeatTimes:1,historyData:[],disableBody:!!y.get(\"disableComposerBody\"),enableProxyRules:\"\"!==y.get(\"composerProxyRules\"),url:t.url,method:-1===Q.indexOf(a)?\"GET\":a,headers:r(t.headers,J),body:c,tabName:\"Request\",showPretty:n,useH2:o,rules:\"string\"==typeof e?e:\"\",type:d,disableComposerRules:i,isHexText:!!y.get(\"showHexTextBody\"),isCRLF:!!y.get(\"useCRLBody\")}},componentDidMount:function(){var e=this;e.update(e.props.modal),this.refs.uploadBody.update(this.uploadBodyData),this.hintElem=M(H(this.refs.hints)),w.onTakeTimeChange=function(t){e.setState({composerTime:t})},b.on(\"_setComposerData\",function(t,n){n&&(b.trigger(\"showComposerTab\"),e.onCompose(n))}),b.on(\"setComposer\",function(){if(!e.state.pending&&!e.props.disabled){var t=e.props.modal;if(t){e.setState({reqData:t}),t.frames&&w.setComposerItem(t);var n=v.getBody(t.req),r=function(){var n=t.req.headers;t.h2Id&&(n=M.extend({},n),n[\"x-whistle-alpn-protocol\"]=t.h2Id,t=M.extend({},t),t.req=M.extend({},t.req),t.req.headers=n);var r={useH2:t.useH2,url:t.url,headers:n,result:t,type:s(n),method:t.req.method,tabName:\"Request\"},o=v.getBody(t.req);o&&(r.disableBody=!1,-1!==o.indexOf(\"\\n\")&&(r.isCRLF=-1!==o.indexOf(\"\\r\\n\"))),y.set(\"useCRLBody\",r.isCRLF?1:\"\"),e.setState(r,function(){e.update(t),e.onComposerChange()}),y.set(\"useH2InComposer\",t.useH2?1:\"\")};n.length>K?L.confirm(\"Request body exceeds limit and will be truncated. Continue?\",function(e){e&&r()}):r()}}}),b.on(\"showFramesInComposer\",function(){e.setState({tabName:\"Frames\"})}),b.on(\"updateStrictMode\",function(){e.setState({})}),e.updatePrettyData(),M(document).on(\"click mousedown\",function(t){var n=M(t.target);n.closest(\".w-com-params\").length||n.closest(\".w-com-params-editor\").length||n.closest(\".w-com-dialog\").length||n.closest(\".w-win-dialog\").length||n.closest(\".w-ctx-menu\").length||e.hideParams(),n.closest(\".w-com-history-data\").length||n.closest(\".w-keep-history-data\").length||n.closest(\".w-replay-count-dialog\").length||n.closest(\".w-com-history-btn\").length||n.closest(\".w-copy-text-with-tips\").length||n.closest(\".w-ie-dialog\").length||n.closest(\".w-service-dialog\").length||e.hideHistory()}),b.trigger(\"composerDidMount\")},repeatTimesChange:function(e){var t=e.target.value.replace(/^\\s*0*|[^\\d]+/,\"\"),n=t.slice(0,3);n>ee&&(n=ee),this.setState({repeatTimes:n})},repeatRequest:function(e){e&&\"click\"!==e.type&&13!==e.keyCode||(this.refs.setRepeatTimes.hide(),this._isReplay?this.onReplay(this.state.repeatTimes):this.execute(null,this.state.repeatTimes))},loadHistory:function(){var e=this;2!==e.state.loading&&(e.state.loading=2,w.getHistory(function(t){return Array.isArray(t)?void e.setState({loading:0,historyData:e.formatHistory(t)}):void setTimeout(this.loadHistory,6e3)}))},getMethod:function(){var e=this.state.method||\"GET\",t=H(this.refs.method).value||e;return\"+ Custom\"===t?t:e},updatePrettyData:function(){if(this.state.showPretty){var e=H(this.refs.headers).value,t=v.parseHeaders(e);this.refs.prettyHeaders.update(t);var n=H(this.refs.body).value;n&&this.state.isHexText&&(n=i(n)),n=v.parseQueryString(n,null,null,decodeURIComponent),this.refs.prettyBody.update(n)}},update:function(e){if(e){var t=e.rulesHeaders,n=t&&t[te];n&&(t=M.extend({},t),delete t[te],this.updateRules(n));var o=this.refs,i=e.req;H(o.url).value=e.url,H(o.method).value=i.method,H(o.headers).value=v.getOriginalReqHeaders(e,t);var a=H(o.body);if(\"GET\"===i.method)a.value=\"\";else{var s=this.state.isHexText?v.getHexText(v.getHex(i)):v.getBody(i),l=r(s);a.value=l}this.updatePrettyData(),this.updateUploadForm(i)}},parseUploadModal:function(e){var t=v.parseUploadBody(e),n={};return t&&t.forEach(function(e){var t=e.name,r=n[t];r?r.push(e):n[t]=[e]}),n},updateUploadForm:function(e){return l(e.headers)?(this.refs.uploadBody.update(this.parseUploadModal(e)),!0):!1},shouldComponentUpdate:v.shouldComponentUpdate,getComposerData:function(){var e=this.refs,t=this.getMethod(),n=H(this.refs.url).value.trim(),r=H(this.refs.headers).value;return{url:n,headers:r,method:t,useH2:this.state.useH2?1:\"\",body:h(H(e.body).value)}},saveComposer:function(){var e=this.getComposerData();return this.state.url=e.url,this.state.headers=e.headers,y.set(\"composerData\",JSON.stringify(e)),this.hasBody!=o(e.method,e.url,e.headers)&&this.setState({}),e},addHistory:function(e){var t=this,n=t.state.historyData;e.date=Date.now();for(var r=0,o=n.length;o>r;r++){var i=n[r];if(i.url===e.url&&i.method===e.method&&i.headers===e.headers&&i.body===e.body){e.selected=i.selected,n.splice(r,1);break}}n.unshift(e);var a=n.length-$;a>0&&n.splice($,a),t.setState({historyData:t.formatHistory(n)})},formatHistory:function(e){var t,n=[],r=[],o=[],i={};return e.forEach(function(e){if(e.url&&\"string\"==typeof e.url){-1===r.indexOf(e.url)&&r.push(e.url);var a=v.parseUrl(e.url),s=a?a.host:\"\",l=i[s];l||(l={title:s,list:[]},o.push(l),i[s]=l),e.selected&&(t?e.selected=!1:t=!0),l.list.push(e),e.path=a?a.path:e.url,e.protocol=a?a.protocol.slice(0,-1):\"HTTP\",e.protocol=/^([\\w.-]+):\\/\\//i.test(e.url)?RegExp.$1.toUpperCase():\"HTTP\",e.body=e.body||\"\",n.push(e)}}),!t&&n[0]&&(n[0].selected=!0),n._groupList=o.reduce(function(e,t){return e.push({title:t.title}),t.list.forEach(function(t){e.push(t)}),e},[]),this._histroyUrls=r,this.state.showHints&&this.showHints(),n},onHexTextChange:function(e){var t=e.target.checked;y.set(\"showHexTextBody\",t?1:\"\");var n=H(this.refs.body),r=n.value;if(r.trim())if(t){var o=this.state.isCRLF;this._preBody===r&&!this._isCRLF==!o?n.value=this._preHex:n.value=a(o?h(r):r)}else this._preBody=i(r),this._isCRLF=this.state.isCRLF,this._preHex=r,n.value=this._preBody;this.setState({isHexText:t}),this.saveComposer()},onCRLFChange:function(e){var t=e.target.checked;y.set(\"useCRLBody\",t?1:\"\"),this.setState({isCRLF:t})},updateRules:function(e){Array.isArray(e)&&(e=e.join(\"\\n\")),e&&\"string\"==typeof e&&(e=v.decodeURIComponentSafe(e),H(this.refs.composerRules).value=e,this.setState({rules:e}),this.onRulesChange(),this.setRulesDisable(!1))},onCompose:function(e){if(e){this.state.tabName=\"Request\",this.result=null;var t=this.refs,n=!!e.isHexText,r=e.headers,o=[];v.notEStr(e.rules)&&o.push(e.rules);var i={headers:{},base64:e.base64};this.handleFrames(),v.notEStr(r)&&(r=r.trim().split(/[\\r\\n]+/).filter(function(e){e=e.trim();var t,n=e.indexOf(\":\"),r=e;return-1!==n&&(r=e.substring(0,n),t=e.substring(n+1).trim()),r=r.toLowerCase(),i.headers[r]=t,r===te?(t&&o.push(t),!1):!0}).join(\"\\r\\n\")),o.length&&this.updateRules(o.join(\"\\n\")),v.isString(e.url)&&(H(t.url).value=e.url,this.state.url=e.url),v.isString(e.method)&&(H(t.method).value=e.method,this.state.method=e.method),v.isString(r)&&(H(t.headers).value=r,this.state.headers=r),n||e.body||!e.base64||(n=!0);var a=n&&e.base64?v.getHexText(v.getHexFromBase64(e.base64)):v.getText(e.body)||\"\";this.state.tabName=\"Request\",this.state.result=\"\",this.state.isHexText=n,this.state.useH2=e.useH2,null!=e.disableBody?this.state.disableBody=!!e.disableBody:a&&(this.state.disableBody=!1),null!=e.isCRLF&&(this.state.isCRLF=!!e.isCRLF,y.set(\"useCRLBody\",e.isCRLF?1:\"\")),null!=e.disableComposerRules&&this.setRulesDisable(e.disableComposerRules),null!=e.enableProxyRules&&(this.state.enableProxyRules=!!e.enableProxyRules,y.set(\"composerProxyRules\",e.enableProxyRules?1:\"\")),this.updateUploadForm(i)||(H(t.body).value=a),this.onComposerChange(!0),y.set(\"disableComposerBody\",this.state.disableBody?1:\"\"),y.set(\"useH2InComposer\",e.useH2?1:\"\"),y.set(\"showHexTextBody\",n?1:\"\")}},onReplay:function(e){this._selectedItem&&this.sendRequest(M.extend({},this._selectedItem,{repeatCount:e||1,needResponse:!1}))},handleUrlKeyUp:function(e){27===e.keyCode&&this.hideHints()},onUrlChange:function(e){this.onComposerChange(e),clearTimeout(this._urlTimer),this._urlTimer=setTimeout(this.showHints,300)},onComposerChange:function(e){var t=this;clearTimeout(t.composerTimer),t.composerTimer=setTimeout(t.saveComposer,1e3);var n=e===!0?e:e&&e.target;if(n){if(n===!0||\"SELECT\"===n.nodeName){var r=H(t.refs.method).value;t.setState({method:r},t.updatePrettyData)}(n===!0||\"headers\"===n.name)&&(clearTimeout(t.typeTimer),t.typeTimer=setTimeout(function(){var e=H(t.refs.headers).value;t.setState({type:s(v.parseHeaders(e))})},1e3))}},onTypeChange:function(e){var t=e.target;if(\"INPUT\"===t.nodeName){var n=t.getAttribute(\"data-type\");if(n){this.setState({type:n}),n=P[n];var r=H(this.refs.headers),o=v.parseHeaders(r.value);Object.keys(o).forEach(function(e){if(\"content-type\"===e.toLowerCase())if(n){var t=G.test(o[e])?RegExp[\"$&\"]:\"\";o[e]=n+t,n=null}else delete o[e]}),n&&(o[\"Content-Type\"]=n),r.value=v.objectToString(o),this.updatePrettyData(),this.saveComposer()}}},addHeader:function(){this.refs.prettyHeaders.onAdd()},addField:function(){this.refs.prettyBody.onAdd()},addUploadFiled:function(){this.refs.uploadBody.onAdd()},onHeaderChange:function(e,t){var n=this.refs,r=n.prettyHeaders.toString();H(n.headers).value=r,this.saveComposer(),(\"content-type\"===e.toLowerCase()||t&&\"content-type\"===t.toLowerCase())&&this.setState({type:s(v.parseHeaders(r))})},onFieldChange:function(){var e=this.refs,t=e.prettyBody.toString();t&&this.state.isHexText&&(t=a(t)),H(e.body).value=t,this.saveComposer()},updateUploadData:function(){var e=this.refs.uploadBody.getFields(),t={},n=Z,r=function(e){return!e.data||e.data.length>n?e.value:(n-=e.data.length,{value:e.value,type:e.type,base64:v.bytesToBase64(e.data)})};e.forEach(function(e){var n=t[e.name];Array.isArray(n)?n.push(r(e)):t[e.name]=null==n?r(e):[n,r(e)]}),y.set(\"composerUploadBody\",JSON.stringify(t))},onProxyRules:function(e){var t=e.target.checked;y.set(\"composerProxyRules\",t?1:\"\"),this.setState({enableProxyRules:t})},onShowPretty:function(e){var t=e.target.checked;y.set(\"showPretty\",t?1:0),this.setState({showPretty:t},this.updatePrettyData)},toggleH2:function(e){var t=this;if(!w.supportH2)return void L.confirm(\"HTTP/2 requires Node.js LTS version v16+. Please upgrade\",function(e){e&&window.open(\"https://nodejs.org/\"),t.setState({})});var n=e.target.checked;y.set(\"useH2InComposer\",n?1:\"\"),t.setState({useH2:n})},hideHistory:function(){this.state.showHistory&&this.setState({showHistory:!1})},toggleHistory:function(){var e=!this.state.showHistory;this.setState({showHistory:e}),e&&this.loadHistory(),this.hideHints()},setRulesDisable:function(e){y.set(\"disableComposerRules\",e?1:0),this.setState({disableComposerRules:e})},onDisableChange:function(e){this.setRulesDisable(!e.target.checked)},enableRules:function(){this.state.disableComposerRules&&this.setRulesDisable(!1)},handeHistoryReplay:function(e,t){this._selectedItem=e,t?this.showRepeatTimes(!0):this.onReplay()},handleHistoryEdit:function(e){\nthis.onCompose(e),this.hideHistory()},showRepeatTimes:function(e){var t=this;t.refs.setRepeatTimes.show(),t._isReplay=e,H(t.refs.repeatBtn).innerHTML=e?\"Replay\":\"Send\",setTimeout(function(){var e=H(t.refs.repeatTimes);e.select(),e.focus()},300)},execute:function(e,t){if(t=t>0?Math.min(ee,t):void 0,!e||t||\"INPUT\"!==e.target.nodeName||13===e.keyCode){if(e&&e.shiftKey)return this.showRepeatTimes();var n=this.refs,r=H(n.url).value.trim();if(r&&!this.state.pending){this.onComposerChange(),this.setState({tabName:\"Request\"});var i=w.isStrictMode()||this.state.disableComposerRules,a=i?null:this.state.rules,s=H(n.headers).value,l=s;if(\"string\"==typeof a&&(a=a.trim())){var d,p=v.parseJSON(l),g=[];a=[a],p?(Object.keys(p).forEach(function(e){if(e.toLowerCase()===te){var t=p[e];try{t=\"string\"==typeof t?decodeURIComponent(t):\"\"}catch(n){}t&&a.push(t),delete p[e]}}),d=c(a),d&&(p[te]=d),l=JSON.stringify(p)):(l.split(/\\r\\n|\\r|\\n/).forEach(function(e){var t=e.indexOf(\": \");-1===t&&(t=e.indexOf(\":\"));var n=-1===t?e:e.substring(0,t);if(n=n.toLowerCase(),n===te){var r=e.substring(t+1).trim();try{r=decodeURIComponent(r)}catch(o){}a.push(r)}else g.push(e)}),d=c(a),d&&g.push(te+\": \"+d),l=g.join(\"\\n\"))}var f,m,A,M=this,b=M.getMethod();if(null!=M.localFileBase64)o(b,r,s)&&(m=M.localFileBase64),M.localFileBase64=null;else if(!M.state.disableBody&&o(b,r,s))if(\"upload\"===M.state.type){var y=v.getMultiBody(this.refs.uploadBody.getFields()),x=y.boundary,T=y.length;m=y.base64;var C,N=v.parseJSON(l);if(N)Object.keys(N).forEach(function(e){e=e.toLowerCase(),\"content-type\"===e?(C=C||N[e],delete N[e]):\"content-length\"===e&&delete N[e]}),N[\"Content-Type\"]=u(C,x),N[\"Content-Length\"]=T,l=JSON.stringify(N);else{var I=[];l.split(/\\r\\n|\\r|\\n/).forEach(function(e){var t=e.indexOf(\": \");-1===t&&(t=e.indexOf(\":\"));var n=-1===t?e:e.substring(0,t);n=n.toLowerCase(),\"content-type\"===n?C=C||e.substring(t+1).trim():\"content-length\"!==n&&I.push(e)}),I.push(\"Content-Type: \"+u(C,x)),I.push(\"Content-Length: \"+T),l=I.join(\"\\n\")}}else f=H(n.body).value,A=this.state.isHexText,A?(m=v.getBase64FromHexText(f),f=void 0):f&&this.state.isCRLF&&(f=h(f));this.sendRequest({useH2:this.state.useH2?1:\"\",needResponse:!0,url:r.replace(/^\\/\\//,\"\"),headers:l,method:b,body:f,base64:m,repeatCount:t,isHexText:A,enableProxyRules:this.state.enableProxyRules})}}},handleBody:function(e){var t=this,n=e&&e.reqId,r=t._curDataId;if(r&&w.offComposeData(r),t._curDataId=n,n){var o;w.onComposeData(n,function(e){if(e){var r=t.state.result,i=r&&r.res;if(i){o=v.joinBase64(o,e);var a={};Object.keys(i).forEach(function(e){a[e]=i[e]}),a.base64=o,r.res=a,t.setState({})}o.length>V&&w.offComposeData(n)}})}},handleFrames:function(e){var t=e&&e.headers,n=t&&t[\"x-whistle-req-id\"],r=t&&\"1\"===t[\"x-whistle-frames-mode\"];if(w.curComposerReqId=n,!n||!r)return w.setComposerItem(),void this.setState({reqData:null});var o={id:n,frames:[]};return w.setComposerItem(o),this.setState({reqData:o}),!0},sendRequest:function(e){var t=this,n=(t._reqIndex||0)+1;t._reqIndex=n,e.needResponse&&(clearTimeout(t.comTimer),t.comTimer=setTimeout(function(){t.setState({pending:!1})},5e3)),b.trigger(\"enableRecord\"),t.handleFrames(),w.composeInner(e,function(r,o,i){if(e.needResponse&&t._reqIndex===n){clearTimeout(t.comTimer);var a={pending:!1,tabName:\"Response\"},s=r&&r.res;t.handleFrames(s)&&(r.frames=[],r.inComposer=!0);var l;if(t.handleBody(s),r&&0===r.ec)s?(l=s.headers&&s.headers[\"x-whistle-req-id\"],s.rawHeaders=w.getRawHeaders(s.headers,s.rawHeaderNames),s.rawTrailers=w.getRawHeaders(s.trailers,s.rawTrailerNames)):r.res={statusCode:200},r.url=e.url,r.req=\"\",a.result=r;else{var c=o&&o.status;c?(i=c,v.showSystemError(o)):i&&\"string\"==typeof i&&\"error\"!==i||(i=\"Please check the proxy settings or whether whistle has been started\"),a.result={url:e.url,req:\"\",res:{statusCode:i}}}a.reqId=l,t.setState(a)}}),e.date=Date.now(),e.body=e.body||\"\",e.needResponse&&this.addHistory(e),b.trigger(\"autoRefreshNetwork\"),t.setState({result:\"\",pending:e.needResponse,composerTime:null})},selectAll:function(e){e.target.select(),this.showHints()},saveRules:function(){var e=H(this.refs.composerRules).value;this.state.rules=e,y.set(\"composerRules\",e),this.setState({})},formatJSON:function(){var e=H(this.refs.body);if(e.value.trim()){var t=v.parseRawJson(e.value);t&&(e.value=JSON.stringify(t,null,\"  \"),this.saveComposer())}},inspectJSON:function(){var e=H(this.refs.body).value;b.trigger(\"showJsonViewDialog\",e.trim())},onRulesChange:function(){clearTimeout(this.rulesTimer),this.rulesTimer=setTimeout(this.saveRules,600)},onKeyDown:function(e){(e.ctrlKey||e.metaKey)&&(68==e.keyCode?(e.target.value=\"\",e.preventDefault(),e.stopPropagation()):88==e.keyCode&&e.stopPropagation())},formatHeaders:function(e){v.handleTab(e),this.onKeyDown(e)},onFormat:function(e){v.handleFormat(e,this.formatJSON),v.handleTab(e),this.onKeyDown(e)},showCookiesDialog:function(){var e=this,t=H(e.refs.url).value,n=v.getHostname(t).toLowerCase();return/^[a-z.\\d_-]+$/.test(n)?void(e._pending||(e._pending=!0,w.getCookies({domain:n},function(t,r){e._pending=!1,t=t&&t.cookies||[];var o=30;if(t.length<o)for(var i=w.networkModal.getList(),a=i.length-1;a>=0;a--){var s=i[a];if(v.getHostname(s.url)===n){var l=s.req.headers.cookie;if(l&&-1===t.indexOf(l)&&(t.push(l),t.length>=o))break}}if(!t.length)return I.info(\"Cookies not found\");if(t.length<o){var c=e._cacheCookies;if(c&&c.domain===n)for(var u=0,d=c.cookies.length;d>u;u++){var p=c.cookies[u];if(p&&-1===t.indexOf(p)&&(t.push(p),t.length>=o))break}}e._cacheCookies={domain:n,cookies:t},e.refs.cookiesDialog.show(t)}))):I.info(\"Cookies not found\")},insertCookie:function(e){var t=H(this.refs.headers),n=v.parseHeaders(t.value);n.cookie=e,t.value=v.objectToString(n),this.state.showPretty&&this.refs.prettyHeaders.update(n),this.saveComposer()},setUrl:function(e){H(this.refs.url).value=e||\"\",this.hideHints(),this.onComposerChange()},clickHints:function(e){var t=e.target.title;t&&this.setUrl(t)},onUrlKeyDown:function(e){var t;if(38===e.keyCode)t=this.hintElem.find(\".w-active\"),this.state.showHints||this.showHints(),t.length&&(t.removeClass(\"w-active\"),t=t.prev(\"li\").addClass(\"w-active\")),t.length||(t=this.hintElem.find(\"li:last\"),t.addClass(\"w-active\")),v.ensureVisible(t,this.hintElem),e.preventDefault();else if(40===e.keyCode)t=this.hintElem.find(\".w-active\"),this.state.showHints||this.showHints(),t.length&&(t.removeClass(\"w-active\"),t=t.next(\"li\").addClass(\"w-active\")),t.length||(t=this.hintElem.find(\"li:first\"),t.addClass(\"w-active\")),v.ensureVisible(t,this.hintElem),e.preventDefault();else if(13===e.keyCode){t=this.hintElem.find(\".w-active\");var n=t.attr(\"title\");n&&this.setUrl(n),this.execute()}else{var r=e.target.value;this.onKeyDown(e),r&&!e.target.value&&(this.showHints(),this.setUrl())}},onTabChange:function(e){var t=e.target.name||\"Request\";t!==this.state.tabName&&this.setState({tabName:t})},onContextMenu:function(e){e.preventDefault();var t=!w.whistleId,n=v.getMenuPosition(e,150);O[2].name=this.state.showHistory?\"Hide History\":\"Show History\",O[3].hide=t,n.list=O,n.className=\"w-ctx-sub-menu-left\",this.refs.contextMenu.show(n),this.hideHints()},showHints:function(){var e=this._histroyUrls;if(!e||!e.length)return this.state.showHints=!0,this.loadHistory();var t=H(this.refs.url).value.trim(),n=t.toLowerCase(),r=n?e.filter(function(e){return-1!==e.toLowerCase().indexOf(n)}):e;1===r.length&&t===r[0]&&(r=null),this.setState({showHints:!0,urlHints:r})},hideHints:function(){this.state.showHints&&M(this.refs.hints).find(\".w-active\").removeClass(\"w-active\"),this.setState({showHints:!1})},showParams:function(){var e=H(this.refs.url).value.replace(/#.*$/,\"\"),t=e.indexOf(\"?\"),n=-1!==t,r=n?e.substring(t+1):\"\",o=v.parseQueryString(r,null,null,decodeURIComponent);this.refs.paramsEditor.update(o),this.state.showParams?this.setState({hasQuery:n}):this.setState({showParams:!0,hasQuery:n})},hideParams:function(){this.state.showParams&&this.setState({showParams:!1})},toggleParams:function(){this.state.showParams?this.hideParams():this.showParams()},clearQuery:function(){var e=this;L.confirm(\"Do you confirm the deletion of all params?\",function(t){t&&(e.refs.paramsEditor.clear(),e.hideParams())})},addQueryParam:function(){this.refs.paramsEditor.onAdd()},onParamsChange:function(){var e=this.refs.paramsEditor.toString(),t=H(this.refs.url);t.value=v.replacQuery(t.value,e),this.saveComposer(),this.setState({hasQuery:!!e})},onClickContextMenu:function(e){switch(e){case\"Replay Times\":return this.showRepeatTimes();case\"history\":return this.toggleHistory();case\"file\":return this.uploadFile();case\"Export\":return this[\"export\"]();case\"createApiTest\":return v.showService(\"createApiTest\");case\"copyAsScript\":return v.showService(\"copyAsScript\")}},uploadFile:function(){this.reading||H(this.refs.readLocalFile).click()},readLocalFile:function(){var e=new FormData(H(this.refs.readLocalFileForm)),t=e.get(\"localFile\");if(t.size>F)return L.alert(\"Maximum file size: 20MB\");var n=this;n.reading=!0,v.readFile(t,function(e){n.reading=!1,n.localFileBase64=v.bytesToBase64(e),n.execute()}),H(this.refs.readLocalFile).value=\"\"},\"import\":function(e){b.trigger(\"showImportDialog\",\"composer\")},copyAsCURL:function(){var e=this.state,t=H(this.refs.body).value,n=\"\";e.isHexText&&(n=v.getBase64FromHexText(t),t=\"\");var r=v.asCURL({url:e.url||\"\",req:{method:e.method,headers:v.parseHeaders(e.headers),base64:n,body:t}});v.copyText(r,!0)},\"export\":function(){var e=this.getComposerData(),t=this.state;e.disableBody=t.disableBody,e.rules=t.rules,e.disableComposerRules=t.disableComposerRules,e.isHexText=t.isHexText,e.isCRLF=t.isCRLF,e.type=\"setComposerData\",e.enableProxyRules=t.enableProxyRules,b.trigger(\"showExportDialog\",[\"composer\",e])},onBodyStateChange:function(e){var t=!e.target.checked;this.setState({disableBody:t}),y.set(\"disableComposerBody\",t?1:\"\")},focusEnableBody:function(){this.setState({disableBody:!1}),y.set(\"disableComposerBody\",\"\")},shakeMethod:function(){this.hasBody||v.shakeElem(M(H(this.refs.method)))},render:function(){var e=this,t=e.state,n=t.type,r=t.rules,i=t.showPretty,a=t.useH2,s=t.pending,l=t.result||\"\",c=t.tabName,u=t.showParams,p=\"Request\"===c,h=\"Response\"===c,f=\"Frames\"===c,A=l?l.res&&l.res.statusCode:\"\",M=\"form\"===n,b=t.method,y=o(b,t.url,t.headers),I=i&&M&&y,L=\"upload\"===n&&y,O=w.isStrictMode(),H=O||t.disableComposerRules,F=t.isHexText,V=t.isCRLF,P=t.disableBody,G=s||P||!y,W=t.showHistory,X=t.urlHints,_=t.hasQuery,q=t.enableProxyRules,Z=t.reqData,$=y?null:b+\" method is not allowed to have a request body\",te=t.composerTime,re={\"Status Code\":null==A?\"aborted\":A,\"Take Time\":g(te)};return e.hasBody=y,m.createElement(\"div\",{className:\"fill box w-detail-ctn w-detail-com\"+(W?\" w-show-history\":\"\")+(v.getBool(e.props.hide)?\" hide\":\"\")},m.createElement(\"div\",{className:\"fill v-box\"},m.createElement(\"div\",{className:\"w-com-url box\"},m.createElement(B,{name:\"dashboard\",className:\"w-com-history-btn\",title:(W?\"Hide\":\"Show\")+\" history list\",onClick:this.toggleHistory}),m.createElement(\"select\",{disabled:s,value:b,onChange:this.onComposerChange,ref:\"method\",className:\"form-control w-com-method\"},Q.map(function(e){return m.createElement(\"option\",{value:e},e)})),m.createElement(\"input\",{readOnly:s,defaultValue:t.url,onChange:this.onUrlChange,onKeyUp:this.handleUrlKeyUp,onKeyDown:this.onUrlKeyDown,onFocus:this.selectAll,onDoubleClick:this.showHints,onBlur:this.hideHints,ref:\"url\",type:\"text\",maxLength:\"8192\",placeholder:\"Enter URL\",className:\"fill w-com-input\"}),m.createElement(\"button\",{className:\"btn btn-default w-com-params\",onClick:e.toggleParams},\"Params\"),m.createElement(\"button\",{disabled:s,onClick:this.execute,onContextMenu:e.onContextMenu,title:q?null:\"Whistle Rules IGNORED\",className:\"btn w-com-execute btn-\"+(q?\"primary\":\"info\")},m.createElement(B,{name:\"send\"})),m.createElement(\"div\",{className:\"w-filter-hint\",style:{display:t.showHints&&X&&X.length?\"\":\"none\"},onMouseDown:v.preventBlur},m.createElement(\"div\",{className:\"w-filter-bar\"},m.createElement(\"a\",{onClick:this.toggleHistory},m.createElement(B,{name:\"dashboard\"}),W?\"Hide\":\"Show\",\" history\"),m.createElement(R,{onClick:e.hideHints,className:\"w-clear-hints\"})),m.createElement(\"ul\",{ref:\"hints\",onClick:this.clickHints},X?X.map(function(e){return m.createElement(\"li\",{title:e},e)}):null))),m.createElement(\"div\",{className:\"w-layer w-com-params-editor v-box\"+(u?\"\":\" hide\")},m.createElement(\"div\",{className:\"w-filter-bar\"},m.createElement(R,{onClick:e.hideParams,className:\"w-close-params\"}),m.createElement(\"a\",{style:{display:_?null:\"none\"},className:\"w-params-clear-btn\",onClick:this.clearQuery},m.createElement(B,{name:\"trash\"}),\"Clear\"),m.createElement(\"a\",{onClick:this.addQueryParam},\"+Param\")),m.createElement(N,{ref:\"paramsEditor\",onChange:this.onParamsChange,callback:this.execute})),m.createElement(x,{vertical:\"true\",leftWidth:\"72\"},m.createElement(\"div\",{ref:\"rulesCon\",onDoubleClick:this.enableRules,title:O?Y:void 0,className:\"v-box fill w-com-rules\"},m.createElement(\"div\",{className:\"w-detail-inspectors-title\"},m.createElement(\"label\",{className:\"w-com-rules-label\"},m.createElement(\"input\",{disabled:s,onChange:this.onDisableChange,checked:!t.disableComposerRules,type:\"checkbox\"}),\"Rules\"),m.createElement(\"label\",{className:\"w-com-proxy-rules\",title:\"Whether to use the Rules in Whistle?\"},m.createElement(\"input\",{disabled:s,type:\"checkbox\",onChange:this.onProxyRules,checked:q}),\"Whistle Rules\"),m.createElement(\"label\",{className:\"w-com-use-h2\"},m.createElement(\"input\",{disabled:s,type:\"checkbox\",onChange:this.toggleH2,checked:w.supportH2&&a}),\"HTTP/2\"),m.createElement(\"label\",{className:\"w-com-enable-body\"+(y?\"\":\" w-disabled\"),title:$,onClick:e.shakeMethod},m.createElement(\"input\",{disabled:s||!y,checked:!P&&y,type:\"checkbox\",onChange:this.onBodyStateChange}),\"Body\"),m.createElement(\"div\",{className:\"w-com-btns\"},m.createElement(\"a\",{draggable:\"false\",onClick:e[\"import\"]},\"Import\"),m.createElement(\"a\",{draggable:\"false\",onClick:e[\"export\"]},\"Export\"),m.createElement(\"a\",{draggable:\"false\",onClick:e.copyAsCURL},\"AsCURL\"))),m.createElement(\"textarea\",{readOnly:H||s,defaultValue:r,onKeyDown:v.handleTab,ref:\"composerRules\",onChange:this.onRulesChange,onDoubleClick:this.enableRules,style:{background:!H&&r?\"var(--b-filtered)\":void 0},maxLength:\"8192\",className:\"fill v-box w-com-rules\",placeholder:\"Enter rules (Higher priority than Whistle Rules)\"})),m.createElement(\"div\",{className:\"v-box fill\"},m.createElement(\"div\",{className:\"w-detail-inspectors-title w-com-tabs\"},m.createElement(\"button\",{onClick:this.onTabChange,name:\"Request\",className:ne(p)},m.createElement(B,{name:\"edit\"}),\"Request\"),m.createElement(\"button\",{title:l.url,onClick:this.onTabChange,name:\"Response\",style:{fontWeight:\"normal\"},className:ne(h)},m.createElement(B,{name:\"arrow-left\"}),\"Response\"),m.createElement(\"button\",{title:l.url,id:\"whistleComposerFrames\",onClick:this.onTabChange,name:\"Frames\",style:{fontWeight:\"normal\"},className:ne(f)},m.createElement(B,{name:\"menu-hamburger\"}),\"Frames\")),m.createElement(x,{hide:!p,vertical:\"true\"},m.createElement(\"div\",{className:\"fill v-box w-com-headers\"},m.createElement(\"div\",{className:\"w-com-bar\",onChange:this.onTypeChange},m.createElement(\"label\",null,m.createElement(\"input\",{onChange:this.onShowPretty,type:\"checkbox\",checked:i}),\"Pretty\"),m.createElement(\"label\",{className:\"w-com-label\"},\"Type:\"),m.createElement(\"label\",null,m.createElement(\"input\",{disabled:s,\"data-type\":\"form\",name:\"type\",type:\"radio\",checked:M}),\"Form\"),m.createElement(\"label\",null,m.createElement(\"input\",{disabled:s,\"data-type\":\"upload\",name:\"type\",type:\"radio\",checked:\"upload\"===n}),\"Upload\"),m.createElement(\"label\",null,m.createElement(\"input\",{disabled:s,\"data-type\":\"json\",name:\"type\",type:\"radio\",checked:\"json\"===n}),\"JSON\"),m.createElement(\"label\",null,m.createElement(\"input\",{disabled:s,\"data-type\":\"text\",name:\"type\",type:\"radio\",checked:\"text\"===n}),\"Text\"),m.createElement(\"label\",null,m.createElement(\"input\",{\"data-type\":\"custom\",name:\"type\",type:\"radio\",checked:\"custom\"===n}),\"Raw\"),m.createElement(\"div\",{className:\"flex-1\"}),m.createElement(\"button\",{disabled:s,className:\"btn btn-default\",onClick:this.showCookiesDialog},\"Cookies\"),m.createElement(\"button\",{disabled:s,className:\"btn btn-primary\",onClick:this.addHeader},\"+Header\")),m.createElement(\"textarea\",{readOnly:s,defaultValue:t.headers,onChange:this.onComposerChange,maxLength:J,onKeyDown:this.formatHeaders,ref:\"headers\",placeholder:\"Enter headers\",name:\"headers\",className:\"fill v-box\"+(i?\" hide\":\"\")}),m.createElement(N,{disabled:s,ref:\"prettyHeaders\",isHeader:\"1\",hide:!i,onChange:this.onHeaderChange,callback:this.execute})),m.createElement(\"div\",{className:\"fill v-box w-com-body\"+(P?\" w-com-disable-body\":\"\")},m.createElement(\"div\",{className:\"w-com-bar\"},m.createElement(\"label\",{className:\"w-com-label\",onClick:e.shakeMethod},m.createElement(\"input\",{disabled:s||!y,checked:!P&&y,type:\"checkbox\",onChange:this.onBodyStateChange}),\"Body\"),m.createElement(\"label\",{className:\"w-com-hex-text\"+(F?\" w-checked\":\"\")+(L||I?\" hide\":\"\"),onDoubleClick:this.focusEnableBody},m.createElement(\"input\",{disabled:s,checked:F,type:\"checkbox\",onChange:this.onHexTextChange}),\"HexText\"),m.createElement(\"label\",{className:\"w-com-crlf\"+(F||I||L?\" hide\":\"\")+(V?\" w-checked\":\"\"),onDoubleClick:this.focusEnableBody},m.createElement(\"input\",{disabled:s,checked:V,onChangeCapture:this.onCRLFChange,type:\"checkbox\"}),\"\\\\r\\\\n\"),m.createElement(\"button\",{disabled:s,className:\"btn btn-default\"+(I||F||L?\" hide\":\"\"),onClick:this.formatJSON},\"Format\"),m.createElement(\"button\",{className:\"btn btn-primary\"+(I||F||L?\" hide\":\"\"),onClick:this.inspectJSON},\"Inspect\"),m.createElement(\"button\",{disabled:G,className:\"btn btn-primary\"+(I&&!F||L?\"\":\" hide\"),onClick:L?this.addUploadFiled:this.addField},\"+Param\")),$&&m.createElement(\"div\",{className:\"w-record-status\",onClick:e.shakeMethod},$),m.createElement(\"textarea\",{readOnly:G,defaultValue:t.body,onChange:this.onComposerChange,maxLength:K,onDoubleClick:this.focusEnableBody,onClick:e.shakeMethod,style:{fontFamily:F?\"monospace\":void 0},onKeyDown:this.onFormat,ref:\"body\",placeholder:\"Enter \"+(F?\"hex text\":\"body\"),className:\"fill v-box\"+(I||L?\" hide\":\"\")}),m.createElement(N,{onDoubleClick:this.focusEnableBody,disabled:G,ref:\"prettyBody\",hide:!I||L,onChange:this.onFieldChange,callback:this.execute}),m.createElement(N,{onDoubleClick:this.focusEnableBody,disabled:G,ref:\"uploadBody\",hide:!L,onChange:this.updateUploadData,onUpdate:this.updateUploadData,callback:this.execute,allowUploadFile:!0}))),m.createElement(j,{inited:h},m.createElement(\"div\",{style:{display:h?void 0:\"none\"},className:\"w-com-res w-com-res-\"+d(A)},m.createElement(\"button\",{onClick:this.onTabChange,name:\"Request\",className:\"btn btn-default w-com-back-btn\",title:\"Back to Request\"},m.createElement(B,{name:\"menu-left\"})),m.createElement(C,{modal:re}),m.createElement(z,{reqId:t.reqId}))),m.createElement(j,{inited:h},m.createElement(T,{inComposer:\"1\",modal:l,hide:!h})),m.createElement(j,{inited:f},m.createElement(U,{hide:!f,data:Z,frames:Z&&Z.frames}))))),m.createElement(E,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}),m.createElement(S,{ref:\"setRepeatTimes\",wstyle:\"w-replay-count-dialog\"},m.createElement(\"div\",{className:\"modal-body\"},m.createElement(\"label\",null,\"Times:\",m.createElement(\"input\",{ref:\"repeatTimes\",placeholder:\"<= \"+ee,onKeyDown:this.repeatRequest,onChange:this.repeatTimesChange,value:t.repeatTimes,className:\"form-control\",maxLength:\"3\"})),m.createElement(\"button\",{type:\"button\",ref:\"repeatBtn\",onKeyDown:this.repeatRequest,tabIndex:\"0\",onMouseDown:v.preventBlur,className:\"btn btn-primary\",onClick:this.repeatRequest,disabled:!t.repeatTimes},\"Send\"))),m.createElement(D,{onInsert:this.insertCookie,ref:\"cookiesDialog\"}),m.createElement(k,{show:W,data:t.historyData,onClose:this.hideHistory,onReplay:this.handeHistoryReplay,onEdit:this.handleHistoryEdit}),m.createElement(\"form\",{ref:\"readLocalFileForm\",encType:\"multipart/form-data\",style:{display:\"none\"}},m.createElement(\"input\",{ref:\"readLocalFile\",onChange:this.readLocalFile,type:\"file\",name:\"localFile\"})))}});e.exports=re},function(e,t,n){var r=n(585);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-detail-com{overflow-x:hidden;overflow-y:auto}.w-detail-com textarea{display:block;width:100%;resize:none;border:none;padding:5px}.w-com-url{padding:2px 2px 2px 0;border-bottom:1px solid var(--c-border);position:relative}.w-com-url .w-filter-hint{width:calc(100% - 218px);z-index:2;top:30px;bottom:unset;left:113px;overflow-x:hidden;overflow-y:auto}.w-filter-hint>ul{overflow-x:hidden;overflow-y:auto;max-height:var(--h-hints,360px)}.w-filter-hint>ul li{cursor:default}.w-com-url .w-filter-hint a{text-decoration:none;left:2px}.w-com-execute,.w-com-input,.w-com-method{height:26px}.w-com-input{padding:0 5px;display:block;border:1px solid var(--c-border);border-left:none;border-right:none}.w-com-history-btn{height:26px;width:24px;text-align:center;margin:0 1px 0 2px!important;line-height:26px;cursor:pointer;font-size:15px;color:var(--c-thin)}.w-com-history-btn:hover{color:var(--c-link);background:var(--b-hover)}.w-com-method{display:block;width:85px;margin:0 0 0 2px;padding:0;font-size:9pt;font-weight:700;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.w-com-execute,.w-com-params{padding:0 8px;line-height:24px;margin-left:4px}.w-com-params{border-radius:0;margin-left:0;border-top-right-radius:4px;border-bottom-right-radius:4px;background-color:var(--b-bar);font-size:9pt;font-weight:700}.w-com-params-editor{position:absolute;width:500px;max-width:calc(100% - 40px);height:15pc;z-index:10;top:30px;right:40px}.w-com-params-editor .w-filter-bar{border-bottom:1px solid var(--c-border)}.w-com-params-editor .w-filter-bar a{left:unset;right:5px;top:1px}.w-com-params-editor .w-filter-bar .w-params-clear-btn{right:60px;white-space:nowrap;top:-1px}.w-com-params-editor .w-filter-bar .w-com-params-clear{right:60px}.w-com-rules-label{padding-right:9pt;border-right:1px solid var(--c-border)}.w-com-proxy-rules{padding-left:15px;font-weight:400}.w-com-params-editor .w-filter-bar{text-align:left}.w-com-tabs{position:relative;padding:0!important}.w-com-tabs .btn{position:absolute;top:1px;right:10px;font-size:13px;line-height:14px}.w-com-tabs button{line-height:1pc;padding:0 10px!important}.w-com-tabs button:hover{background:var(--b-btn-hover)}.w-com-tabs .w-tab-btn{outline:0;border:none;background:transparent;height:100%}.w-com-tabs .w-tab-btn:hover{color:var(--c-link)}.w-com-tabs .w-tab-btn.w-active{background:var(--b-active)}.w-com-tabs .w-tab-btn:disabled{color:var(--c-disabled)!important;cursor:not-allowed}.w-com-res-forbidden pre,.w-com-res-forbidden th{color:var(--c-forbidden)!important}.w-com-res-error pre,.w-com-res-error th{color:var(--c-error)!important}.w-com-bar{border-bottom:1px solid var(--c-border);line-height:18px;height:18px;overflow:hidden;background-color:var(--b-bar);position:relative}.w-com-bar .btn{line-height:13px;height:15px;padding:0 5px;font-weight:400;font-size:11px;border-radius:2px;outline:0;margin-right:5px;margin-bottom:1px;overflow:hidden}.w-com-body .w-record-status{font-size:13px}.w-com-body .w-com-bar{text-align:right;height:19px}.w-com-bar,.w-com-bar label{font-size:9pt;font-weight:400}.w-com-bar label{margin-left:10px;cursor:pointer;display:inline-flex;align-items:center;margin-bottom:0}.w-com-bar input,.w-com-rules .w-detail-inspectors-title input{margin:0 5px 0 0;padding:0}.w-com-res{position:relative}textarea.w-com-rules{margin-bottom:2px}.w-com-bar label:first-child{font-weight:700}.w-com-bar label:hover{color:var(--c-default)}.w-com-bar .w-com-label{font-weight:700;display:inline-flex;align-items:center;padding:0 0 0 15px;border-left:1px solid var(--c-border);cursor:default;color:var(--c-default)!important}.w-com-body .w-com-encoding{position:absolute;top:1px;left:60px}.w-com-body>.w-com-bar>.w-com-label{padding:0 10px!important;position:absolute;left:0;top:1px;font-weight:700;border:none;padding:0;margin:0;border-right:1px solid var(--c-border)}.w-com-rules .w-detail-inspectors-title{padding-left:10px!important}.w-com-rules .w-detail-inspectors-title label{margin:0;display:inline-flex;align-items:center}.w-com-rules .w-detail-inspectors-title label:hover{color:var(--c-default)}.w-com-headers .w-com-bar{display:flex;align-items:center}.w-com-body textarea[readonly],.w-com-headers textarea[readonly],.w-com-rules textarea:disabled,.w-com-rules textarea[readonly],.w-detail-com input[readonly],.w-mock-key-name[readonly]{background:var(--b-disabled)!important;color:var(--c-disabled)!important;cursor:default}.w-com-cookies-dialog table th{padding:5px 0 5px 8px}.w-com-cookie-order{width:50px}.w-com-cookie-value{white-space:break-spaces;word-break:break-all}.w-com-cookie-operation{width:10pc}.w-com-cookie-operation a,.w-com-history-operation .btn{padding:2px 10px!important;margin-right:10px;display:inline-block}.w-com-history-dialog .modal-dialog{width:795pt}.w-com-cookies-dialog .modal-dialog{width:45pc}.w-com-bar .w-com-hex-text{position:absolute;left:75px;top:0}.w-com-bar .w-com-crlf{top:0;left:150px;position:absolute}.w-com-bar .w-com-crlf.w-checked,.w-com-bar .w-com-gzip.w-checked,.w-com-bar .w-com-hex-text.w-checked{font-weight:700}.w-com-back-btn{padding:2px 0;width:40px;text-align:center;position:absolute;left:0;top:0;z-index:1;height:23px;border:0;border-radius:0;background-color:var(--b-filtered)}.w-com-use-h2{right:5px}.w-com-history{right:2px;position:absolute}.w-com-enable-body,.w-com-pretty,.w-com-use-h2{margin-left:13px!important;font-weight:400}.w-com-tabs input{margin-right:5px}.w-com-methods .modal-dialog{width:500px}.w-com-methods textarea{width:100%;height:200px;font-size:14px}.w-com-tab-list{border-bottom:1px solid var(--c-border);height:23px;background-color:var(--b-title);min-width:35pc}.w-com-tab-list button{border:none;border-right:1px solid var(--c-border);height:22px;line-height:18px;font-size:9pt;padding:0 9pt;border-radius:0;font-weight:700}.w-com-list{position:relative}.w-mask-iframe{background-color:transparent;position:absolute;left:0;right:0;top:0;bottom:0;z-index:1}.w-com-list .w-custom-tabs{height:22px}.w-hide-history{position:absolute;top:0;right:5px;font-size:1pc;cursor:pointer;line-height:1}.w-history-title{background:var(--b-title);padding:3px 5px;overflow:hidden;position:sticky;top:0;font-weight:500;white-space:nowrap;text-overflow:ellipsis;color:var(--c-default)}.w-history-item,.w-history-title{border-bottom:1px solid var(--c-border)}.w-history-item{padding:6px 5px 8px 10px;overflow:hidden;cursor:default;background-color:var(--b-bar)}.w-history-item div{overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:5;-webkit-box-orient:vertical;max-height:86px;word-wrap:break-word;word-break:break-all}.w-history-item p{margin:0;padding:2px 0 0;white-space:nowrap}.w-history-item i{display:inline-block;font-style:normal;padding:0 5px;line-height:1pc;margin-right:5px;font-size:9px;border-radius:3px;font-weight:400;line-height:9pt;font-weight:500}.w-history-item i.w-req-method-tag-GET{background:var(--c-ok);color:var(--c-active);border-color:var(--c-ok)}.w-history-item i.w-req-method-tag-POST{background:var(--c-post);color:var(--c-active);border-color:var(--c-post)}.w-history-item i.w-req-method-tag-DELETE{background:var(--c-del);color:var(--c-active);border-color:var(--c-del)}.w-history-item i.w-req-method-tag-HEAD{background:var(--c-head);color:var(--c-active);border-color:var(--c-head)}.w-history-item i.w-req-method-tag-OPTIONS{background:var(--c-options);color:var(--c-active);border-color:var(--c-options)}.w-history-item i.w-req-method-tag-TRACE{background:var(--c-trace);color:var(--c-active);border-color:var(--c-trace)}.w-history-item i.w-req-method-tag-PATCH{background:var(--c-patch);color:var(--c-active);border-color:var(--c-patch)}.w-history-item i.w-req-method-tag-PUT{background:var(--c-put);color:var(--c-active);border-color:var(--c-put)}.w-history-item i:last-child{margin-right:0}.w-history-item:hover{background:var(--b-hover);color:var(--c-default)}.w-history-item.w-selected{background:var(--b-active);color:var(--c-default)}.w-req-protocol-tag{border:1px solid var(--c-tag)}.w-req-method-tag{border:1px solid var(--c-ok)}.w-req-type-tag{border:1px solid var(--c-tagx)}.w-com-history-data{position:absolute;top:0;left:0;right:0;bottom:0;z-index:9;opacity:0;transition:ease .6s;visibility:hidden}.w-com-history-data.w-show{opacity:1;visibility:visible}.w-com-history-data .close{position:absolute;right:10px;top:0;cursor:pointer;z-index:1;font-size:20px}.w-com-history-footer .close{top:6px}.w-com-history-list{border-right:1px solid var(--c-border);overflow-x:hidden;overflow-y:auto;height:100%;background:var(--b-bar);position:relative;font-size:9pt}.w-com-history-ctn pre{display:block;padding:0 5px;overflow:auto;white-space:pre;background-color:var(--b-disabled);position:relative}.w-com-history-ctn pre .w-load-tips{position:absolute;z-index:1;background:var(--b-rgba30);left:0;right:0;top:0;bottom:0;color:var(--c-active);font-size:14px;display:none;align-items:center;justify-content:center}.w-com-history-ctn pre.w-show-loading{overflow:hidden}.w-com-history-ctn pre.w-show-loading .w-load-tips{display:flex}.w-com-history-footer{line-height:33px;white-space:nowrap;overflow:hidden;border-bottom:1px solid var(--c-border);padding:0 0 2px;position:relative}.w-com-history-footer .btn{height:24px;line-height:22px;padding:0 6px;margin-left:10px;font-size:13px}.w-com-btns{position:absolute;top:0;right:0}.w-com-btns a{color:var(--c-default);font-weight:400;margin-right:10px;text-decoration:none}.w-com-btns a:last-child{margin-right:5px}.w-com-btns a:hover{color:var(--c-link)}.w-com-disable-body .w-props-editor{opacity:.7}.w-view-inspectors-btn{position:absolute;top:27px;right:5px;z-index:1;display:block;padding:0 5px;background:var(--b-bar);border-radius:3px;font-size:9pt;text-decoration:none!important}\",\"\"])},function(e,t,n){\"use strict\";n(587);var r=n(24),o=n(57),i=n(198),a=n(202),s=n(206),l=n(209),c=n(293),u=n(213),d=n(214),p=20971520,h=128,g=65536,f=160,m=f,A=/^x-whistle-/,M=o.findDOMNode,w=function(e){return\"x-forwarded-for\"===e||A.test(e)},v=r.createClass({displayName:\"PropsEditor\",getInitialState:function(){return{}},getValue:function(e,t){var n=this.props.isHeader,r=this.props.allowUploadFile,o=n?a.decodeURIComponentSafe:a.noop,i=o(e.substring(0,h),n),s={name:i};return r&&t&&null!=t.value?(s.value=o(a.toString(t.value).substring(0,g),n),s.data=t.data,s.size=t.data&&t.data.length||0,s.type=t.type):s.value=o(a.toString(t).substring(0,g),n),s},update:function(e){var t,n={};if(e){var r=this,o=Object.keys(e);t=o.length>=f,t&&(o=o.slice(0,f)),o.forEach(function(t){var o=e[t];return Array.isArray(o)?void o.forEach(function(e,o){n[t+\"_\"+o]=r.getValue(t,e)}):void(n[t+\"_0\"]=r.getValue(t,o))})}return this.setState({modal:n},this.props.onUpdate),t},onAdd:function(){if(!this.props.disabled){if(Object.keys(this.state.modal||\"\").length>=f)return s.error(\"Maximum allowed value: \"+f);this.setState({data:\"\"}),this.showDialog()}},clear:function(){Object.keys(this.state.modal||\"\").length&&this.setState({modal:{}},this.props.onChange)},onEdit:function(e){if(!this.props.disabled){var t=e.target.getAttribute(\"data-name\"),n=this.state.modal[t];this.setState({data:n}),this.showDialog(n)}},execCallback:function(e){\"callback\"===e.target.getAttribute(\"data-action\")&&this.props.callback()},edit:function(e){var t=M(this.refs.name),n=t.value.trim();if(!n)return t.focus(),s.error(\"The name is required\");var r=M(this.refs.valueInput),o=r.value.trim(),i=this.state,a=i.data,l=a.name;a.name=n,a.data=i.fileData,i.fileData?(a.size=i.fileSize,a.value=i.filename,a.type=i.fileType):a.value=o,this.props.onChange(l,n),this.setState({fileData:null,fileSize:null,filename:null,fileType:null}),this.hideDialog(),t.value=r.value=\"\",this.execCallback(e);\n},add:function(e){var t=M(this.refs.name),n=t.value.trim();if(!n)return t.focus(),s.error(\"The name is required\");var r=M(this.refs.valueInput),o=r.value.trim(),i=this.state.modal,a=this.state;i||(i={},a.modal=i),i[n+\"_\"+ ++m]=a.fileData?{name:n,value:a.filename,size:a.fileSize,data:a.fileData,type:a.fileType}:{name:n,value:o},this.props.onChange(n),this.setState({fileData:null,fileSize:null,filename:null,fileType:null}),this.hideDialog(),t.value=r.value=\"\",this.execCallback(e)},hideDialog:function(){this.refs.composerDialog.hide()},showDialog:function(e){this.refs.composerDialog.show();var t=M(this.refs.name);e&&(t.value=e.name||\"\",e.data?this.setState({filename:e.value,fileSize:e.size,fileData:e.data,fileType:e.type}):M(this.refs.valueInput).value=e.value||\"\"),setTimeout(function(){t.select(),t.focus()},600)},onRemove:function(e){var t=this;if(!t.props.disabled){var n=e.target.getAttribute(\"data-name\"),r=t.props.isHeader?\"header\":\"param\",o=t.state.modal[n];l.confirm(\"Do you confirm the deletion of this \"+r+\" '\"+o.name+\"'?\",function(e){e&&(delete t.state.modal[n],t.props.onChange(o.name),t.setState({}))})}},getFields:function(){var e=this.state.modal||\"\";return Object.keys(e).map(function(t){return e[t]})},toString:function(){var e=this.state.modal||\"\",t=Object.keys(e);return this.props.isHeader?t.map(function(t){var n=e[t];return n.name+\": \"+a.encodeNonLatin1Char(n.value)}).join(\"\\r\\n\"):t.map(function(t){var n=e[t];return a.encodeURIComponent(n.name)+\"=\"+a.encodeURIComponent(n.value)}).join(\"&\")},onUpload:function(){this.reading||M(this.refs.readLocalFile).click()},readLocalFile:function(){var e=new FormData(M(this.refs.readLocalFileForm)),t=e.get(\"localFile\");if(t.size>p)return l.alert(\"Total file size must not exceed 20MB\");var n=this.state.modal||\"\",r=t.size;if(Object.keys(n).forEach(function(e){r+=n[e].size}),r>p)return l.alert(\"Total file size must not exceed 20MB\");var o=this;o.reading=!0,a.readFile(t,function(e){o.reading=!1,o.localFileData=e,o.setState({filename:t.name||\"unknown\",fileData:e,fileSize:t.size,fileType:t.type})}),M(this.refs.readLocalFile).value=\"\"},removeLocalFile:function(e){var t=this;t.setState({filename:null,fileData:null},function(){var e=M(t.refs.valueInput);e.select(),e.focus()}),e.stopPropagation()},onContextMenu:function(e){a.handlePropsContextMenu(e,this.refs.contextMenu)},render:function(){var e=this,t=this.state.modal||\"\",n=this.state.filename,o=this.state.fileSize,s=Object.keys(t),l=this.props.isHeader,p=this.props.allowUploadFile,h=this.state.data||\"\",f=h?\"Modify\":\"Add\",m=f+(l?\" Header\":\" Param\"),A=this.props.callback?f+\" & Send\":null;return r.createElement(\"div\",{className:\"fill v-box w-props-editor\"+(this.props.hide?\" hide\":\"\"),title:this.props.title,onDoubleClick:this.props.onDoubleClick},s.length?r.createElement(\"table\",{className:\"table\"},r.createElement(\"tbody\",{onContextMenu:this.onContextMenu},s.map(function(n){var o=t[n];return r.createElement(\"tr\",{key:n,\"data-name\":o.name,\"data-value\":o.value},r.createElement(\"th\",{className:l&&w(o.name)?\"w-bold\":void 0},o.name),r.createElement(\"td\",null,r.createElement(\"pre\",null,o.data?r.createElement(u,{name:\"file\"}):void 0,o.data?\" [\"+a.getSize(o.size)+\"] \":void 0,o.value)),r.createElement(\"td\",{className:\"w-props-ops\"},r.createElement(u,{name:\"edit\",className:\"w-edit-btn\",\"data-name\":n,onClick:e.onEdit,title:\"Edit\"}),r.createElement(u,{name:\"remove\",className:\"w-del-btn\",\"data-name\":n,onClick:e.onRemove,title:\"Delete\"})))}))):r.createElement(\"button\",{onClick:this.onAdd,disabled:this.props.disabled,className:\"btn btn-primary btn-sm w-add-field\"+(this.props.isHeader?\" w-add-header\":\"\")},this.props.isHeader?\"+Header\":\"+Param\"),r.createElement(i,{ref:\"composerDialog\",wstyle:\"w-com-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(d,null),r.createElement(\"label\",null,\"Name:\",r.createElement(\"input\",{ref:\"name\",placeholder:\"Enter name\",className:\"form-control\",maxLength:\"128\"})),r.createElement(\"div\",null,\"Value:\",r.createElement(\"div\",{className:p?\"w-props-editor-upload\":\"w-props-editor-form\"},r.createElement(\"div\",{onClick:this.onUpload,className:\"w-props-editor-file\"+(n?\"\":\" hide\"),title:n},r.createElement(d,{className:\"w-del-btn\",onClick:this.removeLocalFile}),r.createElement(u,{name:\"file\"}),\" [\"+a.getSize(o)+\"] \",n),r.createElement(\"textarea\",{ref:\"valueInput\",maxLength:g,placeholder:\"Enter value\",className:\"form-control\"+(n?\" hide\":\"\"),onKeyDown:a.handleTab}),r.createElement(\"button\",{onClick:this.onUpload,className:\"btn btn-primary\"+(n?\" hide\":\"\")},r.createElement(u,{name:\"folder-open\"}),\"Upload\")))),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),A?r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-action\":\"callback\",onClick:h?e.edit:e.add},A):null,r.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:h?e.edit:e.add},m))),r.createElement(\"form\",{ref:\"readLocalFileForm\",encType:\"multipart/form-data\",style:{display:\"none\"}},r.createElement(\"input\",{ref:\"readLocalFile\",onChange:this.readLocalFile,type:\"file\",name:\"localFile\"})),r.createElement(c,{ref:\"contextMenu\"}))}});e.exports=v},function(e,t,n){var r=n(588);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-props-editor{list-style:none;margin:0;padding:0;overflow-y:auto;overflow-x:hidden}.w-props-editor td,.w-props-editor th{border:none;border-bottom:1px solid var(--c-border);border-top:none!important;padding:0!important;font-weight:400;font-size:9pt;word-break:break-word}.w-props-editor th{width:125px;line-height:22px!important;background:var(--b-prop);text-align:right;padding:0 8px!important}.w-props-editor .w-props-ops{width:75px;padding:4px 0 0 8px!important;padding-left:0!important;background:var(--b-bar)}.w-props-ops span{margin-left:15px!important;text-decoration:none!important;cursor:pointer}.w-edit-btn{color:var(--c-heavy)}.w-edit-btn:hover{color:var(--c-link)}.w-del-btn{color:var(--c-risk)}.w-del-btn:hover{color:var(--c-error)}.w-props-editor pre{overflow:auto;line-height:22px!important;max-height:350px;padding:0 8px!important}.w-add-field{font-size:14px!important;position:absolute;top:50%;left:50%;margin:-17px 0 0 -38px}.w-add-field.w-add-header{margin-left:-3pc}.w-com-dialog .modal-content{width:600px}.w-com-dialog label{display:block;padding:10px 0}.w-com-dialog textarea{height:90pt;white-space:normal}.w-props-editor-upload{white-space:nowrap}.w-props-editor-upload .btn-primary{line-height:81pt;vertical-align:middle}.w-props-editor-upload textarea{display:inline-block;width:478px;margin-right:8px;vertical-align:middle}.w-props-editor-form button{display:none}.w-com-dialog label input,.w-com-dialog label textarea{font-weight:400}.w-props-editor-file{position:relative;display:inline-block;width:577px;height:90pt;line-height:90pt;vertical-align:middle;cursor:pointer;border:1px solid var(--c-border);border-radius:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:center}.w-props-editor-file .close{width:20px;height:20px;position:absolute;line-height:20px;top:0;right:3px}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(214),a=r.createClass({displayName:\"CookiesDialog\",getInitialState:function(){return{cookies:[]}},show:function(e){this.refs.cookiesDialog.show(),this._hideDialog=!1,this.setState({cookies:e||[]})},hide:function(){this.refs.cookiesDialog.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},insert:function(e){var t=e.target.getAttribute(\"data-index\"),n=this.state.cookies[t];n&&this.props.onInsert(n,!0),this.hide()},render:function(){var e=this,t=e.state.cookies;return r.createElement(o,{ref:\"cookiesDialog\",wstyle:\"w-com-cookies-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(i,{onClick:e.hide}),r.createElement(\"table\",{className:\"table\"},r.createElement(\"thead\",null,r.createElement(\"th\",{className:\"w-com-cookie-order\"},\"#\"),r.createElement(\"th\",{className:\"w-com-cookie-value\"},\"Cookie\"),r.createElement(\"th\",{className:\"w-com-cookie-operation\"},\"Operation\")),r.createElement(\"tbody\",{className:\"w-hover-body\"},t.map(function(t,n){return r.createElement(\"tr\",null,r.createElement(\"th\",{className:\"w-com-cookie-order\"},n+1),r.createElement(\"td\",{className:\"w-com-cookie-value\"},t),r.createElement(\"td\",{className:\"w-com-cookie-operation\"},r.createElement(\"a\",{className:\"w-copy-text-with-tips\",\"data-clipboard-text\":t},\"Copy\"),r.createElement(\"a\",{\"data-index\":n,onClick:e.insert},\"Insert\")))})))),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",onClick:e.hide},\"Close\")))}});e.exports=a},function(e,t,n){\"use strict\";function r(e){return!(!e||null!=e.body||!e.dataHash&&!e.base64Hash)}function o(e,t){if(i===e&&a===t)return s;var n=[e.method+\" \"+e.url+\" HTTP/\"+(e.useH2?\"2.0\":\"1.1\")];return t?n.push(\"// Loading data, please wait...\"):(e.headers&&n.push(e.headers),n.push(\"\\n\",e.body||e.base64&&g.base64Decode(e.base64))||\"\"),s=n.join(\"\\n\"),i=e,a=t,s}var i,a,s,l=n(24),c=n(57),u=n(18),d=n(220),p=n(293),h=n(199),g=n(202),f=n(200),m=n(213),A=n(214),M=[{name:\"Copy URL\"},{name:\"Copy As cURL\"},{name:\"Replay\"},{name:\"Replay Times\"},{name:\"Export\"},{name:\"Edit\",icon:\"send\"},{name:\"Service\",list:[{name:\"Create API Test\",action:\"createApiTest\"},{name:\"Copy As Script\",action:\"copyAsScript\"},{name:\"Share Via URL\",action:\"Export\"}]}],w=/^\\s*x-whistle-rule-value:/im,v=l.createClass({displayName:\"HistoryData\",getInitialState:function(){return{}},shouldComponentUpdate:function(e){return this.props.show||e.show!==this.props.show},getItem:function(){for(var e=this.props.data,t=0,n=e.length;n>t;t++){var r=e[t];if(r.selected)return r}},scrollToTop:function(){c.findDOMNode(this.refs.list).scrollTop=0},copyAsCURL:function(e,t){if(t=t||this.getItem()){var n=t.headers;\"string\"==typeof n&&(n=w.test(n)?n.split(/\\r\\n|\\r|\\n/).filter(function(e){return!w.test(e)}).join(\"\\r\\n\"):n,n=g.parseHeaders(n));var r=g.asCURL({url:t.url||\"\",req:{method:t.method,headers:n,base64:t.base64||\"\",body:t.body||\"\"}});g.copyText(r,!0)}},onEdit:function(){this.props.onEdit(this.getItem())},onReplay:function(){this.props.onReplay(this.getItem()),this.scrollToTop()},onReplayTimes:function(){this.props.onReplay(this.getItem(),!0),this.scrollToTop()},handleClick:function(e){this.props.data.forEach(function(e){e.selected=!1}),e.selected=!0,this.setState({})},exportItem:function(e){var t=e.headers,n=[];\"string\"==typeof t&&w.test(t)&&(t=t.split(/\\r\\n|\\r|\\n/).filter(function(e){return w.test(e)?(e=e.substring(e.indexOf(\":\")+1).trim(),e=g.decodeURIComponentSafe(e).trim(),e&&n.push(e),!1):!0}).join(\"\\r\\n\")),h.trigger(\"showExportDialog\",[\"composer\",{type:\"setComposerData\",useH2:e.useH2,rules:n.join(\"\\n\"),url:e.url,method:e.method,headers:t,body:e.body,base64:e.base64,isHexText:e.isHexText}])},\"export\":function(){var e=this.props.data&&this.props.data._groupList;if(e)for(var t=e.length,n=0;t>n;n++){var r=e[n];r&&r.selected&&this.exportItem(r)}},onContextMenu:function(e){var t,n=u(e.target).closest(\"div.w-history-item\");if(n.length||(n=u(e.target).closest(\"div.w-history-title\"),t=!0),e.preventDefault(),n.length){var o=this.props.data&&this.props.data._groupList,i=o&&o[n.attr(\"data-index\")];if(i){var a=!f.whistleId,s=r(i);this._focusItem=i,M[0].name=t?\"Copy\":\"Copy URL\",M[0].copyText=n.attr(\"title\");for(var l=1,c=M.length;c>l;l++){var d=M[l];d.hide=t,d.disabled=s,6===l&&(d.hide=a||t)}var p=g.getMenuPosition(e,130,185-(t?150:0)+(a?0:30));p.className=\"w-keep-history-data\",p.list=M,this.refs.contextMenu.show(p)}}},onClickContextMenu:function(e){switch(e){case\"Copy As cURL\":return this.copyAsCURL(null,this._focusItem);case\"Export\":return this.exportItem(this._focusItem);case\"Replay\":return this.props.onReplay(this._focusItem),this.scrollToTop();case\"Replay Times\":return this.props.onReplay(this._focusItem,!0),this.scrollToTop();case\"Edit\":return this.props.onEdit(this._focusItem);case\"createApiTest\":return g.showService(\"createApiTest\");case\"copyAsScript\":return g.showService(\"copyAsScript\")}},render:function(){var e,t,n=this,i=n.props,a=i.show,s=i.data||[],c=s._groupList||[];return l.createElement(\"div\",{ref:\"historyDialog\",className:\"w-layer box w-com-history-data\"+(a?\" w-show\":\"\")},s.length?null:l.createElement(A,{onClick:i.onClose}),s.length?l.createElement(d,{leftWidth:\"170\"},l.createElement(\"div\",{ref:\"list\",className:\"w-com-history-list\",onContextMenu:n.onContextMenu},c.map(function(o,i){return o.title?l.createElement(\"div\",{className:\"w-history-title\",title:o.title,\"data-index\":i},o.title):(o.selected&&(e=o,t=r(o)),l.createElement(\"div\",{className:\"w-history-item\"+(o.selected?\" w-selected\":\"\"),title:o.url,onClick:function(){n.handleClick(o)},\"data-index\":i},l.createElement(\"div\",null,o.url),l.createElement(\"p\",null,l.createElement(\"i\",{className:\"w-req-method-tag w-req-method-tag-\"+o.method},o.method),l.createElement(\"i\",{className:\"w-req-protocol-tag\"},o.protocol),o.body?l.createElement(\"i\",{className:\"w-req-type-tag\"},\"Body\"):null)))})),l.createElement(\"div\",{className:\"fill v-box w-com-history-ctn\"},e?l.createElement(\"div\",{className:\"w-com-history-footer\"},l.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",disabled:t,onClick:this.copyAsCURL},\"As cURL\"),l.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",disabled:t,onClick:this.onReplay},\"Replay\"),l.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",disabled:t,onClick:this.onReplayTimes},\"Replay Times\"),l.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",disabled:t,onClick:this[\"export\"]},\"Export\"),l.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",disabled:t,onClick:this.onEdit},l.createElement(m,{name:\"send\"}),\"Edit\"),l.createElement(A,{onClick:i.onClose})):null,e?l.createElement(\"pre\",{className:t?\"w-show-loading fill\":\"fill\"},o(e,t),l.createElement(\"div\",{className:\"w-load-tips\"},\"Loading...\")):null)):l.createElement(\"div\",{className:\"w-empty-data\"},\"Empty\"),l.createElement(p,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}))}});e.exports=v},function(e,t,n){\"use strict\";var r=n(24),o=n(199),i=r.createClass({displayName:\"ViewInspector\",getInitialState:function(){return{visible:!1}},showInspectors:function(){o.trigger(\"setActiveSession\",this.props.reqId),o.trigger(\"showInspectors\")},shouldComponentUpdate:function(e,t){var n=e.reqId,r=this.props.reqId!==n;return r&&o.trigger(\"checkViewInspectors\",n),r||this.state.visible!==t.visible},componentDidMount:function(){var e=this;o.on(\"showViewInspectorsBtn\",function(t,n){e.setState({visible:n})}),o.trigger(\"checkViewInspectors\",this.props.reqId)},render:function(){return this.state.visible&&this.props.reqId?r.createElement(\"a\",{className:\"w-view-inspectors-btn\",onClick:this.showInspectors},\"View server-received request data\"):null}});e.exports=i},function(e,t,n){\"use strict\";n(593);var r=n(24),o=n(595),i=n(596),a=n(597),s=n(199),l=n(578),c=n(200),u=n(547),d=n(527),p=n(202),h=[{name:\"Console\",icon:\"console\",active:!0},{name:\"Server\",icon:\"file\"},{name:\"Toolbox\",icon:\"briefcase\"}],g=r.createClass({displayName:\"Tools\",getInitialState:function(){return{name:\"Console\"}},componentDidMount:function(){var e=this;s.on(\"toolTabsChange\",function(){e.changeTab=!0,e.setState({})})},shouldComponentUpdate:function(e){var t=p.getBool(this.props.hide);if(t!=p.getBool(e.hide))return!0;if(t)return!1;var n=this.changeTab;return this.changeTab=!1,n===!0},toggleTabs:function(e){this.changeTab=!0,this.setState({name:e.name,plugin:null})},clearLogs:function(){h[0].active?this.refs.console.clearLogs():h[1].active&&this.refs.serverLog.clearLogs()},onDoubleClickBar:function(){h[0].active?this.refs.console.container.scrollTop<5?this.refs.console.autoRefresh():this.refs.console.scrollTop():h[1].active&&(this.refs.serverLog.container.scrollTop<5?this.refs.serverLog.autoRefresh():this.refs.serverLog.scrollTop())},isActive:function(e){var t=this.state.plugin;return t&&t.plugin===e},getStyle:function(e){return\"btn btn-default\"+(this.isActive(e)?\" w-spec-active\":\"\")},showCustomTab:function(e){this.changeTab=!0,this.refs.tabs.clearSelection(),this.setState({name:null,plugin:e})},createCustomTabs:function(){var e=this,t=c.getToolTabs();return t.length?r.createElement(\"div\",{className:\"fill w-custom-tabs\"},t.map(function(t){var n=t.plugin,o=p.getTabIcon(t);return r.createElement(\"button\",{key:\"_\"+n,onClick:function(){e.showCustomTab(t)},className:\"w-custom-tab-btn \"+e.getStyle(n),title:n},o?r.createElement(\"img\",{className:\"w-tab-icon\",src:o}):null,t.name)})):void 0},render:function(){var e=this.state,t=e.name;return r.createElement(\"div\",{className:\"fill v-box w-tools\"+(p.getBool(this.props.hide)?\" hide\":\"\")},r.createElement(d,{ref:\"tabs\",onDoubleClickBar:this.onDoubleClickBar,onClick:this.toggleTabs,onDoubleClick:this.clearLogs,btns:h,appendTabs:this.createCustomTabs()}),r.createElement(l,{inited:t===h[0].name},r.createElement(o,{ref:\"console\",hide:!h[0].active})),r.createElement(l,{inited:t===h[1].name},r.createElement(i,{ref:\"serverLog\",hide:!h[1].active})),r.createElement(l,{inited:t===h[2].name},r.createElement(a,{hide:!h[2].active})),r.createElement(u,{active:e.plugin&&e.plugin.plugin,hide:p.getBool(this.props.hide),tabs:c.getToolTabs(),className:\"w-custom-tab-panel\"}))}});e.exports=g},function(e,t,n){var r=n(594);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-tools{position:relative;min-width:35pc}.w-tools li,.w-tools ul{list-style:none;padding:0;margin:0;display:block;width:100%;font-size:9pt}.w-tools li{width:100%;padding:1px 0;position:relative}.w-tools pre{background:none;border:none;padding:10px;margin:0;font-size:9pt}.w-log li.w-fatal{background:var(--b-gray)}.w-log li.w-error{background:var(--b-error)}.w-log li.w-warn{background:var(--b-warn)}.w-log li.w-info{background:var(--b-default)}.w-log li.w-debug{background:var(--b-disabled)}.w-log li.w-error pre{color:var(--c-error)}.w-log li.w-warn pre{color:var(--c-warn)}.w-log li>pre>ul{display:inline-block;background:transparent!important;margin:0 0 0 10px!important;vertical-align:top}.w-log li>pre>span{vertical-align:top;line-height:24px}.w-log li>pre li,.w-log li>pre ul{width:auto!important}.w-log>.w-log-ctn>ul>li{border-bottom:1px solid var(--c-border)}.w-log li>pre>span{margin-left:10px}.w-log li>pre>span:first-child,.w-log li>pre>ul:first-child{margin-left:0!important}.w-server-log li.w-fatal pre{color:var(--c-risk)}.w-server-log li.w-error pre{color:var(--c-error)}.w-server-log li.w-warn pre{color:var(--c-warn-active)}.w-server-log li.w-info pre{color:var(--c-ok)}.w-server-log li.w-debug pre{color:var(--c-editor)}.w-server-log li.w-trace pre{color:var(--c-disabled)}.w-log,.w-server-log{overflow:auto}.w-server-log .w-log-ctn{background:var(--b-editor)}.w-log-ctn{overflow:auto}.w-log-ctn strong{margin-right:5px}.w-log-action-bar{background:var(--b-title);line-height:20px;height:21px;border-bottom:1px solid var(--c-border);position:relative;padding-left:10px}.w-log-action-bar .w-textarea-bar{right:5px!important;display:block!important;background:transparent;position:absolute!important;top:1px!important}.w-log-action-bar .w-textarea-bar.hide{display:none!important}.w-log-action-bar .w-textarea-bar a{color:var(--c-default)}.w-log-action-bar .w-textarea-bar a:hover{color:var(--c-link)}.w-log-action-bar>.w-dropdown{margin-right:18px;width:70px}.w-com-enable-body.w-disabled,.w-textarea-bar .w-disabled{cursor:default;color:var(--c-disabled)!important}.w-textarea-bar .w-switch-btn{display:inline-block}.w-tools .w-textarea-bar .w-menu-item{top:21px}.w-tools .w-textarea-bar .w-switch-btn,.w-tools .w-textarea-bar a{padding-bottom:2px}.w-log-expand-root{font-weight:400;font-size:9pt;line-height:20px;display:inline-block;height:20px;vertical-align:top;margin-left:15px}.w-log-expand-root input{margin-right:5px;vertical-align:text-bottom}.w-tool-box-ctn{display:block;width:100%;height:10pc;border:1px solid var(--c-border);padding:5px}.w-tool-box{overflow-x:hidden;overflow-y:auto;padding-bottom:20px}.w-tool-box .w-detail-inspectors-title{border:none;border-right:1px solid var(--c-border);line-height:20px}.w-tool-box .w-detail-inspectors-title button{height:18px;line-height:1pc;font-size:9pt;float:right;padding:0 10px;margin-top:1px}.w-tool-box-base64{height:75pt;line-height:92px;text-align:center;border:1px solid var(--c-border);color:var(--c-disabled);font-size:1pc;background:var(--b-default);font-weight:400}.w-text-dialog .modal-dialog{width:900px}.w-json-dialog .modal-dialog{width:905px}.w-text-dialog textarea{width:879px;border:1px solid var(--c-border);border-radius:3px;padding:5px}.w-text-dialog .w-textarea{width:55pc}.w-text-dialog button.close{margin-top:-3px}.w-mock-file button.close{top:3px;right:5px;height:22px;line-height:1;padding:0;position:absolute;width:22px}.w-mock-dialog .modal-dialog{width:750pt}.w-rules-dialog{z-index:var(--z-modal)}.w-create-rules-dialog{z-index:calc(var(--z-modal) + 1)}.w-rules-dialog .modal-dialog{width:765pt}.w-generate-cert input{line-height:34px;height:34px;padding:5px;border-radius:0;border:1px solid var(--c-border);display:block}.w-generate-cert button{border-radius:0}.w-tools .w-custom-tabs{height:22px;background-color:var(--b-title)}.w-tools .w-custom-tabs button.w-spec-active{background-color:var(--b-btn-hover)}.w-tools .w-custom-tabs button{border:none;border-right:1px solid var(--c-border);height:22px;line-height:18px;font-size:9pt;padding:0 9pt;border-radius:0}.w-mock-preview{line-height:1pc;min-height:36px;max-height:90pt;padding:10px;overflow:auto;background-color:var(--b-disabled);display:inline-block;width:820px;vertical-align:top;white-space:pre;position:relative;font-size:13px}.w-mock-dialog .form-control{height:2pc;border-radius:2px;box-shadow:none;display:inline-block;vertical-align:middle}.w-mock-dialog .glyphicon-question-sign{margin-right:5px}.w-mock-row{white-space:nowrap;font-size:9pt;font-weight:400;margin-top:15px;position:relative}.w-mock-row span{display:inline-block;width:90pt;padding-right:10px;font-weight:600;font-size:14px;text-align:right}.w-big-editor-dialog .w-mock-action a,.w-mock-row a{color:var(--c-default);text-decoration:none}.w-big-editor-dialog .w-mock-action a:hover,.w-mock-row a:hover{color:var(--c-link-hover)}.w-url-pattern{width:778px;border-top-right-radius:0!important;border-bottom-right-radius:0!important}.w-mock-row .w-com-params{line-height:30px;border-left:none}.w-mock-protocol{width:10pc;padding-right:0}.w-mock-value-options{width:105px;padding-right:0;margin:0 10px}.w-mock-key-name{width:375px;margin:0 10px 0 0;margin-left:10px;font-weight:700}.w-mock-value-type{width:170px;padding-right:10px}.w-mock-comment{display:none}.w-mock-file .w-mock-comment{margin-left:20px;display:inline-block;color:var(--c-thin)}.w-mock-file .w-mock-comment input{margin-left:5px;width:291px;font-weight:400;font-size:9pt}.w-mock-dialog .modal-footer select{width:220px;vertical-align:middle;margin-right:20px}.w-mock-inline{display:inline-block;vertical-align:top;width:555px;height:200px;border:1px solid var(--c-border);border-radius:2px;padding:5px}.w-mock-row .w-mock-rules-action{display:none;position:absolute;top:0;right:40px;z-index:1}.w-big-editor-dialog .modal-body .w-mock-action a,.w-mock-row .w-mock-action a,.w-mock-row .w-mock-rules-action a{padding:0 6px;border-radius:2px;background:var(--b-title);display:inline-block;color:var(--c-thin)}.w-big-editor-dialog .modal-body .w-mock-action a:hover,.w-mock-row .w-mock-action a:hover,.w-mock-row .w-mock-rules-action a:hover{color:var(--c-default)}.w-mock-row:hover .w-mock-rules-action{display:inline-block}.w-rules-dialog select{margin:0 10px;width:260px;display:inline-block;vertical-align:middle}.w-rules-dialog .w-list-content{height:500px}.w-rules-dialog .CodeMirror{border-radius:3px}.w-rules-dialog .modal-title{padding:10px 0;font-weight:700}.w-mock-action{top:2px;right:20px;position:absolute;display:none}.w-big-editor-dialog .modal-body .w-mock-action{top:9pt;right:11px;font-size:9pt}.w-big-editor-dialog .modal-body:hover .w-mock-action,.w-mock-row:hover .w-mock-action{display:block;background:var(--b-title);border-radius:3px;z-index:1}.w-mock-dialog,.w-mock-dialog .form-control{font-size:13px}.w-mock-dialog .w-mock-row .glyphicon-trash{position:absolute;display:inline-block;width:1pc;top:9pt;right:15px;color:var(--c-thin);cursor:pointer}.w-mock-dialog .w-mock-row .glyphicon-trash:hover{color:var(--c-default)}.w-mock-dialog .w-com-params-editor{top:60px;right:30px;width:840px}.w-com-params-editor .w-params-clear-btn .glyphicon-trash{width:1pc;margin-right:0}\",\"\"])},function(e,t,n){\"use strict\";function r(e,t){if(e.view)return e.view;try{var n=JSON.parse(e.text),r=n.some(function(e){return\"string\"!=typeof e||\"undefined\"===e});return e.view=n.map(function(e){return\"string\"==typeof e&&\"undefined\"!==e?s.createElement(u,{text:r?'\"'+e+'\"':e}):e&&\"object\"===(\"undefined\"==typeof e?\"undefined\":i(e))?s.createElement(c,{data:e,onSearch:function(){d.showJSONDialog(e)},shouldExpandNode:t?void 0:!1}):s.createElement(u,{wStyle:{color:\"var(--c-has)\"},text:e+\"\"})}),e.view}catch(o){}return s.createElement(u,{text:e.text})}function o(e){var t=[\"Level: \"+e.level.toUpperCase()];return e.logId&&t.unshift(\"LogID: \"+e.logId),\" (\"+t.join(\", \")+\")\"}var i=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},a=n(18),s=n(24),l=n(57),c=n(342)[\"default\"],u=n(341),d=n(202),p=n(200),h=n(290),g=n(565),f=n(568),m=n(199),A=n(210),M=n(209),w=l.findDOMNode,v=p.MAX_LOG_LENGTH,b=2097152,y={value:\"\",text:\"All Logs\"},x=s.createClass({displayName:\"Console\",getInitialState:function(){return{scrollToBottom:!0,logIdList:[y],levels:[{value:\"\",text:\"All Levels\"},{value:\"debug\",text:\"Debug\"},{value:\"info\",text:\"Info (Log)\"},{value:\"warn\",text:\"Warn\"},{value:\"error\",text:\"Error\"},{value:\"fatal\",text:\"Fatal\"}],expandRoot:1!=A.get(\"expandJsonRoot\")}},componentDidMount:function(){var e=this,t=this.container=w(e.refs.container),n=this.content=w(e.refs.logContent),r=function(r){var o=e.state,i=o.logs;if(i!==r&&Array.isArray(i)&&r.push.apply(r,i),o.logs=d.filterLogList(r,e.keyword,!0),!e.props.hide){var a=d.scrollAtBottom(t,n);if(a){var s=r.length-v;s>9&&d.trimLogList(r,s,e.keyword)}e.setState({})}};p.uploadLogs&&(r(p.uploadLogs),p.uploadLogs=null),m.on(\"uploadLogs\",function(t,n){if(!e.props.hide){var o=n.logs,i=e.state.logs;if(i){i.push.apply(i,o);var a=i.length-v;a>19&&d.trimLogList(i,a,e.keyword)}else i=o;r(i)}}),p.on(\"log\",r),m.on(\"consoleImportFile\",function(t,n){e.importFile(n)}),m.on(\"consoleImportData\",function(t,n){e.importData(n)}),a(t).on(\"scroll\",function(){var r=e.state.logs;clearTimeout(e.scrollTimer),r&&(e.state.scrollToBottom=d.scrollAtBottom(t,n))&&(e.scrollTimer=setTimeout(function(){var t=r.length-v;e.scrollTimer=null,t>9&&(d.trimLogList(r,t,e.keyword),e.setState({logs:r}))},2e3))})},selectFile:function(){m.trigger(\"showImportDialog\",\"console\")},importFile:function(e){return e&&/\\.log$/i.test(e.name)?e.size>b?M.alert(\"Maximum file size: 2MB\"):void d.readFileAsText(e,this.importData):M.alert(\"Only .log files are supported\")},importData:function(e){e=d.parseLogs(e),e&&m.trigger(\"uploadLogs\",{logs:e})},changeLogId:function(e){p.changeLogId(e.value)},changeLevel:function(e){this.setState({level:e.value})},clearLogs:function(){p.clearedLogs=!0,p.clearLogList(),this.setState({logs:[]})},scrollTop:function(){this.container.scrollTop=0},autoRefresh:function(){this.container.scrollTop=1e7},stopAutoRefresh:function(){d.scrollAtBottom(this.container,this.content)&&(this.container.scrollTop=this.container.scrollTop-10)},shouldComponentUpdate:function(e){var t=d.getBool(this.props.hide),n=t!=d.getBool(e.hide);return n||!t?(n||t||(this.state.scrollToBottom=d.scrollAtBottom(this.container,this.content)),clearTimeout(this.filterTimer),clearTimeout(this.scrollTimer),!0):!1},componentDidUpdate:function(){!this.props.hide&&this.state.scrollToBottom&&(this.container.scrollTop=1e7)},onConsoleFilterChange:function(e){var t=this;e=e.trim();var n=t.state.logs,r=d.parseKeyword(e);if(t.keyword=e&&r,d.filterLogList(n,r),!e){var o=n&&n.length-v;o>9&&n.splice(0,o)}clearTimeout(t.filterTimer),t.filterTimer=setTimeout(function(){t.filterTimer=null,t.setState({})},500)},handleAction:function(e){if(\"top\"===e)return this.scrollTop();if(\"bottom\"===e)return this.autoRefresh();if(\"pause\"===e)return void p.pauseConsoleRecord();var t=\"refresh\"===e;return p.stopConsoleRecord(!t),t?this.autoRefresh():void 0},onBeforeShow:function(){var e=p.getLogIdList()||[];e=e.map(function(e){return{value:e,text:e}}),e.unshift(y),this.setState({logIdList:e})},changeExpandRoot:function(e){this.state.expandRoot=e.target.checked},\"export\":function(){var e=[];this.state.logs.forEach(function(t){t.hide||e.push({id:t.id,text:t.text,level:t.level,date:t.date})}),m.trigger(\"showExportDialog\",[\"console\",e])},render:function(){var e=this.state,t=e.logs||[],n=e.logIdList,i=e.level,a=e.expandRoot,l=!d.hasVisibleLog(t),c=0;return s.createElement(\"div\",{className:\"fill v-box w-textarea w-log\"+(this.props.hide?\" hide\":\"\")},s.createElement(\"div\",{className:\"w-log-action-bar\"},s.createElement(g,{onBeforeShow:this.onBeforeShow,help:d.getDocUrl(\"gui/console.html\"),onChange:this.changeLogId,options:n}),s.createElement(g,{onChange:this.changeLevel,options:e.levels}),s.createElement(\"label\",{className:\"w-log-expand-root\"},s.createElement(\"input\",{type:\"checkbox\",defaultChecked:a,onChange:this.changeExpandRoot}),\"Expand JSON Root\"),s.createElement(\"div\",{className:\"w-textarea-bar\"},s.createElement(f,{onClick:this.handleAction}),s.createElement(\"a\",{onClick:this.selectFile,draggable:\"false\"},\"Import\"),s.createElement(\"a\",{className:l?\"w-disabled\":\"\",onClick:l?null:this[\"export\"],draggable:\"false\"},\"Export\"),s.createElement(\"a\",{className:\"w-clear\"+(l?\" w-disabled\":\"\"),onClick:l?void 0:this.clearLogs,draggable:\"false\"},\"Clear\"))),s.createElement(\"div\",{ref:\"container\",className:\"fill w-log-ctn\"},s.createElement(\"ul\",{ref:\"logContent\"},t.map(function(e,t){var n=\"Date: \"+d.toLocaleString(new Date(e.date))+o(e)+\"\\r\\n\",l=e.hide||i&&!l&&e.level!==i?\" hide\":\"\";return l||++c,s.createElement(\"li\",{key:e.id,title:e.level.toUpperCase(),className:\"w-\"+e.level+l},s.createElement(\"pre\",null,s.createElement(\"strong\",null,\"#\",c),n,r(e,a)))}))),s.createElement(h,{onChange:this.onConsoleFilterChange}))}});e.exports=x},function(e,t,n){\"use strict\";var r=n(18),o=n(24),i=n(57),a=n(341),s=n(202),l=n(200),c=n(290),u=n(568),d=n(199),p=n(565),h=n(209),g=l.MAX_LOG_LENGTH,f=2097152,m=i.findDOMNode,A=o.createClass({displayName:\"ServerLog\",getInitialState:function(){return{scrollToBottom:!0,levels:[{value:\"\",text:\"All Levels\"},{value:\"debug\",text:\"Debug\"},{value:\"info\",text:\"Info/Log\"},{value:\"warn\",text:\"Warn\"},{value:\"error\",text:\"Error\"},{value:\"fatal\",text:\"Fatal\"}]}},componentDidMount:function(){var e=this,t=this.container=m(e.refs.svrContainer),n=this.content=m(e.refs.svrContent),o=function(r,o){var i=e.state,a=i.logs;if(a!==o&&Array.isArray(a)&&o.push.apply(o,a),i.logs=s.filterLogList(o,e.keyword,!0),!e.props.hide){var l=s.scrollAtBottom(t,n);if(l){var c=o.length-g;c>9&&s.trimLogList(o,c,e.keyword)}e.setState({})}};l.uploadLogs&&(o(null,l.uploadLogs),l.uploadLogs=null),d.on(\"uploadLogs\",function(t,n){if(!e.props.hide){var r=n.logs,i=e.state.logs;if(i){i.push.apply(i,r);var a=i.length-g;a>19&&s.trimLogList(i,a,e.keyword)}else i=r;o(null,i)}}),l.on(\"log\",o);var i;r(t).on(\"scroll\",function(){var r=e.state.logs;i&&clearTimeout(i),r&&(e.state.scrollToBottom=s.scrollAtBottom(t,n))&&(i=setTimeout(function(){var t=r.length-g;t>9&&(s.trimLogList(r,t,e.keyword),e.setState({}))},2e3))}),d.on(\"serverImportFile\",function(t,n){\ne.importFile(n)}),d.on(\"serverImportData\",function(t,n){e.importData(n)})},clearLogs:function(){l.clearedSvrLogs=!0,l.clearSvgLogList(),this.setState({logs:[]})},stopAutoRefresh:function(){s.scrollAtBottom(this.container,this.content)&&(this.container.scrollTop=this.container.scrollTop-10)},scrollTop:function(){this.container.scrollTop=0},autoRefresh:function(){this.container.scrollTop=1e7},shouldComponentUpdate:function(e){var t=s.getBool(this.props.hide),n=t!=s.getBool(e.hide);return n||!t?(n||t||(this.state.scrollToBottom=s.scrollAtBottom(this.container,this.content)),clearTimeout(this.filterTimer),!0):!1},componentDidUpdate:function(){!this.props.hide&&this.state.scrollToBottom&&(this.container.scrollTop=1e7)},onServerFilterChange:function(e){var t=this;e=e.trim();var n=s.parseKeyword(e),r=t.state.logs;if(t.keyword=e&&n,s.filterLogList(r,n),!e){var o=r&&r.length-g;o>9&&r.splice(0,o)}clearTimeout(t.filterTimer),t.filterTimer=setTimeout(function(){t.filterTimer=null,t.setState({})},500)},selectFile:function(){d.trigger(\"showImportDialog\",\"server\")},changeLevel:function(e){this.setState({level:e.value})},importFile:function(e){return e&&/\\.log$/i.test(e.name)?e.size>f?h.alert(\"Maximum file size: 2MB\"):void s.readFileAsText(e,this.importData):h.alert(\"Only .log files are supported\")},importData:function(e){e=s.parseLogs(e),e&&d.trigger(\"uploadLogs\",{logs:e})},handleAction:function(e){if(\"top\"===e)return this.scrollTop();if(\"bottom\"===e)return this.autoRefresh();if(\"pause\"===e)return void l.pauseServerLogRecord();var t=\"refresh\"===e;return l.stopServerLogRecord(!t),t?this.autoRefresh():void 0},\"export\":function(){var e=[];this.state.logs.forEach(function(t){t.hide||e.push({id:t.id,text:t.text,level:t.level,date:t.date})}),d.trigger(\"showExportDialog\",[\"server\",e])},render:function(){var e=this.state,t=e.logs||[],n=e.level,r=!s.hasVisibleLog(t),i=0;return o.createElement(\"div\",{className:\"fill v-box w-textarea w-server-log\"+(this.props.hide?\" hide\":\"\")},o.createElement(\"div\",{className:\"w-log-action-bar\"},o.createElement(p,{onChange:this.changeLevel,options:e.levels}),o.createElement(\"div\",{className:\"w-textarea-bar\"},o.createElement(u,{onClick:this.handleAction}),o.createElement(\"a\",{onClick:this.selectFile,draggable:\"false\"},\"Import\"),o.createElement(\"a\",{className:r?\" w-disabled\":\"\",onClick:r?void 0:this[\"export\"],draggable:\"false\"},\"Export\"),o.createElement(\"a\",{className:\"w-clear\"+(r?\" w-disabled\":\"\"),onClick:r?void 0:this.clearLogs,draggable:\"false\"},\"Clear\"))),o.createElement(\"div\",{ref:\"svrContainer\",className:\"fill w-log-ctn\"},o.createElement(\"ul\",{ref:\"svrContent\"},t.map(function(e){var t=e.level.toUpperCase(),r=\"Date: \"+s.toLocaleString(new Date(e.date))+\" (Level: \"+t+\")\\r\\n\"+e.text,l=e.hide||n&&!l&&e.level!==n?\" hide\":\"\";return l||++i,o.createElement(\"li\",{key:e.id,title:t,className:\"w-\"+e.level+l},o.createElement(\"pre\",null,o.createElement(\"strong\",null,\"#\",i),r&&r.length>=2100?o.createElement(a,{text:r}):r))}))),o.createElement(c,{onChange:this.onServerFilterChange}))}});e.exports=A},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(202),a=n(521),s=n(598),l=n(210),c=n(209),u=n(199),d=n(213),p=/^(?:(?:[\\w.-]+:)?\\/\\/)?([\\w.-]+)/i,h=/[^\\s]/,g=2048,f=32768,m=5120,A=5120,M=3145728,w=o.findDOMNode,v=r.createClass({displayName:\"ToolBox\",getInitialState:function(){return{qrcodeValue:i.toString(l.get(\"qrcodeValue\")).substring(0,g),jsonValue:i.toString(l.get(\"jsonValue\")).substring(0,m),codecText:i.toString(l.get(\"codecText\")).substring(0,A)}},_saveQRCodeValue:function(){l.set(\"qrcodeValue\",this.state.qrcodeValue)},saveQRCodeValue:function(){clearTimeout(this.qrcodeTimer),this.qrcodeTimer=setTimeout(this._saveQRCodeValue,1e3)},_saveJSONValue:function(){var e=this.state.jsonValue;e.length<=m&&l.set(\"jsonValue\",e)},_saveCodecText:function(){var e=this.state.codecText;e.length<=A&&l.set(\"codecText\",e)},saveCodecText:function(){clearTimeout(this.codecTimer),this.codecTimer=setTimeout(this._saveCodecText,1e3)},onJSONChange:function(e){this.setState({jsonValue:e.target.value},this._saveJSONValue)},onForamt:function(e){i.handleFormat(e,this.formatJSON),i.handleTab(e)},onCodecChange:function(e){this.setState({codecText:e.target.value},this._saveCodecText)},generageQRCode:function(){this.refs.qrcodeDialog.show(this.state.qrcodeValue)},parseJSON:function(){u.trigger(\"showJsonViewDialog\",this.state.jsonValue)},formatJSON:function(){var e=this.state.jsonValue;e=e&&i.parseRawJson(e),e&&this.setState({jsonValue:JSON.stringify(e,null,\"  \")},this._saveJSONValue)},encode:function(){try{var e=i.toBase64(this.state.codecText);this.refs.textDialog.show(e)}catch(t){c.alert(t.message)}},decode:function(){try{var e=i.decodeBase64(this.state.codecText).text;this.refs.textDialog.show(e)}catch(t){c.alert(t.message)}},uploadFile:function(){w(this.refs.uploadFile).click()},readFile:function(){var e=this,t=new FormData(w(this.refs.uploadFileForm)).get(\"file\");if(!(t.size<=M))return c.alert(\"Maximum file size: 3MB\");var n=\"data:\"+t.type+\";base64,\";i.readFileAsBase64(t,function(r){w(e.refs.uploadFile).value=\"\",e.refs.textDialog.show(n+r,r,t.name)})},onQRCodeChange:function(e){this.setState({qrcodeValue:e.target.value},this.saveQRCodeValue)},onDomainChange:function(e){this.setState({domainValue:e.target.value})},generateCert:function(){window.open(\"cgi-bin/create-cert?domain=\"+encodeURIComponent(this.state.domainValue),\"downloadTargetFrame\")},shouldComponentUpdate:i.shouldComponentUpdate,render:function(){var e=this.state,t=e.qrcodeValue,n=e.jsonValue,o=e.domainValue,l=e.codecText,c=!h.test(n),u=!h.test(l);return r.createElement(\"div\",{className:\"fill v-box w-tool-box \"+(this.props.hide?\"hide\":\"\")},r.createElement(\"div\",{className:\"w-detail-inspectors-title\"},r.createElement(d,{name:\"qrcode\"}),\"QRCode\",r.createElement(\"button\",{className:\"btn btn-primary\",disabled:!h.test(t),onClick:this.generageQRCode},\"Show\")),r.createElement(\"textarea\",{onChange:this.onQRCodeChange,onKeyDown:i.handleTab,value:t,className:\"w-tool-box-ctn\",maxLength:g,placeholder:\"Enter URL or text\"}),r.createElement(\"div\",{className:\"w-detail-inspectors-title\"},r.createElement(d,{name:\"pencil\"}),\"JSON\",r.createElement(\"button\",{className:\"btn btn-primary\",disabled:c,onClick:this.parseJSON,style:{marginLeft:10}},\"Inspect\"),r.createElement(\"button\",{className:\"btn btn-default\",disabled:c,onClick:this.formatJSON},\"Format\")),r.createElement(\"textarea\",{onChange:this.onJSONChange,value:n,className:\"w-tool-box-ctn\",maxLength:f,placeholder:\"Enter JSON text\",onKeyDown:this.onForamt}),r.createElement(\"div\",{className:\"w-detail-inspectors-title\",style:{height:20}},r.createElement(d,{name:\"eye-close\"}),\"Base64\",r.createElement(\"button\",{className:\"btn btn-primary\",style:{marginLeft:10},onClick:this.uploadFile},\"Upload\"),r.createElement(\"button\",{className:\"btn btn-default\",style:{marginLeft:10},disabled:u,onClick:this.encode},\"Encode\"),r.createElement(\"button\",{className:\"btn btn-default\",disabled:u,onClick:this.decode},\"Decode\")),r.createElement(\"textarea\",{onChange:this.onCodecChange,onKeyDown:i.handleTab,value:l,className:\"w-tool-box-ctn\",maxLength:A,placeholder:\"Enter text\"}),r.createElement(\"div\",{className:\"w-detail-inspectors-title\"},r.createElement(d,{name:\"certificate\"}),\"Certificate\"),r.createElement(\"div\",{className:\"box w-generate-cert\"},r.createElement(\"input\",{className:\"fill\",maxLength:\"256\",placeholder:\"Enter certificate domain name\",value:o,onChange:this.onDomainChange}),r.createElement(\"button\",{className:\"btn btn-primary\",disabled:!o||!p.test(o),onClick:this.generateCert},\"Download\")),r.createElement(a,{ref:\"qrcodeDialog\"}),r.createElement(s,{ref:\"textDialog\"}),r.createElement(\"form\",{ref:\"uploadFileForm\",encType:\"multipart/form-data\",style:{display:\"none\"}},r.createElement(\"input\",{ref:\"uploadFile\",onChange:this.readFile,name:\"file\",type:\"file\"})))}});e.exports=v},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(543),a=n(214),s=r.createClass({displayName:\"TextDialog\",getInitialState:function(){return{}},show:function(e,t,n){if(e){var r=this;r.setState({value:e,base64:t,name:n},function(){r.refs.textDialog.show()})}},render:function(){var e=this.state,t=e.value;return r.createElement(o,{ref:\"textDialog\",wstyle:\"w-text-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(a,null),r.createElement(\"div\",{className:\"v-box\",style:{width:860,height:560,marginTop:22}},r.createElement(i,{className:\"fill\",value:t,base64:e.base64,defaultName:e.name}))),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),r.createElement(\"button\",{type:\"button\",className:\"btn btn-primary w-copy-text-with-tips\",\"data-clipboard-text\":t},\"Copy\")))}}),l=r.createClass({displayName:\"TextDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e,t,n){this.refs.textDialog.show(e,t,n)},render:function(){return r.createElement(s,{ref:\"textDialog\"})}});e.exports=l},function(e,t,n){\"use strict\";var r=n(24),o=n(600),i=n(200),a=n(202),s=n(199),l=n(206),c=n(209),u=[{name:\"date\",title:\"Date\",width:200},{name:\"displayName\",title:\"Filename (Count)\"},{name:\"operation\",title:\"Operation\",width:170}],d=function(e,t){var n=t.time-e.time;return 0===n?0:n>0?1:-1},p=r.createClass({displayName:\"Saved\",getInitialState:function(){return{rows:[],loading:!0}},shouldComponentUpdate:function(e){var t=a.getBool(this.props.hide);return t!=a.getBool(e.hide)?(!t&&this.loadDebounce(),!0):!t},componentDidMount:function(){this.loadData(),s.on(\"savedSessionsChanged\",this.loadData)},onImport:function(e){var t=this,n=e.target.parentNode.getAttribute(\"data-index\"),r=t.state.rows[n];t.loadSessions(r,function(e){i.addNetworkList(e),l.success(\"Sessions imported successfully\")})},onExport:function(e){var t=e.target.parentNode.getAttribute(\"data-index\"),n=this.state.rows[t];n&&this.loadSessions(n,function(e){s.trigger(\"exportSessions\",[e,n.filename])})},onRemove:function(e){var t=this,n=e.target,r=n.parentNode.getAttribute(\"data-index\"),o=t.state.rows,s=o[r];s&&c.confirm(\"Do you confirm the deletion of the saved sessions\"+(s.filename?\" '\"+s.filename+\"'\":\"\")+\"?\",function(e){e&&i.removeSavedSessions(JSON.stringify({filename:s.filename,time:s.time,count:s.count}),function(e,n){return e?e.ec?l.error(e.em||\"Error occurred when removing saved sessions\"):(l.success(\"Saved sessions removed successfully\"),r=o.indexOf(s),-1!==r&&o.splice(r,1),t.setState({rows:o}),void t.loadDebounce()):a.showSystemError(n)})})},loadSessions:function(e,t){e&&i.getSavedSessions({filename:e.filename,time:e.time,count:e.count},function(e,n){return e?Array.isArray(e)?void t(e):l.error(\"Error occurred when loading saved sessions\"):a.showSystemError(n)})},loadDebounce:function(){var e=this;clearTimeout(this.loadTimer),this.loadTimer=setTimeout(function(){e.loadData(!0)},600)},loadData:function(e){var t=this;i.getSavedListSafe(function(n){t.setState({rows:n.sort(d).map(function(e,n){var o=e.filename||\"\";return e.date=a.toLocaleString(new Date(e.time)),e.key=o+\"_\"+e.count+\"_\"+e.time,e.displayName=(o?o+\" \":\"\")+\"(\"+e.count+\")\",e.operation=r.createElement(\"div\",{className:\"w-order-table-operation\",\"data-index\":n},r.createElement(\"a\",{onClick:t.onImport},\"Import\"),r.createElement(\"a\",{onClick:t.onExport},\"Export\"),r.createElement(\"a\",{onClick:t.onRemove,className:\"w-delete\"},\"Delete\")),e}),loading:!1},e===!0?null:function(){t.refs.orderTable.scrollToTop()})})},render:function(){var e=this.props;return r.createElement(o,{ref:\"orderTable\",cols:u,rows:this.state.rows,hide:e.hide,loading:this.state.loading})}});e.exports=p},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=r.createClass({displayName:\"OrderTable\",scrollToTop:function(){o.findDOMNode(this.refs.body).scrollTop=0},render:function(){var e=this.props,t=e.rowKey||\"key\",n=e.cols||[],o=e.rows||[],i=e.emptyUrl;return r.createElement(\"div\",{className:\"w-order-table fill vertical-box\"+(e.hide?\" hide\":\"\")},r.createElement(\"table\",{className:\"table w-order-table-head\"},r.createElement(\"thead\",null,r.createElement(\"tr\",null,r.createElement(\"th\",null,\"#\"),n.map(function(e){return r.createElement(\"th\",{key:e.name,style:{width:e.width}},e.title||e.name)})))),r.createElement(\"div\",{ref:\"body\",className:\"w-order-table-body fill\"},r.createElement(\"table\",{className:\"table\"},r.createElement(\"tbody\",{className:\"w-hover-body\"},o.length?o.map(function(e,o){return r.createElement(\"tr\",{key:e[t]||o},r.createElement(\"th\",null,o+1),n.map(function(t){var n=t.name;return r.createElement(\"td\",{key:n,style:{width:t.width},className:t.className},e[n])}))}):r.createElement(\"tr\",null,r.createElement(\"td\",{colSpan:n.length+1,className:\"w-empty\"},e.loading?\"Loading...\":i?r.createElement(\"a\",{href:i,target:\"_blank\"},\"Empty\"):\"Empty\"))))))}});e.exports=i},function(e,t,n){\"use strict\";n(602);var r=n(24),o=n(198),i=n(200),a=n(210),s=n(202),l=n(199),c=n(213),u=n(214),d=s.isElectron,p=d?\"electron\":\"nodejs\",h=r.createClass({displayName:\"About\",getInitialState:function(){return{}},hasNewVersion:function(e){var t=this.props.clientVersion,n=this.state,r=s.compareVersion(e.latestVersion,e.version);return n._hasNewWhistle=r,n._hasNewClient=0,r=r&&s.compareVersion(e.latestVersion,a.get(\"latestVersion\")),!r&&t&&e.latestClientVersion?(n._hasNewClient=s.compareVersion(e.latestClientVersion,t),n._hasNewClient&&s.compareVersion(e.latestClientVersion,a.get(\"latestClientVersion\"))):r},componentDidMount:function(){var e=this,t=function(t){e.setState({version:t.version,latestVersion:t.latestVersion,latestClientVersion:t.latestClientVersion,hasUpdate:e.checkUpdate(e.hasNewVersion(t))})};i.getInitialData(t),l.on(\"updateVersion\",function(e,n){t(n)})},checkUpdate:function(e){return this.props.onCheckUpdate&&(!this._hasUpdate&&!e||e!==this._hasUpdate)&&(this._hasUpdate=e,this.props.onCheckUpdate(e)),e},checkUpdateClient:function(e){this.state._hasNewClient&&i.showLatestClientVersion()&&e.preventDefault()},showAboutInfo:function(){var e=this;e.showDialog();var t=e.props.onClick;\"function\"==typeof t&&t(),i.checkUpdate(function(t){t&&0===t.ec&&(t.latestVersion&&a.set(\"latestVersion\",t.latestVersion),t.latestClientVersion&&a.set(\"latestClientVersion\",t.latestClientVersion),e.setState({version:t.version,latestVersion:t.latestVersion,latestClientVersion:t.latestClientVersion,hasUpdate:e.checkUpdate(e.hasNewVersion(t))}))})},showDialog:function(){this.refs.aboutDialog.show()},hideDialog:function(){this.refs.aboutDialog.hide()},render:function(){var e=this,t=e.state,n=t.version,i=t.latestVersion,a=t.latestClientVersion,l=e.props.clientVersion,d=t._hasNewWhistle,h=t._hasNewClient;return r.createElement(\"a\",{draggable:\"false\",onClick:e.showAboutInfo,className:\"w-about-menu\"},t.hasUpdate?r.createElement(\"i\",{className:\"w-new-version-icon\"}):null,r.createElement(c,{name:\"info-sign\"}),\"About\",r.createElement(o,{ref:\"aboutDialog\",wstyle:\"w-about-dialog\"},r.createElement(\"div\",{className:\"modal-body w-about-has-plugins\"},r.createElement(u,null),r.createElement(\"img\",{alt:\"logo\",src:\"img/whistle.png?v=2016\"}),r.createElement(\"span\",{className:\"w-about-dialog-ctn\"},r.createElement(\"span\",{className:\"w-about-dialog-title\"},\"Whistle for Web Developers\"),l?\"Client Version: \":null,l?r.createElement(\"a\",{className:\"w-about-version\",href:\"https://github.com/avwo/whistle-client/blob/main/CHANGELOG.md\",target:\"_blank\"},l):null,h?r.createElement(\"a\",{className:\"w-new-version\",title:\"Update Whistle Client\",onClick:e.checkUpdateClient,href:s.UPDATE_URL,target:\"_blank\"},\"(NEW: \",a,\")\"):null,l?r.createElement(\"br\",null):null,l?\"Whistle Version: \":\"Version: \",r.createElement(\"a\",{className:\"w-about-version\",href:\"https://github.com/avwo/whistle/blob/master/CHANGELOG.md\",target:\"_blank\"},n),d?r.createElement(\"a\",{className:\"w-new-version\",title:l?\"Update Whistle Client\":\"Update Whistle\",onClick:e.checkUpdateClient,href:s.UPDATE_URL,target:\"_blank\"},\"(NEW: \",i,\")\"):null,r.createElement(\"br\",null),\"Visit\",\" \",r.createElement(\"a\",{className:\"w-about-url\",href:s.getDocUrl()+\"?type=\"+p+\"&version=\"+n,target:\"_blank\"},\"https://wproxy.org\"))),r.createElement(\"div\",{className:\"modal-footer\"},d||h?r.createElement(\"a\",{className:\"btn btn-primary\",title:l?\"Update Whistle Client\":\"Update Whistle\",onClick:e.checkUpdateClient,href:s.UPDATE_URL,target:\"_blank\"},\"Update Now\"):null,r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"))))}});e.exports=h},function(e,t,n){var r=n(603);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-about-dialog .modal-dialog{width:390px}.w-about-dialog .modal-dialog .modal-body{padding-bottom:15px}.w-about-dialog-ctn{display:inline-block;padding-top:10px;margin-left:20px}.w-about-dialog-title{margin-bottom:10px;display:block}.w-about-dialog img{width:60px;vertical-align:top;margin-top:10px}.w-about-menu{position:relative}\",\"\"])},function(e,t,n){\"use strict\";function r(){return L&&\"auto\"===U||\"dark\"===U}function o(e){U=e,j()}function i(e){var t;try{t=window.matchMedia(\"(prefers-color-scheme: dark)\"),L=t.matches}catch(n){return}t&&(e(),\"function\"==typeof t.addEventListener?t.addEventListener(\"change\",e):\"function\"==typeof t.addListener&&t.addListener(e))}function a(e){try{var t=e.contentDocument.documentElement.style;t.colorScheme!==S&&(t.colorScheme=S,t.setProperty(\"--c-border\",g))}catch(n){}}function s(e){if(\"fake\"===e.getAttribute(\"data-type\"))return a(e);try{var t=e.contentWindow,n=t&&t.document.documentElement;if(!n)return;var r=n.getAttribute(\"data-theme\");S!==r&&(n.setAttribute(\"data-theme\",S),\"function\"==typeof t.onWhistleThemeChange&&t.onWhistleThemeChange(S))}catch(o){}}function l(e){e=+e,2===D?(1>e||e>E)&&(e=1):1===D?2!==e&&e!==E&&(e=1):e!==E&&(e=0),p&&h!==e&&(h=e,p.find(\".w-dns-order-option select\").val(h))}function c(e){var t=[];return e?(t.push('<option value=\"1\">Verbatim</option>','<option value=\"2\">IPv4-first</option>'),2===e&&t.push('<option value=\"3\">IPv6-first</option>')):t.push('<option value=\"0\">Default</option>'),t.push('<option value=\"'+E+'\">IPv6-only</option>'),t.join(\"\")}function u(){if(!p){var e=['<h5><strong>Uptime:</strong> <span id=\"whistleUptime\">-</span></h5>','<h5><strong>All Requests:</strong> <span id=\"whistleAllRequests\">-</span></h5>','<h5><strong>All QPS:</strong> <span id=\"whistleAllQps\">-</span></h5>','<h5><strong>Requests:</strong> <span id=\"whistleRequests\">-</span></h5>','<h5><strong>QPS:</strong> <span id=\"whistleQps\">-</span></h5>','<h5><strong>CPU:</strong> <span id=\"whistleCpu\">-</span></h5>','<h5><strong>Memory:</strong> <span id=\"whistleMemory\">-</span></h5>'];p=f('<div class=\"modal fade w-online-dialog\"><div class=\"modal-dialog\"><div class=\"modal-content\"><div class=\"modal-body\"><button type=\"button\" class=\"close\" data-dismiss=\"modal\">&times;</button><div class=\"w-online-ctn\"></div><div class=\"w-online-info\">'+e.join(\"\")+'</div><h5 class=\"w-theme-option\"><strong>Theme:</strong><select class=\"form-control\"><option value=\"auto\">Auto</option><option value=\"dark\">Dark</option><option value=\"light\">Light</option></select></h5><h5 class=\"w-dns-order-option\"><strong>DNS Order:</strong><select class=\"form-control\"></select></h5><p><a class=\"w-online-dns\">View Custom DNS Servers</a></p><a class=\"w-online-shortcuts-settings\">Shortcuts Settings</a></div><div class=\"modal-footer\"><button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button></div></div></div></div>').appendTo(document.body);var t=p.find(\".w-theme-option select\");t.on(\"change\",function(e){var t=e.target.value;y.set(\"appearanceMode\",t),o(t)}),t.val(U),p.find(\".w-dns-order-option select\").on(\"change\",function(e){var t=e.target,n=+t.value;self._pendingDnsOrder=!0,w.setDnsOrder({order:n},function(e,t){return setTimeout(function(){self._pendingDnsOrder=!1},300),e?void l(n):void v.showSystemError(t)})})}return p}function d(e){return e.map(function(e){return\"  \"+e})}n(605);var p,h,g,f=n(18),m=n(24),A=n(57),M=n(198),w=n(200),v=n(202),b=n(607),y=n(210),x=n(209),T=n(206),C=n(608),N=n(213),I=n(214),E=4,D=-1,S=\"light\",L=!1,k=function(){var e=r()?\"dark\":\"light\";if(S!==e){S=e;try{var t=document.documentElement;t.getAttribute(\"data-theme\")!==S&&t.setAttribute(\"data-theme\",S);var n=getComputedStyle(t);g=n.getPropertyValue(\"--c-border\").trim()}catch(o){}}},j=k,U=y.get(\"appearanceMode\");-1===[\"auto\",\"dark\",\"light\"].indexOf(U)&&(U=\"auto\"),o(U),i(function(e){L=e?e.matches:L,j()}),w.handleIframeLoad=function(e){s(e.target)};var B=m.createClass({displayName:\"Online\",getInitialState:function(){return{}},componentWillMount:function(){var e=this,t=!0,n=f(document.body);w.on(\"serverInfo\",function(r){e.updateServerInfo(r),r&&e.checkServerChanged(r);var o=!!r;o!==t&&(t=o,t?n.removeClass(\"w-offline-status\"):n.addClass(\"w-offline-status\")),e.setState({server:r})})},componentDidMount:function(){var e=this;w.setServerInfo=function(t){!e._pendingDnsOrder&&l(t.ipv6Only?E:t.dnsOrder)},j=function(){k();for(var e=document.querySelectorAll(\"iframe\"),t=0;t<e.length;t++)s(e[t])},j(),setInterval(j,1600)},checkServerChanged:function(e){e.mac=e.mac||\"\",void 0===this.macAddr?(this.macAddr=e.mac,this.serverPort=e.port,this.version=e.version,this.baseDir=e.baseDir,this.networkMode=e.networkMode,this.pluginsMode=e.pluginsMode,this.rulesMode=e.rulesMode,this.multiEnv=e.multiEnv,this.rulesOnlyMode=e.rulesOnlyMode):this.version!==e.version||this.baseDir!==e.baseDir||this.rulesOnlyMode!==e.rulesOnlyMode||this.networkMode!==e.networkMode||this.pluginsMode!==e.pluginsMode||this.rulesMode!==e.rulesMode||this.multiEnv!==e.multiEnv?this.refs.confirmReload.show():this.refs.confirmReload.hide()},showServerInfo:function(){this.state.server&&(this.updateServerInfo(w.getServerInfo()),p.modal(\"show\"))},updateServerInfo:function(e){if(e){var t=this,n=t.props.clientVersion;t.state.server=e;var r=[],o=v.escape(e.whistleId),i=v.escape(e.username),a=v.escape(e.host);o&&r.push('<h5 class=\"w-whistle-id-option\" title=\"'+o+'\"><strong>Whistle ID:</strong> '+o+\"</h5>\"),i&&r.push('<h5 class=\"w-system-host\"><strong>Username:</strong> '+i+\"</h5>\"),a&&r.push(\"<h5><strong>Host:</strong> \"+a+\"</h5>\"),e.pid&&r.push(\"<h5><strong>PID:</strong> \"+e.pid+\"</h5>\"),e.nodeVersion&&r.push(\"<h5><strong>Node:</strong> \"+e.nodeVersion+\"</h5>\"),n&&r.push(\"<h5><strong>Client:</strong> v\"+n+\"</h5>\"),e.version&&r.push(\"<h5><strong>Whistle:</strong> v\"+e.version+\"</h5>\");var s=e.realPort||e.port;if(s){var d=null!=e.realHost?e.realHost:e.bip;\"string\"==typeof d&&-1!==d.indexOf(\":\")&&(d=\"[\"+d+\"]\"),r.push(\"<h5><strong>Port:</strong> \"+(d?d+\":\"+s:s)+\"</h5>\")}e.socksPort&&r.push(\"<h5><strong>SOCKS Port:</strong> \"+e.socksPort+\"</h5>\"),e.httpPort&&r.push(\"<h5><strong>HTTP Port:</strong> \"+e.httpPort+\"</h5>\"),e.httpsPort&&r.push(\"<h5><strong>HTTPS Port:</strong> \"+e.httpsPort+\"</h5>\"),e.ipv4.length&&(r.push(\"<h5><strong>IPv4:</strong></h5>\"),r.push(\"<p>\"+e.ipv4.join(\"<br/>\")+\"</p>\")),e.ipv6.length&&(r.push(\"<h5><strong>IPv6:</strong></h5>\"),r.push(\"<p>\"+e.ipv6.join(\"<br/>\")+\"</p>\")),u();var h=p.find(\".w-online-ctn\").html(r.join(\"\")),g=p.find(\".w-login-btn\"),m=g[0];if(m){var A=w.hasWhistleToken;m.className=\"btn w-login-btn  btn-\"+(A?\"danger\":\"primary\"),g.attr(\"data-action\",A?\"logout\":\"login\"),g.find(\".w-login-label\").text(A?\"Logout\":\"Login\"),g.find(\".glyphicon\")[0].className=\"glyphicon glyphicon-log-\"+(A?\"out\":\"in\")}if(D!==e.verbatim&&(D=e.verbatim,p.find(\".w-dns-order-option select\").html(c(e.verbatim))),!t._pendingDnsOrder&&l(e.dnsOrder),h.find(\"h5.w-system-host\").attr(\"title\",e.host),g.on(\"click\",function(e){var t=f(e.target).closest(\"button\").attr(\"data-action\");return\"logout\"===t?x.confirm(\"Are you sure you want to log out?\",function(e){e&&w.logout(function(e,t){return e?0!==e.ec?T.error(e.em||\"Logout failed\"):(T.success(\"Logged out successfully\"),void w.triggerWhistleIdChanged()):v.showSystemError(t)})}):void v.showService(\"login\")}),!t._initProxyInfo){t._initProxyInfo=!0;var M,b=!0,y=p.find(\".w-online-dns\"),C=p.find(\".w-online-shortcuts-settings\"),N=!0;y.on(\"click\",function(){t.refs.dnsDialog.show(w.getServerInfo())}),C.on(\"click\",function(){t.refs.shortcutsSettings.show()});var I=function(e){e&&e.dns?N&&(N=!1,y.show()):N||(N=!0,y.hide())};I(e),setInterval(function(){var e=w.getServerInfo(),t=e&&e.pInfo;if(I(e),!t)return void(b&&(b=!0,p.find(\".w-online-info\").hide()));b&&(b=!1,p.find(\".w-online-info\").show());var n=p.find(\"#whistleRequests\"),r=p.find(\"#whistleAllRequests\"),o=p.find(\"#whistleCpu\"),i=p.find(\"#whistleMemory\"),a=p.find(\"#whistleUptime\"),s=p.find(\"#whistleQps\"),l=p.find(\"#whistleAllQps\");a.text(v.formatTime(t.uptime)),a.parent().attr(\"title\",t.uptime),n.parent().attr(\"title\",\"HTTP[S]: \"+t.httpRequests+\" (Total: \"+t.totalHttpRequests+\")\\nWS[S]: \"+t.wsRequests+\" (Total: \"+t.totalWsRequests+\")\\nTUNNEL: \"+t.tunnelRequests+\" (Total: \"+t.totalTunnelRequests+\")\"),r.parent().attr(\"title\",\"HTTP[S]: \"+t.allHttpRequests+\" (Total: \"+t.totalAllHttpRequests+\")\\nWS[S]: \"+t.allWsRequests+\" (Total: \"+t.totalAllWsRequests+\")\\nTUNNEL: \"+t.tunnelRequests+\" (Total: \"+t.totalTunnelRequests+\")\"),i.parent().attr(\"title\",Object.keys(t.memUsage).map(function(e){return e+\": \"+t.memUsage[e]}).join(\"\\n\")),s.parent().attr(\"title\",[\"HTTP[s]: \"+v.getQps(t.httpQps),\"WS[S]: \"+v.getQps(t.wsQps),\"TUNNEL: \"+v.getQps(t.tunnelQps)].join(\"\\n\")),l.parent().attr(\"title\",[\"HTTP[s]: \"+v.getQps(t.allHttpQps),\"WS[S]: \"+v.getQps(t.allWsQps),\"TUNNEL: \"+v.getQps(t.tunnelQps)].join(\"\\n\"));var c=t.httpRequests+t.wsRequests+t.tunnelRequests,u=t.allHttpRequests+t.allWsRequests+t.tunnelRequests,d=t.totalHttpRequests+t.totalWsRequests+t.totalTunnelRequests,h=t.totalAllHttpRequests+t.totalAllWsRequests+t.totalTunnelRequests;if(t.totalCount=c,t.allCount=d,t.totalUICount=u,t.allUICount=h,M&&M.pInfo){var g=M.pInfo;t.memUsage.rss!==g.memUsage.rss&&i.text(v.getSize(t.memUsage.rss)+\" (Max: \"+v.getSize(t.maxRss)+\")\"),(c!==g.totalCount||d!==g.allCount)&&n.text(c+\" (Total: \"+d+\")\"),(u!==g.totalUICount||h!==g.allUICount)&&r.text(u+\" (Total: \"+h+\")\"),t.cpuPercent!==g.cpuPercent&&o.text(t.cpuPercent+\" (Max: \"+t.maxCpu+\")\"),t.totalQps!==g.totalQps&&s.text(v.getQps(t.totalQps)+\" (Max: \"+v.getQps(t.maxQps)+\")\"),t.totalAllQps!==g.totalAllQps&&l.text(v.getQps(t.totalAllQps)+\" (Max: \"+v.getQps(t.maxAllQps)+\")\")}else n.text(c+\" (Total: \"+d+\")\"),r.text(u+\" (Total: \"+h+\")\"),o.text(t.cpuPercent+\" (Max: \"+t.maxCpu+\")\"),i.text(v.getSize(t.memUsage.rss)+\" (Max: \"+v.getSize(t.maxRss)+\")\"),s.text(v.getQps(t.totalQps)+\" (Max: \"+v.getQps(t.maxQps)+\")\"),l.text(v.getQps(t.totalAllQps)+\" (Max: \"+v.getQps(t.maxAllQps)+\")\");M=e},1e3)}}},reload:function(){location.reload()},getTitle:function(e){if(e){var t=[];e.whistleId&&t.push(\"Whistle ID: \"+e.whistleId),e.username&&t.push(\"Username: \"+e.username),e.host&&t.push(\"Host: \"+e.host),e.pid&&t.push(\"PID: \"+e.pid);var n=e.realPort||e.port;n&&t.push(\"Port: \"+n),e.socksPort&&t.push(\"SOCKS Port: \"+e.socksPort),e.httpPort&&t.push(\"HTTP Port: \"+e.httpPort),e.httpsPort&&t.push(\"HTTPS Port: \"+e.httpsPort),e.ipv4.length&&(t.push(\"IPv4:\"),t.push.apply(t,d(e.ipv4))),e.ipv6.length&&(t.push(\"IPv6:\"),t.push.apply(t,d(e.ipv6)));var r=e.pInfo;return r&&(t.push(\"Uptime: \"+v.formatTime(r.uptime)),t.push(\"All Requests: \"+(r.allHttpRequests+r.allWsRequests+r.tunnelRequests+\" (Total: \"+(r.totalAllHttpRequests+r.totalAllWsRequests+r.totalTunnelRequests)+\")\")),t.push(\"Requests: \"+(r.httpRequests+r.wsRequests+r.tunnelRequests+\" (Total: \"+(r.totalHttpRequests+r.totalWsRequests+r.totalTunnelRequests)+\")\")),r.cpuPercent&&t.push(\"CPU: \"+r.cpuPercent),t.push(\"Memory: \"+v.getSize(r.memUsage.rss)),t.push(\"QPS: \"+v.getQps(r.totalQps)),t.push(\"All QPS: \"+v.getQps(r.totalAllQps))),e.dns&&t.push(\"Use custom DNS servers\"),t.join(\"\\n\")}},setTitle:function(){var e=w.getServerInfo()||this.state.server;A.findDOMNode(this.refs.onlineMenu).title=this.getTitle(e)||\"\"},render:function(){var e=this.state.server||\"\";return m.createElement(\"a\",{ref:\"onlineMenu\",draggable:\"false\",onMouseEnter:this.setTitle,className:\"w-online-menu w-online\"+(e?\"\":\" w-offline\"),onClick:this.showServerInfo},m.createElement(N,{name:\"stats\"}),e?\"Online\":\"Offline\",e.dns?m.createElement(\"span\",null,e.doh?\"(DoH)\":e.r6?\"(IPv6)\":\"(IPv4)\"):e.ipv6Only?\"(IPv6)\":null,m.createElement(M,{ref:\"confirmReload\",wstyle:\"w-confirm-reload-dialog w-confirm-reload-global\"},m.createElement(\"div\",{className:\"modal-body w-confirm-reload\"},m.createElement(I,null),\"The proxy has been modified.\",m.createElement(\"br\",null),\"Do you want to reload this page?\"),m.createElement(\"div\",{className:\"modal-footer\"},m.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),m.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.reload},\"Reload\"))),m.createElement(b,{ref:\"dnsDialog\"}),m.createElement(C,{ref:\"shortcutsSettings\"}))}});e.exports=B},function(e,t,n){var r=n(606);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-online-dialog .modal-dialog{width:20pc}.w-online-dns,.w-online-info{display:none}.w-online-ctn h5{padding:5px 0;margin:0;text-overflow:ellipsis;max-width:100%;white-space:nowrap;overflow:hidden}.w-online-ctn p{margin:-23px 0 5px 36px}.w-confirm-reload-dialog{z-index:var(--z-max)}.w-confirm-reload-global{z-index:calc(var(--z-max) + 1)}.w-confirm-reload-dialog .modal-dialog{width:310px}.w-confirm-reload-dialog .w-confirm-reload{font-weight:700;line-height:1.8;padding:10px 20px}.w-online-menu>span{margin-left:5px;font-size:13px}.w-dns-servers-dialog{z-index:var(--z-modal)}.w-dns-servers-dialog pre{font-size:14px;font-weight:700}.w-dns-servers-dialog .modal-dialog{width:420px}.w-tips-dialog .modal-dialog{width:35pc!important}#appearanceMode{display:inline-block;width:150px;margin-left:10px;height:2pc;font-size:13px;font-weight:400}.w-online-option{font-weight:400!important;display:block}.w-online-option input,.w-online-option span{vertical-align:middle}.w-dns-order-option,.w-theme-option{display:flex;align-items:center}.w-dns-order-option strong,.w-theme-option strong{width:85px}.w-dns-order-option select,.w-theme-option select{margin-left:5px;width:10pc}.w-login-btn span{margin-right:6px}.w-whistle-id-option{font-size:1pc}.w-shortcuts{z-index:var(--z-modal)}.w-shortcuts .modal-dialog{width:660px}.w-shortcuts h5{font-size:1pc}.w-shortcuts .modal-body>div{margin-top:25px;margin-left:10px}.w-shortcuts .modal-body>div:first-child{margin-top:10px}.w-shortcuts label{display:flex;margin:15px 0 15px 20px;align-items:center;font-weight:400}.w-shortcuts label:hover{color:var(--c-default)}.w-shortcuts label input,.w-shortcuts label strong{margin-right:5px}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(214),a=r.createClass({displayName:\"DNSDialog\",getInitialState:function(){return{servers:\"\"}},show:function(e){if(e&&e.dns){this._hideDialog=!1;var t=e.dns;e.doh||(t=e.dns.split(\",\").map(function(e,t){return\"DNS Server\"+(t+1)+\":  \"+e}).join(\"\\n\")),this.setState({ipv6:e.r6,useDefault:e.df,servers:t,doh:e.doh}),this.refs.dnsServersDialog.show()}},hide:function(){this.refs.dnsServersDialog.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},render:function(){var e,t=this.state;return e=t.doh?\"Resolve IP address from follow URL\":\"Resolve \"+(t.ipv6?\"IPv6\":\"IPv4\")+\" address from follow DNS servers\"+(t.useDefault?\" first\":\"\"),r.createElement(o,{ref:\"dnsServersDialog\",wstyle:\"w-dns-servers-dialog\"},r.createElement(\"div\",{className:\"modal-header\"},e,r.createElement(i,null)),r.createElement(\"pre\",{className:\"modal-body\"},t.servers),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),r.createElement(\"button\",{type:\"button\",\"data-dismiss\":\"modal\",className:\"btn btn-primary w-copy-text-with-tips\",\"data-clipboard-text\":t.servers},\"Copy\")))}});e.exports=a},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(202),a=n(200),s=n(214),l=\"Ctrl[Command]\",c=[{\ncategory:\"Network\",list:[[\"importNetwork\",l+\" + I\",\"Import network sessions\"],[\"exportNetwork\",l+\" + E\",\"Export network sessions\"],[\"saveNetwork\",l+\" + S\",\"Save network sessions\"],[\"toggleNetworkState\",l+\" + O\",\"Turn captured requests ON or OFF\"],[\"toggleNetworkPanelLayout\",l+\" + L\",\"Toggle Network Panel layout: Left-right or top-bottom\"],[\"openNetworkSettings\",l+\" + .\",\"Open network settings\"],[\"removeNetworkSessions\",l+\" + D\",\"Remove selected network sessions\"],[\"switchNetworkView\",l+\" + B\",\"Switch between tree and list view of network sessions\"],[\"replaySelectedRequests\",l+\" + Enter\",\"Replay selected requests\"],[\"replaySelectedRequestsTimes\",l+\" + Shift + Enter\",\"Set the number of times to replay the selected requests\"],[\"abortRequest\",l+\" + A\",\"Abort requests\"],[\"clearNetworkSessions\",l+\" + X\",\"Clear network sessions\"],[\"focusNetworkSearchBox\",\"/\",\"Focus the network search box at the bottom\"]]},{category:\"Frames\",list:[[\"replaySelectedFrame\",l+\" + Enter\",\"Replay selected frames\"],[\"clearNetworkFrames\",l+\" + X\",\"Clear frames\"]]},{category:\"Rules\",list:[[\"importRules\",l+\" + I\",\"Import rules\"],[\"exportRules\",l+\" + E\",\"Export rules\"],[\"saveRulesChanges\",l+\" + S\",\"Save rules changes\"],[\"toggleRules\",l+\" + O\",\"Turn rules ON or OFF\"],[\"toggleRulesNum\",l+\" + L\",\"Toggle line numbers\"],[\"openRulesSettings\",l+\" + .\",\"Open rules settings\"],[\"focusRulesSearchBox\",\"/\",\"Focus the rules search box at the bottom\"]]},{category:\"Values\",list:[[\"importValues\",l+\" + I\",\"Import values\"],[\"exportValues\",l+\" + E\",\"Export values\"],[\"saveValuesChanges\",l+\" + S\",\"Save values changes\"],[\"toggleValuesNum\",l+\" + L\",\"Toggle line numbers\"],[\"openValuesSettings\",l+\" + .\",\"Open values settings\"],[\"focusValuesSearchBox\",\"/\",\"Focus the values search box at the bottom\"]]},{category:\"Plugins\",list:[[\"openInstallPlugins\",l+\" + I\",\"Open the plugin installation dialog box\"],[\"togglePlugins\",l+\" + O\",\"Turn all plugins ON or OFF\"]]},{category:\"Others\",list:[[\"switchTabReverse\",l+\" + <--\",\"Toggle Network, Rules, Values, and Plugins in reverse order\"],[\"switchTab\",l+\" + -->\",\"Toggle Network, Rules, Values, and Plugins\"],[\"openService\",l+\" + J\",\"Open service dialog\"]]}],u=r.createClass({displayName:\"ShortcutsSettings\",getInitialState:function(){return{settings:i.shortcutsSettings}},show:function(){this.refs.dialog.show()},hide:function(){this.refs.dialog.hide()},onChange:function(e){var t=e.target;if(\"INPUT\"===t.nodeName){var n=t.checked,r=t.getAttribute(\"data-name\"),o=this.state.settings;o[r]=n,this.setState({settings:o}),i.saveShortcutsSettings()}},render:function(){var e=this.state.settings;return r.createElement(o,{ref:\"dialog\",wstyle:\"w-shortcuts\"},r.createElement(\"div\",null,r.createElement(\"div\",{className:\"modal-header\"},r.createElement(\"h4\",null,\"Shortcuts Settings\"),r.createElement(s,null)),r.createElement(\"div\",{className:\"modal-body\",onChange:this.onChange},c.map(function(t){return r.createElement(\"div\",null,r.createElement(\"h5\",{key:t.category},t.category),t.list.map(function(t){return a.whistleId||\"openService\"!==t[0]?r.createElement(\"label\",{key:t[0]},r.createElement(\"input\",{type:\"checkbox\",\"data-name\":t[0],checked:e[t[0]]!==!1}),\" \",r.createElement(\"strong\",null,t[1],\" :\"),\" \",t[2]):null}))}))),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))}});e.exports=u},function(e,t,n){\"use strict\";n(610);for(var r=n(24),o=n(57),i=n(18),a=n(199),s=n(202).EDITOR_THEMES,l=[13],c=14;36>=c;c+=2)l.push(c);var u=r.createClass({displayName:\"EditorSettings\",componentDidMount:function(){var e=this;a.on(\"toggle\"+(\"rules\"===this.props.name?\"Rules\":\"Values\")+\"LineNumbers\",function(){i(o.findDOMNode(e.refs.showLineNumbers)).trigger(\"click\")})},render:function(){return r.createElement(\"div\",{className:\"w-editor-settings\"},r.createElement(\"p\",null,r.createElement(\"label\",null,r.createElement(\"span\",{className:\"w-label\"},\"Theme:\"),r.createElement(\"select\",{value:this.props.theme,onChange:this.props.onThemeChange,className:\"form-control\"},s.map(function(e){return r.createElement(\"option\",{key:e,value:e},e)})))),r.createElement(\"p\",null,r.createElement(\"label\",null,r.createElement(\"span\",{className:\"w-label\"},\"Font Size:\"),r.createElement(\"select\",{value:this.props.fontSize,onChange:this.props.onFontSizeChange,className:\"form-control\"},l.map(function(e){return r.createElement(\"option\",{key:e,value:e+\"px\"},e+\"px\")})))),r.createElement(\"p\",{className:\"w-editor-settings-box\"},r.createElement(\"label\",{className:\"w-align-items\"},r.createElement(\"input\",{ref:\"showLineNumbers\",checked:this.props.lineNumbers,onChange:this.props.onLineNumberChange,type:\"checkbox\"}),\" \",\"Show line number\")),r.createElement(\"p\",{className:\"w-editor-settings-box\"},r.createElement(\"label\",{className:\"w-align-items\"},r.createElement(\"input\",{checked:this.props.lineWrapping,onChange:this.props.onLineWrappingChange,type:\"checkbox\"}),\" \",\"Auto line wrapping\")))}});e.exports=u},function(e,t,n){var r=n(611);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-editor-settings label,.w-editor-settings-box label{font-weight:400;white-space:nowrap}.w-editor-settings label .w-label{display:inline-block;width:60px;text-align:right;margin-right:5px}.w-editor-settings select{width:186px}.w-rules-settings-dialog .w-editor-settings select{width:260px}.w-editor-settings-box{padding-left:65px;margin:10px!important}.w-editor-settings .form-control{display:inline-block;margin-left:5px}.w-editor-settings p{margin:5px}\",\"\"])},function(e,t,n){\"use strict\";var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e};n(613);var o=n(18),i=n(24),a=n(57),s=n(211),l=n(198),c=n(523),u=n(200),d=n(199),p=n(202),h=n(209),g=n(210),f=n(213),m=n(511),A=n(214),M={backgroundColor:\"var(--b-filtered)\"},w=/[^\\s]/,v=i.createClass({displayName:\"Settings\",getInitialState:function(){var e=c.getDragger(),t=g.get(\"urlType\");return e.onDrop=e.onDrop.bind(this),o.extend(this.getNetworkSettings(),{dragger:e,urlType:\"-\"===t?\"-\":\"\"})},getNetworkSettings:function(){return o.extend(u.getFilterText(),{columns:c.getAllColumns()})},onColumnsResort:function(){d.trigger(\"onColumnsChanged\"),this.setState({columns:c.getAllColumns()})},resetColumns:function(){var e=this;h.confirm(\"Do you confirm resetting the network table's columns?\",function(t){t&&(e.setState({urlType:\"\"}),g.set(\"urlType\",\"\"),c.reset(),e.onColumnsResort())})},shouldComponentUpdate:function(){return this.refs.networkSettingsDialog.isVisible()},componentDidMount:function(){var e=this;d.on(\"toggleTreeView\",function(){e.setState({})}),d.on(\"setNetworkSettings\",function(t,n){n&&h.confirm(\"Do you confirm the changes to the network settings?\",function(t){t&&e.setSettings(n)})})},componentWillUnmount:function(){d.off(\"toggleTreeView\"),d.off(\"setNetworkSettings\")},onNetworkSettingsChange:function(e){var t=e.target,n=t.getAttribute(\"data-name\");if(n&&\"path\"!==n){if(\"viewOwn\"===n)return u.setOnlyViewOwnData(t.checked),this.setState({}),void d.trigger(\"filterChanged\");if(\"treeView\"===n)return void d.trigger(\"switchTreeView\");if(\"viewAllInNewWindow\"===n)return g.set(\"viewAllInNewWindow\",t.checked?\"1\":\"\"),this.setState({});if(\"disabledHNR\"===n)return g.set(\"disabledHNR\",t.checked?\"\":\"1\"),this.setState({});var r,o,i=this.state;switch(n){case\"filter\":i.disabledFilterText=!t.checked,r=!0;break;case\"excludeFilter\":i.disabledExcludeText=!t.checked,r=!0;break;case\"filterText\":r=!0,i.filterText=t.value;break;case\"excludeText\":r=!0,i.excludeText=t.value;break;case\"networkColumns\":o=!0;break;default:c.setSelected(n,t.checked),o=!0}r?(u.setFilterText(i),d.trigger(\"filterChanged\")):o&&d.trigger(\"onColumnsChanged\"),this.setState(i)}},onFilterKeyDown:function(e){(e.ctrlKey||e.metaKey)&&88==e.keyCode&&e.stopPropagation(),p.handleTab(e)},onRowsChange:function(e){s.setMaxRows(e.target.value)},showDialog:function(){var e=this.getNetworkSettings();this.setState(e),this.refs.networkSettingsDialog.show()},hideDialog:function(){this.refs.networkSettingsDialog.hide()},editCustomCol:function(e){e.preventDefault();var t=this;t.refs.editCustomColumn.show();var n=e.target.getAttribute(\"data-name\"),r=n.toLowerCase();t.setState({name:n,value:u[r],key:u[r+\"Key\"]||\"\",nameChanged:!1},function(){setTimeout(function(){var e=a.findDOMNode(t.refs.newColumnName);e.select(),e.focus()},360)})},onNameChange:function(e){var t=e.target.value;this.setState({value:t.trim(),nameChanged:!0})},onKeyChange:function(e){var t=e.target.value;this.setState({key:t.replace(/\\s+/,\"\"),nameChanged:!0})},setCustomColumn:function(e,t,n){var r=this;u.setCustomColumn({name:e,value:n,key:t},function(o,i){if(!o)return void p.showSystemError(i);r.refs.editCustomColumn.hide();var a=e.toLowerCase();u[a]=n||e,u[a+\"Key\"]=t,r.setState({}),d.trigger(\"onColumnTitleChange\")})},changeName:function(){var e=this.state;this.setCustomColumn(e.name,e.key,e.value)},setSettings:function(e){if(e){var t,n,r,o=this,i=o.state,a=e.viewOwn;p.isBool(a)&&u.isOnlyViewOwnData()!==a&&(u.setOnlyViewOwnData(a),n=!0);var l=e.treeView;p.isBool(l)&&l!==(\"1\"===g.get(\"isTreeView\"))&&d.trigger(\"switchTreeView\");var h=e.viewAllInWindow;p.isBool(h)&&h!==(\"1\"===g.get(\"viewAllInNewWindow\"))&&g.set(\"viewAllInNewWindow\",h?\"1\":\"\");var f=e.disabledHNR;p.isBool(f)&&f!==(\"1\"===g.get(\"disabledHNR\"))&&g.set(\"disabledHNR\",f?\"1\":\"\");var m=e.disabledFilterText;p.isBool(m)&&m!==!!i.disabledFilterText&&(i.disabledFilterText=m,t=!0);var A=e.disabledExcludeText;p.isBool(A)&&A!==!!i.disabledExcludeText&&(i.disabledExcludeText=A,t=!0);var M=e.excludeText;p.isString(M)&&M!==i.excludeText&&(i.excludeText=M,t=!0);var w=e.filterText;p.isString(w)&&w!==i.filterText&&(i.filterText=w,t=!0);var v=e.columns;Array.isArray(v)&&i.columns.forEach(function(e){var t=-1!==v.indexOf(e.name);e.selected!==t&&(r=!0,c.setSelected(e.name,t))}),e.maxRows>0&&s.setMaxRows(e.maxRows),t||n?(t&&u.setFilterText(i),d.trigger(\"filterChanged\")):r&&d.trigger(\"onColumnsChanged\"),this.setState({}),[\"Custom1\",\"Custom2\"].forEach(function(t){var n=t.toLowerCase(),r=n+\"Key\",i=e[n],a=e[r]||\"\";p.isString(i)&&p.isString(a)&&(i=i.trim(),a=a.trim(),!i||u[n]===i&&(u[r]||\"\")===a||o.setCustomColumn(t,a,i))})}},\"import\":function(e){d.trigger(\"showImportDialog\",\"networkSettings\")},getSettings:function(){var e=this.state,t=[];return e.columns.forEach(function(e){e.selected&&t.push(e.name)}),{type:\"setNetworkSettings\",disabledExcludeText:e.disabledExcludeText,excludeText:e.excludeText,disabledFilterText:e.disabledFilterText,filterText:e.filterText,columns:t,custom1:u.custom1||\"Custom1\",custom1Key:u.custom1Key,custom2:u.custom2||\"Custom2\",custom2Key:u.custom2Key,maxRows:s.getMaxRows(),viewOwn:u.isOnlyViewOwnData(),viewAllInWindow:\"1\"===g.get(\"viewAllInNewWindow\"),treeView:\"1\"===g.get(\"isTreeView\"),disabledHNR:\"1\"===g.get(\"disabledHNR\")}},\"export\":function(){d.trigger(\"showExportDialog\",[\"networkSettings\",this.getSettings()])},onUrlType:function(e){var t=e.target.value;g.set(\"urlType\",t),this.setState({urlType:t})},render:function(){var e=this,t=e.state,n=t.columns,o=\"1\"===g.get(\"isTreeView\"),a=\"1\"===g.get(\"viewAllInNewWindow\");return i.createElement(l,{ref:\"networkSettingsDialog\",wstyle:\"w-ns-dialog\"},i.createElement(\"div\",{onChange:e.onNetworkSettingsChange,className:\"modal-body\"},i.createElement(A,null),i.createElement(\"fieldset\",{className:\"w-ns-filter\"},i.createElement(\"legend\",null,i.createElement(\"label\",null,i.createElement(\"input\",{checked:!t.disabledExcludeText,\"data-name\":\"excludeFilter\",type:\"checkbox\",className:\"w-va-mdl\"}),i.createElement(\"span\",{className:\"w-va-mdl\"},\"Exclude Filter\")),i.createElement(m,{docsUrl:\"gui/network.html#settings\"})),i.createElement(\"textarea\",{disabled:t.disabledExcludeText,onKeyDown:e.onFilterKeyDown,value:t.excludeText,\"data-name\":\"excludeText\",placeholder:\"Type filter text\",style:!t.disabledExcludeText&&w.test(t.excludeText)?M:void 0,maxLength:u.MAX_EXCLUDE_LEN})),i.createElement(\"fieldset\",{className:\"w-ns-filter\"},i.createElement(\"legend\",null,i.createElement(\"label\",null,i.createElement(\"input\",{checked:!t.disabledFilterText,\"data-name\":\"filter\",type:\"checkbox\",className:\"w-va-mdl\"}),i.createElement(\"span\",{className:\"w-va-mdl\"},\"Include Filter\")),i.createElement(m,{docsUrl:\"gui/network.html#settings\"})),i.createElement(\"textarea\",{disabled:t.disabledFilterText,onKeyDown:e.onFilterKeyDown,value:t.filterText,\"data-name\":\"filterText\",placeholder:\"Type filter text\",style:!t.disabledFilterText&&w.test(t.filterText)?M:void 0,maxLength:u.MAX_INCLUDE_LEN})),i.createElement(\"fieldset\",{className:\"w-ns-columns\"},i.createElement(\"legend\",null,i.createElement(\"label\",null,\"Network Columns\"),i.createElement(\"label\",{onClick:e.resetColumns,className:\"btn btn-default\"},\"Reset\")),n.map(function(n){if(!n.isPlugin){var o,a=n.name,s=\"custom1\"===a,l=s||\"custom2\"===a;return o=l?s?u.custom1:u.custom2:n.title,i.createElement(\"label\",r({},t.dragger,{\"data-name\":a,draggable:!0,key:a}),i.createElement(\"input\",{disabled:n.locked,checked:!!n.selected||!!n.locked,\"data-name\":a,type:\"checkbox\"}),l?i.createElement(\"span\",{title:o,className:\"w-network-custom-col\"},o):o,\"path\"===a?i.createElement(\"select\",{className:\"w-query-select\",value:t.urlType||\"\",onChange:e.onUrlType},i.createElement(\"option\",{value:\"\"},\"+Query\"),i.createElement(\"option\",{value:\"-\"},\"-Query\")):null,l?i.createElement(f,{name:\"edit\",onClick:e.editCustomCol,\"data-name\":n.title,title:\"Edit \"+n.title}):void 0)}})),i.createElement(\"label\",{className:\"w-ns-own\"},\"Maximum Rows:\",i.createElement(\"select\",{className:\"form-control\",onChange:e.onRowsChange,value:s.getMaxRows()},i.createElement(\"option\",{value:\"500\"},\"500\"),i.createElement(\"option\",{value:\"1000\"},\"1000\"),i.createElement(\"option\",{value:\"1500\"},\"1500\"),i.createElement(\"option\",{value:\"2000\"},\"2000\"),i.createElement(\"option\",{value:\"2500\"},\"2500\"),i.createElement(\"option\",{value:\"3000\"},\"3000\"))),i.createElement(\"label\",{className:\"w-ns-own\"},i.createElement(\"input\",{checked:u.isOnlyViewOwnData(),\"data-name\":\"viewOwn\",type:\"checkbox\"}),\"Viewing only your computer's network requests (IP: \",u.clientIp,\")\"),i.createElement(\"label\",{className:\"w-ns-own\"},i.createElement(\"input\",{checked:a,\"data-name\":\"viewAllInNewWindow\",type:\"checkbox\"}),\"ViewAll in new window\"),i.createElement(\"label\",{className:\"w-ns-own\"},i.createElement(\"input\",{checked:o,\"data-name\":\"treeView\",type:\"checkbox\"}),i.createElement(f,{name:\"tree-conifer\"}),\"Show Tree View (Ctrl[Command] + B)\"),o?i.createElement(\"label\",{style:{marginLeft:20},className:\"w-ns-own\"},i.createElement(\"input\",{checked:\"1\"!==g.get(\"disabledHNR\"),\"data-name\":\"disabledHNR\",type:\"checkbox\"}),\"Highlight new requests\"):null),i.createElement(\"div\",{className:\"modal-footer\"},i.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),i.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:e[\"import\"]},\"Import\"),i.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:e[\"export\"]},\"Export\")),i.createElement(l,{ref:\"editCustomColumn\",wstyle:\"w-ns-edit\"},i.createElement(\"div\",{onChange:e.onNetworkSettingsChange,className:\"modal-body\"},i.createElement(A,null),i.createElement(\"label\",null,i.createElement(\"span\",null,\"Column Name:\"),i.createElement(\"input\",{onChange:this.onNameChange,ref:\"newColumnName\",value:t.value,className:\"form-control\",maxLength:\"16\",placeholder:\"Enter custom column name\"})),i.createElement(\"label\",null,i.createElement(\"span\",null,\"Data Key:\"),i.createElement(\"input\",{onChange:this.onKeyChange,value:t.key,className:\"form-control\",maxLength:\"72\",placeholder:\"Enter data key (as: res.headers.x-server ...)\"}))),i.createElement(\"div\",{className:\"modal-footer\"},i.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),i.createElement(\"button\",{disabled:!t.nameChanged,onClick:e.changeName,type:\"button\",className:\"btn btn-primary\"},\"Confirm\"))))}});e.exports=v},function(e,t,n){var r=n(614);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-ns-dialog fieldset{border:1px solid var(--c-border);padding-bottom:10px}.w-ns-dialog legend{border:none;margin:0 10px;padding:0 10px;width:auto}.w-ns-dialog legend label{font-size:13px;width:auto;padding:0}.w-ns-dialog label{font-size:13px;font-weight:400;line-height:30px;height:30px;display:inline-block;width:6pc;margin-right:10px;font-weight:700;text-align:right}.w-ns-columns{margin-top:10px}.w-ns-filter{border:none!important}.w-ns-filter legend{margin:0;padding:0}.w-ns-filter legend label{margin-right:5px;vertical-align:middle}.w-ns-filter textarea[disabled]{background:var(--b-disabled)}.w-ns-columns label{white-space:nowrap;padding-left:10px;margin-left:10px;text-align:left;width:90pt}.w-ns-columns label .glyphicon-edit{margin-left:5px;cursor:pointer}.w-ns-columns label .glyphicon-edit:hover{color:var(--c-link)}.w-ns-columns .btn{margin-right:0;text-align:center;color:var(--c-thin);width:72px}.w-ns-columns legend label{margin-left:0}.w-ns-dialog .modal-dialog{width:600px}.w-ns-dialog input[type=checkbox]{margin-right:5px}.w-ns-dialog textarea{width:578px;height:90px;border:1px solid var(--c-border);overflow:auto;margin:0;padding:5px 10px;border-radius:5px}.w-ns-dialog div input[type=text]{width:436px}.w-ns-own{width:auto!important;margin:10px 0 0;white-space:nowrap;display:flex!important;text-align:left!important;align-items:center}.w-ns-own select{margin-left:20px;width:10pc;display:inline-block}.w-ns-own .glyphicon-tree-conifer{margin:-3px 2px 0 0}.w-network-custom-col{display:inline-block;max-width:90px;text-overflow:ellipsis;white-space:nowrap}.w-ns-columns label span{vertical-align:middle;overflow:hidden;font-weight:400}.w-ns-edit label{white-space:nowrap;margin-bottom:10px}.w-ns-edit label span{display:inline-block;width:75pt}.w-ns-edit label .form-control{display:inline-block;margin:0 10px;font-weight:400}.w-ns-edit label select{width:150px}.w-ns-edit label input{width:345px}.w-ns-edit .modal-dialog{width:500px}.w-ns-columns legend .btn-primary{width:auto;margin-right:10px;padding:0 8px;color:var(--c-active);font-weight:400}.w-certs-title{margin-bottom:10px;font-size:1pc;display:inline-flex;align-items:center}.w-query-select{font-size:9pt;width:4pc;text-align:center;height:20px;color:var(--c-thin);margin-left:5px;border:1px solid var(--c-border);border-radius:3px}\",\"\"])},function(e,t,n){\"use strict\";function r(e,t,n,r){return e?Object.keys(e).sort(T.getPluginComparator(e)).map(function(o){var i=e[o];return!r&&!x.disableInstaller&&i&&t&&n&&i.installUrl&&!n[o.slice(0,-1)]&&t.push(i),i}):[]}function o(e){if(!e)return\"\";e=e.split(O);for(var t=0,n=e.length;n>t;t++){var r=e[t];if(Q.test(r))return r.substring(11,1035)}return\"\"}function i(e){m||(m=setTimeout(function(){m=null},2e3),b.trigger(\"disableAllPlugins\",e))}function a(e,t){var n=e?\" --account=\"+e:\"\";return n+(t?\" --dir=\"+t:\"\")}function s(e){var t=x.getServerInfo().cmdName,n=\"\";return t&&U.test(t)?(t=RegExp.$1+\" \",n=\" \"+RegExp.$2.trim()):t=\"w2 \",t+\"install\"+n+\" \"+(e?a(x.account,x.whistleName):\"\")}function l(e){return a(e.account,e.dir)}function c(e){return e.updateUrl||e.moduleName}function u(e){return e.rules||e._rules||e.resRules}function d(e){return(e.pluginHomepage||e.openExternal)&&!e.openInPlugins&&!e.openInModal}function p(e){return e.pluginHomepage||\"plugin.\"+T.getSimplePluginName(e)+\"/\"}function h(e){return/^https?:\\/\\/[^/?]/.test(e)&&e.length<=1024?e:\"\"}function g(e,t){if(T.isString(e))e=e.split(/[\\s,;|]+/);else if(!Array.isArray(e))return;var n=[];return e.forEach(function(e){T.isString(e)&&(e=e.trim(),R.test(e)&&n.push(e))}),n.length&&{plugins:n.join(\" \"),registry:h(t)}}function f(e){if(!e)return\"\";var t=[];return t.push(\"Name: \"+e.moduleName),t.push(\"Version: \"+e.version),e.homepage&&t.push(\"Homepage: \"+e.homepage),t.join(\"\\n\")}n(616);var m,A,M=n(18),w=n(24),v=n(57),b=n(199),y=n(198),x=n(200),T=n(202),C=n(209),N=n(578),I=n(210),E=n(618),D=n(293),S=n(296),L=n(213),k=n(214),j=v.findDOMNode,U=/^([\\w]{1,12})(\\s+-g)?$/,B=/(?:^|[\\s,;|])(?:@[\\w.~-]+\\/)?whistle\\.[a-z\\d_-]+(?:\\@[\\w.^~*-]*)?(?:$|[\\s,;|])/,R=/^((?:@[\\w.~-]+\\/)?whistle\\.[a-z\\d_-]+)(?:\\@([\\w.^~*-]*))?$/,z=/\\s+$/,Q=/^--registry=https?:\\/\\/[^/?]/,O=/\\s*[|,;\\s]+\\s*/,H={width:270},F=[{name:\"Copy\"},{name:\"Disable\"},{name:\"Option\"},{name:\"Rules\"},{name:\"Update\"},{name:\"Uninstall\"},{name:\"Install\"},{name:\"Others\",action:\"Plugins\",list:[]},{name:\"Help\",sep:!0}];window.getWhistleProxyServerInfo=function(){var e=x.getServerInfo();return e&&M.extend(!0,{},e)};var V=w.createClass({displayName:\"Home\",componentDidMount:function(){var e=this,t=M(j(e.refs.plugins));e.setUpdateAllBtnState(),b.on(\"openPluginOption\",function(e,t){if(t){if(t.openInModal)return b.trigger(\"showPluginOption\",t);var n=p(t);return d(t)?window.open(n):void b.trigger(\"showPluginOptionTab\",t)}}),b.on(\"showUninstallPlugin\",function(t,n){e.showUninstall(n)}),b.on(\"showInstallPlugin\",function(t,n){e.showUpdate(n)}),b.on(\"showPluginRules\",function(t,n){e.onShowRules(T.getSimplePluginName(n))}),b.on(\"installPlugins\",function(){e.showInstall()}),b.on(\"showInstallPlugins\",function(t,n,r){e.showInstall(n,r)}),b.on(\"showUpdatePlugins\",function(t,n,r){e.showInstall(n,r,!0)}),b.on(\"highlightPlugin\",function(e,n){if(n){var r=t.find('.w-plugins-item[data-name=\"'+n+'\"]');r.length&&T.shakeElem(r)}}),b.on(\"updateAllPlugins\",function(){var t,n=e.props.data||{},o=n.plugins||{},i={};r(o).forEach(function(e){if(e&&!e.isProj&&T.compareVersion(e.latest,e.version)){t=t||e.registry;var n=l(e),r=i[n]||[];r.push(c(e)),i[n]=r}});var a=Object.keys(i).map(function(e){var t=i[e].join(\" \");return s()+t+e}).join(\"\\n\\n\");a&&e.getRegistryList(function(n,r){e.setState({cmdMsg:a,install:!1,registry:r||t||\"\",registryChanged:!0,registryList:n},e.showMsgDialog)})})},getRegistryList:function(e){var t=[];this.installUrls&&this.installUrls.forEach(function(e){var n=e.installUrl&&e.installRegistry;Array.isArray(n)&&n.forEach(function(e){e&&-1===t.indexOf(e)&&t.push(e)})}),x.plugins.getRegistryList(function(n,r){var o=I.get(\"pluginsRegistry\");n?0!==n.ec?C.alert(n.em):(A=x.getPluginRegistry(),n.list.forEach(function(e){-1===A.indexOf(e)&&A.push(e)})):T.showSystemError(r),t.length&&(A=A||[],t.forEach(function(e){-1===A.indexOf(e)&&A.push(e)})),A&&(Q.test(\"--registry=\"+o)||(o=\"\"),e(A,o))})},componentDidUpdate:function(){this.setUpdateAllBtnState()},execCmd:function(){var e=this.state,t=e.install,n=t?e.installMsg:e.cmdMsg;n&&this.refs.pluginsMgrDialog.show(n,this.installUrls,!t)},onOpen:function(e){var t=e.target.getAttribute(\"data-name\"),n=this.props.data,r=n&&n.plugins&&n.plugins[t+\":\"]||{};r.openInModal?b.trigger(\"showPluginOption\",r):this.props.onOpen&&this.props.onOpen(e),e.preventDefault()},syncData:function(e){x.syncData(e)},showDialog:function(){this.refs.pluginRulesDialog.show()},hideDialog:function(){this.refs.pluginRulesDialog.hide()},onShowRules:function(e){var t=this.props.data.plugins[e+\":\"];t.name=e,this.setState({plugin:t},this.showDialog)},showRules:function(e){var t=M(e.target).attr(\"data-name\");this.onShowRules(t)},onCmdChange:function(e){var t=this,n=e.target.value;t.updateCmdMsg(n),t.updateCmdTimer=t.updateCmdTimer||setTimeout(function(){t.updateCmdTimer=null,t.setRegistry(o(n),!1)},1e3)},showMsgDialog:function(){var e=this;e.refs.operatePluginDialog.show(),e.state.install&&setTimeout(function(){j(e.refs.textarea).focus()},600)},updateCmdMsg:function(e,t){this.state.install?this.setState({installMsg:e},t):this.setState({cmdMsg:e},t)},setRegistry:function(e,t){this.setState({registry:e,registryChanged:t!==!1}),I.set(\"pluginsRegistry\",e)},onRegistry:function(e){var t=e.target.value;if(\"+Add\"===t){var n=j(this.refs.textarea),r=[],o=[],i=\"--registry=\";return n.value.trim().split(/\\s+/).forEach(function(e){e.indexOf(i)?r.push(e):o.push(e)}),o.length||o.push(i),void this.updateCmdMsg(r.concat(o).join(\" \"),function(){n.focus()})}this.setRegistry(t)},onShowUpdate:function(e){var t=this,n=M(e.target).attr(\"data-name\"),r=t.props.data.plugins[n+\":\"];x.checkPluginUpdates(r,function(e){return e?T.showService(\"/plugins/store?plugin=\"+encodeURIComponent(n)):void t.showUpdate(r)})},showUpdate:function(e){var t=this;t.getRegistryList(function(n,r){t.setState({cmdMsg:s()+c(e)+l(e),isSys:e.isSys,install:!1,registry:r||e.registry||\"\",registryChanged:!0,registryList:n},t.showMsgDialog)})},showInstall:function(e,t,n){var r=this;r.getRegistryList(function(o,i){var a={install:!n,registryList:o,registry:i||\"\",registryChanged:!0},l=g(e,t);if(l){var c=s(!0)+\" \"+l.plugins;a.cmdMsg=c,a.installMsg=c,l.registry&&(a.registry=l.registry)}r.setState(a,r.showMsgDialog)})},onShowUninstall:function(e){var t=M(e.target).attr(\"data-name\"),n=this.props.data.plugins[t+\":\"];this.showUninstall(n)},showUninstall:function(e){var t=e.moduleName;C.confirm(\"Do you confirm uninstalling the plugin '\"+t+\"'?\",function(t){t&&x.plugins.uninstallPlugins({name:T.getSimplePluginName(e)},function(t,n){return t?t.ec?C.alert((t.em||\"Error\")+\", try again or manually delete the directory:\\n\"+e.path,e.path,\"Copy Directory Path\"):void T.showHandlePluginInfo(t):T.showSystemError(n)})})},enableAllPlugins:function(e){var t=this,n=t.props.data||{};!m&&n.disabledAllPlugins&&C.confirm(\"Do you want to turn on Plugins?\",function(t){t&&i(e)})},setUpdateAllBtnState:function(){b.trigger(\"setUpdateAllBtnState\",this.hasNewPlugin)},showService:function(){T.showService(\"/plugins/store\")},handleLeave:function(){this.setState({copied:!1})},handleCopy:function(){this.setState({copied:!0})},render:function(){var e=this,t=e.state||{},n=e.props.data||{},o=n.plugins||{},i=[],a=n.disabledPlugins||{},l=r(o,i,a,n.disabledAllPlugins),c=t.plugin||{},h=t.install,g=(h?t.installMsg:t.cmdMsg)||\"\",f=t.registryList||[],m=t.registry||\"\",A=n.disabledAllPlugins,M=n.ndp;if(e.hasNewPlugin=!1,e.installUrls=i,t.registryChanged){t.registryChanged=!1;var v=m?\" --registry=\"+m+\"  \":\"\";if(g){var b=z.exec(g),C=b&&b[0];g=g.split(\"\\n\").map(function(e){return e=e.trim(),e=e.split(/\\s+/).filter(function(e){return 0!==e.indexOf(\"--registry\")}).join(\" \"),e&&v&&(e+=v),e}).filter(T.noop).join(\"\\n\"),C&&!m?g+=C:\"w2 install\"===g&&(g+=\" \")}else g=s(!0)+\" \"+(v?v:\"\");t.cmdMsg=g,t.installMsg=g}var N=!B.test(g),I=x.whistleId?H:void 0;return w.createElement(\"div\",{ref:\"plugins\",className:\"fill v-box w-plugins\",style:{display:e.props.hide?\"none\":\"\"}},w.createElement(\"div\",{className:\"w-plugins-headers\"},w.createElement(\"table\",{className:\"table\"},w.createElement(\"thead\",null,w.createElement(\"tr\",null,w.createElement(\"th\",{className:\"w-plugins-order\"},\"#\"),w.createElement(\"th\",{className:\"w-plugins-active\"},\"Active\"),w.createElement(\"th\",{className:\"w-plugins-date\"},\"Date\"),w.createElement(\"th\",{className:\"w-plugins-name\"},\"Name\"),w.createElement(\"th\",{className:\"w-plugins-version\"},\"Version\"),w.createElement(\"th\",{className:\"w-plugins-operation\"},\"Operation\"),w.createElement(\"th\",{className:\"w-plugins-desc\"},\"Description\"))))),w.createElement(\"div\",{className:\"fill w-plugins-list\"},w.createElement(\"table\",{className:\"table table-hover\"},w.createElement(\"tbody\",null,l.length?l.map(function(t,n){var r=T.getSimplePluginName(t),o=!a[r],i=t.openInModal,s=d(t),l=p(t),c=t.homepage,h=T.compareVersion(t.latest,t.version);return h&&(h=\" (NEW: \"+t.latest+\")\",e.hasNewPlugin=!0),w.createElement(\"tr\",{key:r,\"data-name\":r,className:\"w-plugins-item\"+(!A&&o?\"\":\" w-plugins-disable\")+(h?\" w-has-new-version\":\"\")},w.createElement(\"th\",{className:\"w-plugins-order\",onDoubleClick:e.enableAllPlugins},n+1),w.createElement(\"td\",{className:\"w-plugins-active\",onDoubleClick:e.enableAllPlugins},w.createElement(\"input\",{type:\"checkbox\",title:M?\"Not allowed disable plugins\":A?\"Disabled\":(o?\"Disable \":\"Enable \")+r,\"data-name\":r,checked:M||o,disabled:!M&&A,onChange:e.props.onChange,className:M?\"w-not-allowed\":void 0})),w.createElement(\"td\",{className:\"w-plugins-date\"},T.toLocaleString(new Date(t.mtime))),w.createElement(\"td\",{className:\"w-plugins-name\",title:t.moduleName},t.noOpt?w.createElement(\"span\",null,r):w.createElement(\"a\",{href:i?null:l,target:\"_blank\",\"data-name\":r,title:\"Show \"+t.moduleName+\" Option\",onClick:s?null:e.onOpen},r)),w.createElement(\"td\",{className:\"w-plugins-version\"},c?w.createElement(\"a\",{href:c,target:\"_blank\",title:\"Show \"+t.moduleName+\" Help\"},t.version):t.version,h?w.createElement(\"a\",{className:\"w-new-version\",\"data-name\":r,onClick:e.onShowUpdate,title:\"Update \"+t.moduleName},h):null),w.createElement(\"td\",{className:\"w-plugins-operation\"},t.noOpt?w.createElement(\"span\",{className:\"disabled\"},\"Option\"):w.createElement(\"a\",{href:i?null:l,target:\"_blank\",\"data-name\":r,className:\"w-plugin-btn\",onClick:s?null:e.onOpen},\"Option\"),u(t)?w.createElement(\"a\",{draggable:\"false\",\"data-name\":r,onClick:e.showRules},\"Rules\"):w.createElement(\"span\",{className:\"disabled\"},\"Rules\"),t.isProj?w.createElement(\"span\",{className:\"disabled\"},\"Update\"):w.createElement(\"a\",{draggable:\"false\",className:\"w-plugin-btn w-plugin-update-btn\",\"data-name\":r,onClick:e.onShowUpdate},\"Update\"),t.isProj||t.notUn?w.createElement(\"span\",{className:\"disabled\"},\"Uninstall\"):w.createElement(\"a\",{draggable:\"false\",className:\"w-delete\",\"data-name\":r,onClick:e.onShowUninstall},\"Uninstall\"),c?w.createElement(\"a\",{href:c,className:\"w-plugin-btn\",target:\"_blank\"},\"Help\"):w.createElement(\"span\",{className:\"disabled\"},\"Help\"),T.isString(t.rulesUrl)||T.isString(t.valuesUrl)?w.createElement(\"a\",{className:\"w-plugin-btn\",onClick:function(){e.syncData(t)}},\"Sync\"):void 0),w.createElement(\"td\",{className:\"w-plugins-desc\",title:t.description},t.description))}):w.createElement(\"tr\",null,w.createElement(\"td\",{colSpan:\"7\",className:\"w-empty\"},w.createElement(\"a\",{href:\"https://github.com/whistle-plugins\",target:\"_blank\"},\"Empty\")))))),w.createElement(y,{ref:\"pluginRulesDialog\",wstyle:\"w-plugin-rules-dialog\"},w.createElement(\"div\",{className:\"modal-header\"},w.createElement(\"h4\",null,c.name),w.createElement(k,null)),w.createElement(\"div\",{className:\"modal-body\"},w.createElement(\"div\",{className:\"w-plugin-rules\"},c.rules?w.createElement(\"fieldset\",null,w.createElement(\"legend\",null,\"rules.txt\"),w.createElement(\"pre\",null,c.rules)):null,c._rules?w.createElement(\"fieldset\",null,w.createElement(\"legend\",null,\"reqRules.txt (_rules.txt)\"),w.createElement(\"pre\",null,c._rules)):null,c.resRules?w.createElement(\"fieldset\",null,w.createElement(\"legend\",null,\"resRules.txt\"),w.createElement(\"pre\",null,c.resRules)):null)),w.createElement(\"div\",{className:\"modal-footer\"},w.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"))),w.createElement(y,{ref:\"operatePluginDialog\",wstyle:\"w-plugin-update-dialog\"},w.createElement(\"div\",{className:\"modal-body\"},w.createElement(\"h5\",null,h?\"Enter plugin name(s) (space-separated) and click Install:\":\"Update the following plugin(s):\"),w.createElement(\"textarea\",{ref:\"textarea\",value:g||\"\",placeholder:h?\"SUCH AS: whistle.inspect whistle.abc@1.0.0 @scope/whistle.xyz\":void 0,className:\"w-plugin-update-cmd\"+(h?\" w-plugin-install\":\"\"),maxLength:\"600\",onChange:this.onCmdChange,onKeyDown:T.handleTab}),w.createElement(\"a\",{onMouseLeave:this.handleLeave,onClick:this.handleCopy,className:\"w-copy-text-with-tips w-plugin-copy-cmd\"+(t.copied?\" w-copied-text\":\"\"),\"data-clipboard-text\":t.copied?null:g},t.copied?\"Copied\":\"Copy\")),w.createElement(\"div\",{className:\"modal-footer\"},f.length||m?w.createElement(\"label\",{className:\"w-registry-list\"},w.createElement(\"strong\",null,\"--registry=\"),w.createElement(\"select\",{className:\"form-control\",value:m,onChange:this.onRegistry,style:I},w.createElement(\"option\",{value:\"\"},\"None\"),f.map(function(e){return w.createElement(\"option\",{key:e,value:e},e)}),m&&-1===f.indexOf(m)?w.createElement(\"option\",{key:m,value:m},m):null,w.createElement(\"option\",{value:\"+Add\"},\"+Add\"))):w.createElement(\"label\",{\nclassName:\"w-registry-list\"},w.createElement(\"strong\",null,\"--registry=\"),w.createElement(\"select\",{className:\"form-control\",value:m,onChange:this.onRegistry,style:I},w.createElement(\"option\",{value:\"\"},\"None\"),w.createElement(\"option\",{value:\"+Add\"},\"+Add\"))),w.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),x.whistleId?w.createElement(\"button\",{type:\"button\",className:\"btn btn-warning\",\"data-dismiss\":\"modal\",onClick:this.showService},w.createElement(L,{name:\"gift\"}),\"Plugins\"):null,w.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",\"data-dismiss\":\"modal\",onClick:e.execCmd,disabled:N},h?\"Install\":\"Update\"))),w.createElement(E,{ref:\"pluginsMgrDialog\"}))}}),P=w.createClass({displayName:\"Tabs\",componentDidMount:function(){function e(){clearTimeout(n),n=setTimeout(t,60)}function t(){if(!r.props.hide){var e=i.offsetHeight;e&&(o.style.width=i.offsetWidth+\"px\",o.style.height=e+\"px\")}}var n,r=this,o=j(r.refs.tabPanel),i=o.parentNode;r._resizeHandler=e,e(),M(window).on(\"resize\",e)},shouldComponentUpdate:function(e){return!this.props.hide||!e.hide},componentDidUpdate:function(e){e.hide&&!this.props.hide&&this._resizeHandler()},onClose:function(e){this.props.onClose&&this.props.onClose(e),e.stopPropagation()},onContextMenu:function(e){e.preventDefault();var t,n=M(e.target),o=n.closest(\".w-plugins-item\");o.length||(t=n.parent().hasClass(\"active\"),o=n.closest(\".w-plugins-tab\"));var i=o.attr(\"data-name\")||\"\",a=this.props,s=a.plugins[i+\":\"],l=a.disabledPlugins||{},c=!s;this._curPlugin=s;var d;s?d=f(s):\"Plugins\"===i&&(d=r(a.plugins).map(f).join(\"\\n\\n\")),F[0].disabled=!d,F[0].copyText=d,F[1].name=i&&l[i]?\"Enable\":\"Disable\",F[1].disabled=c||a.ndp||a.disabledAllPlugins,F[2].disabled=c||s.noOpt||t,F[3].disabled=c||!u(s),F[4].disabled=c||s.isProj,F[5].disabled=c||s.isProj||s.notUn,F[8].disabled=s&&!s.homepage;var p=F[7];T.addPluginMenus(p,x.getPluginsMenus(),6,c,null,s&&T.getSimplePluginName(s));var h=T.getMenuPosition(e,110,250-(p.hide?0:30));h.list=F,this.refs.contextMenu.show(h)},onClickContextMenu:function(e,t,n,o){var i=this._curPlugin,a=this.props;switch(n||e){case\"Option\":return b.trigger(\"openPluginOption\",i);case\"Rules\":return b.trigger(\"showPluginRules\",i);case\"Disable\":return b.trigger(\"disablePlugin\",[i,!0]);case\"Enable\":return b.trigger(\"disablePlugin\",[i,!1]);case\"Update\":return b.trigger(\"showInstallPlugin\",i);case\"Uninstall\":return b.trigger(\"showUninstallPlugin\",i);case\"Help\":var s=i&&i.homepage;return window.open(s||T.getDocUrl(\"extensions/usage.html\"));case\"Install\":return b.trigger(\"showInstallPlugins\");case\"Plugins\":return void S.fork(e,{port:x.getPort(),type:\"plugins\",name:o,plugin:i,plugins:r(a.plugins),getRegistryList:this.getRegistryList})}},render:function(){var e=this,t=e.props,n=t.tabs||[],r=\"Home\",o=t.disabledAllPlugins,a=e.props.active;if(a&&a!=r)for(var s=0,l=n.length;l>s;s++){var c=n[s];if(c.name==a){r=c.name;break}}return w.createElement(\"div\",{className:\"w-nav-tabs fill v-box\",style:{display:e.props.hide?\"none\":\"\",paddingTop:o?0:void 0},onContextMenu:e.onContextMenu},o?w.createElement(\"div\",{className:\"w-record-status\",style:{marginBottom:5}},\"All plugins are currently disabled\",w.createElement(\"button\",{className:\"btn btn-primary\",onClick:i},\"Enable\")):null,w.createElement(\"ul\",{className:\"nav nav-tabs\"},w.createElement(\"li\",{className:\"w-nav-normal-tab\"+(\"Home\"==r?\" active\":\"\"),\"data-name\":\"Home\",onClick:e.props.onActive},w.createElement(\"a\",{draggable:\"false\",\"data-name\":\"Plugins\",className:\"w-plugins-tab\"},w.createElement(L,{name:\"th-large\"}),\"Plugins\")),n.map(function(n){var o,i;return T.pluginIsDisabled(t,n.name)?o=!0:i=T.getPluginIcon(x.getPlugin(n.name+\":\")),w.createElement(\"li\",{key:n.name,className:r==n.name?\" active\":\"\"},w.createElement(\"a\",{\"data-name\":n.name,title:n.name,onClick:e.props.onActive,draggable:\"false\",className:\"w-plugins-tab\"+(o?\" w-plugin-tab-disabled\":\"\")},o?w.createElement(L,{\"data-name\":n.name,name:\"ban-circle\"}):i?w.createElement(\"img\",{src:i}):void 0,n.name,w.createElement(\"span\",{\"data-name\":n.name,className:\"w-close-icon\",onClick:e.onClose},\"×\")))})),w.createElement(\"div\",{className:\"fill v-box w-nav-tab-panel w-fix-drag\"},w.createElement(\"div\",{ref:\"tabPanel\",className:\"fill v-box\"},w.createElement(V,{data:e.props,hide:\"Home\"!=r,onChange:e.props.onChange,onOpen:e.props.onOpen}),n.map(function(e){return w.createElement(N,{key:e.name,inited:r==e.name},w.createElement(\"iframe\",{style:{display:r==e.name?\"\":\"none\"},src:e.url,onLoad:x.handleIframeLoad}))}))),w.createElement(D,{onClick:this.onClickContextMenu,ref:\"contextMenu\"}))}});e.exports=P},function(e,t,n){var r=n(617);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-nav-tabs{font-size:13px}.w-nav-tabs>.nav-tabs{padding-left:2px;padding-top:5px;background:var(--b-bar)}.w-nav-tabs>.nav>li>a{padding:5px 20px 5px 10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:10pc;min-width:60px;min-height:30px;position:relative;display:flex;align-items:center}.w-nav-tabs>.nav>li>a>.w-close-icon{display:block;position:absolute;top:5px;right:5px;color:var(--c-gray);cursor:pointer}.w-nav-tabs>.nav>li>a>.w-close-icon:hover{color:var(--c-default)}.w-plugin-tab-disabled{color:var(--c-disabled)!important}.w-plugin-tab-disabled .glyphicon-ban-circle{vertical-align:text-top;margin-right:3px;color:var(--c-disabled)!important}.w-custom-tab-btn{display:inline-flex;align-items:center}.w-custom-tab-btn img,.w-plugins-tab img{width:13px;max-height:13px;margin:0 3px 0 0}.w-nav-tabs iframe{border:none;display:block;background-color:var(--b-default);margin:0;padding:0;width:100%;height:100%}.w-nav-tabs .w-nav-tab-panel,.w-nav-tabs .w-nav-tab-panel>div{overflow:hidden}.w-nav-tabs>.nav>.w-nav-normal-tab>a{padding-right:10px!important}.w-nav-tabs>.nav>.w-nav-normal-tab>a span{margin-right:5px;top:0}.w-nav-tabs>.nav>.w-nav-normal-tab>a .glyphicon-cloud{top:2px}.w-plugins td,.w-plugins th{font-size:13px;font-weight:400}.w-plugins-headers{border-bottom:1px solid var(--c-border);overflow:hidden}.w-plugins-headers .table th{border:none!important;padding:6px;color:var(--c-default);background:var(--b-bar);font-weight:700}.w-plugins-list{overflow-y:auto;overflow-x:hidden;outline:0}.w-plugins-list td,.w-plugins-list th{border-top:none!important;border-bottom:1px solid var(--c-border);padding:8px 6px!important;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.w-plugins-disable,.w-plugins-disable a,.w-plugins-disable td{color:var(--c-disabled)}.w-plugins-order{width:50px}.w-plugins-active{width:60px}.w-plugins-list td.w-plugins-active input{margin-left:9pt;vertical-align:text-bottom}.w-plugins-name{width:10pc}.w-plugins-version{width:150px}.w-about-dialog .w-new-version,.w-has-new-version .w-plugin-update-btn,.w-has-new-version .w-plugins-version .w-new-version,.w-plugins-menu.w-plugin-update-btn{color:var(--c-risk)!important}.w-about-dialog .w-new-version{margin-left:5px}.w-about-dialog .w-new-version:hover,.w-has-new-version .w-plugin-update-btn:hover,.w-has-new-version .w-plugins-version .w-new-version:hover,.w-plugins-menu.w-plugin-update-btn:hover{color:var(--c-risk)!important}.w-plugins-date{width:190px}.w-plugins-operation{width:360px}.w-plugins-operation a,.w-plugins-operation span.disabled{margin-right:20px}.w-plugins-operation a:last-child,.w-plugins-operation span.disabled:last-child{margin-right:0}.w-plugins-operation span.disabled{color:var(--c-disabled)}.w-plugin-rules-dialog .modal-dialog{width:680px}.w-plugin-rules-dialog .modal-dialog pre{max-height:230px;overflow:auto;padding:10px}.w-plugin-rules-dialog fieldset{border:1px solid var(--c-border)}.w-plugin-rules-dialog fieldset legend{font-size:13px;padding:0 10px;border:none;width:auto;margin:0 10px;font-weight:700}.w-plugin-rules-dialog fieldset:nth-child(2){margin-top:15px}.w-plugin-rules-dialog .modal-header h4{margin:0}.w-plugin-rules-dialog .modal-header .close{position:absolute;right:10px;top:1pc}.w-plugin-update-dialog{z-index:var(--z-modal-max)}.w-plugin-update-dialog .modal-dialog{width:600px}.w-plugin-update-cmd{background:var(--b-editor);color:var(--c-editor);padding:10px;margin-top:10px;font-size:14px;display:block;width:578px;height:220px;border-radius:3px}.w-plugin-install{padding:5px}.w-plugin-btn{color:var(--c-link)!important}.w-plugin-btn:hover{color:var(--c-link-hover)!important}.w-plugin-update-dialog .modal-footer{white-space:nowrap}.w-registry-list{margin-right:15px}.w-registry-list select{border-radius:0;display:inline-block;font-size:9pt;line-height:28px;height:30px;padding:0 5px;width:350px}.w-plugin-copy-cmd{display:inline-block;position:absolute;z-index:1;text-decoration:none!important;color:var(--c-heavy);padding:0 6px;background:var(--b-heavy);font-size:9pt;top:28px;right:10px;border-top-left-radius:3px;border-top-right-radius:3px}.w-copied-text{color:var(--c-disabled)!important;cursor:not-allowed}.w-plugin-copy-cmd:hover{color:var(--c-default)}\",\"\"])},function(e,t,n){\"use strict\";function r(e){if(e&&\"string\"==typeof e){e=e.trim().split(d);for(var t=0,n=e.length;n>t;t++){var r=e[t].trim();if(u.test(r))return r.substring(11,1035)}}}n(619);var o=n(24),i=n(198),a=n(200),s=n(202),l=n(210),c=n(214),u=/^--registry=https?:\\/\\/[^/?]/,d=/\\s*[|,;\\s]+\\s*/,p=o.createClass({displayName:\"PluginsMgr\",getInitialState:function(){return{list:[]}},handleCallback:function(e,t){if(s.showHandlePluginInfo(e,t)){var n=r(this._cmd);n&&a.plugins.addRegistry({registry:n},function(e){e&&0===e.ec&&l.set(\"pluginsRegistry\",n)})}},installPlugin:function(){a.plugins.installPlugins({cmd:this._cmd},this.handleCallback)},installPluginExt:function(e){var t=a.createCgi(s.getPluginCgiUrl(e.moduleName,e.installUrl),!1,!0);t({cmd:this._cmd},this.handleCallback)},show:function(e,t,n){var r=this;t=t||[];var o=t.length;return r._cmd=e,r._hideDialog=!1,o?void r.setState({isUpdate:n,list:t},function(){r.refs.pluginsMgr.show()}):r.installPlugin()},hide:function(){this.refs.pluginsMgr.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},render:function(){var e=this,t=e.state,n=t.list||[],r=t.isUpdate,a=r?\"Update\":\"Install\";return o.createElement(i,{ref:\"pluginsMgr\",wstyle:\"w-plugins-mgr-dialog\"},o.createElement(\"div\",{className:\"modal-header\"},o.createElement(\"h4\",null,\"Select \",r?\"Updater\":\"Installer\"),o.createElement(c,null)),o.createElement(\"div\",{className:\"modal-body\"},o.createElement(\"div\",{className:\"btn btn-primary plugin-mgr-btn\",\"data-dismiss\":\"modal\",onClick:e.installPlugin},a,\" \",o.createElement(\"span\",null,\"(Use Default)\")),n.map(function(t){return o.createElement(\"div\",{className:\"btn btn-default plugin-mgr-btn\",\"data-dismiss\":\"modal\",key:t.moduleName,onClick:e.installPluginExt.bind(e,t)},a,\" \",o.createElement(\"span\",null,\"(Use plugin \",s.getSimplePluginName(t.moduleName),\")\"))})),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))}});e.exports=p},function(e,t,n){var r=n(620);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-plugins-mgr-dialog{z-index:var(--z-modal-max)}.w-plugins-mgr-dialog .modal-content{width:380px}.w-plugins-mgr-dialog .plugin-mgr-btn{display:block;line-height:1.5;width:20pc;margin:20px auto;padding:15px 10px;text-align:center;cursor:pointer;font-size:15px;overflow:hidden;text-overflow:ellipsis;font-weight:500}.w-plugins-mgr-dialog .plugin-mgr-btn span{opacity:.66;font-weight:400}\",\"\"])},function(e,t,n){\"use strict\";n(622);var r=n(24),o=n(57),i=n(202),a=n(198),s=n(624),l=n(625),c=n(18),u=n(334),d=n(200),p=n(626),h=n(213),g=n(214),f=o.findDOMNode,m=/(?:^|\\s)(?:[\\w-]+:\\/\\/)?temp\\/([\\da-z]{64})(?:\\.[\\w-]+)?(?:$|\\s)/gm,A=r.createClass({displayName:\"ListDialog\",getInitialState:function(){return{checkedItems:{},checkedRuleList:[],ruleListLen:0,filename:\"\",tabs:[{icon:\"file\",name:\"Rules Files\",active:!0},{icon:\"list\",name:\"Rules Items\"}]}},shouldComponentUpdate:function(){return this.refs.dialog.isVisible()},onChange:function(e){var t=e.target,n=t.parentNode.title,r=this.state.checkedItems;t.checked?r[n]=1:delete r[n],this.setState({checkedItems:r})},donwload:function(e){var t=f(this.refs.filename),n=f(this.refs.exportData);f(this.refs.exportName).value=t.value.trim(),f(this.refs.data).value=JSON.stringify(e),n.submit(),t.value=\"\"},filterFilename:function(e){this.setState({filename:i.formatFilename(e.target.value)})},getInputValue:function(){return i.formatFilename(f(this.refs.filename).value.trim())},getRuleList:function(e){var t=this.state.rulesModal,n={},r=this.state.checkedRuleList,o=[];t.list.forEach(function(e,t){t&&e.checked&&(n=c.extend({},e.rawValues,n)),e.rules.forEach(function(e){o.push(e.join(\" \"))})});var a=t.list[0];a&&a.checked&&(n=c.extend({},a.rawValues,n));var s=r.filter(function(e){return-1!==o.indexOf(e)}).join(\"\\n\"),l=[],u=[];s.replace(/\\{([^\\s}]+)\\}/g,function(e,t){var r=n[t];-1===l.indexOf(r)&&l.push(r)}).replace(m,function(e,t){-1===u.indexOf(t)&&u.length<11&&u.push(t)}),l=l.join(\"\\n\"),l&&(s+=\"\\n\\n\"+l);var p,h=this.getInputValue()||\"mock_\"+i.formatDate()+\".txt\",g=function(){e([s,p||{}],h)};u.length?d.getTempFile({files:u.join()},function(e){var t=e&&e.list;t&&t.length&&(p={isFile:!0,base64:t.shift(),list:t}),g()}):g()},exportRuleList:function(){this.getRuleList(function(e,t){i.download(e,t)})},isRuleList:function(){return this.state.tabs[1].active},onConfirm:function(e){if(!e.target.disabled){if(this.refs.dialog.hide(),this.isRuleList())return this.exportRuleList();var t=this.state.checkedItems;this.donwload(t)}},getExportData:function(e){return this.isRuleList()?this.getRuleList(e):void e(this.state.checkedItems)},getAllItems:function(){var e=this.props.list||[],t={};return e.forEach(function(e){t[e]=1}),t},show:function(e){var t=this,n=t.props,r=n.list||[],o=t.getRulesModal();if(t.refs.dialog.show(),e){e&&\"string\"==typeof e&&(e=[e]);var i={};Array.isArray(e)&&e.forEach(function(e){-1!==r.indexOf(e)&&(i[e]=1)}),t.setState({checkedItems:i})}if(!this.props.onConfirm&&setTimeout(function(){var e=f(t.refs.filename);e.focus(),e.select()},500),o){var a=0,s={},l=t.state.rulesModal;r=o.list.map(function(e){var t=o.get(e),n=u(t.value)||{rules:[],values:{},rawValues:{}},r=l&&l.map[e];return n.name=e,n.checked=r&&r.checked,s[e]=n,a+=n.rules.length,n}),this.setState({rulesModal:{list:r,map:s},ruleListLen:a})}},hide:function(){this.refs.dialog.hide()},preventDefault:function(e){e.preventDefault()},onTabChange:function(e){var t=this,n=t.state.tabs;n.forEach(function(e){e.active=!1}),e.active=!0,t.setState({tabs:n})},getRulesModal:function(){var e=this.props;return\"rules\"===e.name&&e.modal},onCheckedRuleChange:function(e){this.setState({checkedRuleList:e})},checkAll:function(e){var t=this.props.list;if(t&&t.length){var n={};e.target.checked&&t.forEach(function(e){n[e]=1}),this.setState({checkedItems:n})}},checkItem:function(e,t){t.checked=e.target.checked,this.setState({})},onShare:function(e){e||(this.refs.dialog.hide(),f(this.refs.filename).value=\"\")},render:function(){var e=this,t=e.state,n=e.props,o=t.tabs,c=t.ruleListLen,u=t.rulesModal,d=n.list||[],f=d.length,m=t.checkedItems,A=Object.keys(m),M=u&&o[1].active?t.checkedRuleList.length:A.length,w=n.name,v=M?n.tips:null,b=n.onConfirm,y=n.isRules,x=f&&d.every(function(e){return-1!==A.indexOf(e)});return r.createElement(a,{ref:\"dialog\",wclassName:\"w-list-dialog\"},n.title?r.createElement(\"div\",{className:\"modal-header\"},r.createElement(\"h4\",null,n.title),r.createElement(g,null)):null,r.createElement(\"div\",{className:\"modal-body\"},n.title?null:r.createElement(g,null),u?r.createElement(s,{tabs:o,onChange:e.onTabChange}):null,r.createElement(\"div\",{className:o[0].active?\"\":\" hide\",style:{marginTop:10}},r.createElement(\"div\",{className:\"w-list-wrapper\"},d.map(function(t,n){return n||!y?r.createElement(\"label\",{title:t,key:t},r.createElement(\"input\",{onChange:e.onChange,type:\"checkbox\",checked:!!m[t]}),i.isGroup(t)?r.createElement(h,{name:\"triangle-right\",className:\"w-list-group-icon\"}):null,t):void 0})),v?r.createElement(\"h5\",{className:\"w-list-tips-title\"},v):null),u?r.createElement(l,{modal:u,hide:!o[1].active,onChange:e.onCheckedRuleChange}):null,v?r.createElement(\"div\",{className:\"w-list-tips\"},A.map(function(e){return r.createElement(\"span\",{key:e},i.isGroup(e)?r.createElement(h,{name:\"triangle-right\",className:\"w-list-group-icon\"}):null,e)})):b&&!u?null:r.createElement(\"p\",{style:{marginTop:10,whiteSpace:\"nowrap\"}},\"Filename:\",r.createElement(\"input\",{ref:\"filename\",value:t.filename,onChange:e.filterFilename,style:{width:812,display:\"inline-block\",marginLeft:5},className:\"form-control\",placeholder:\"Enter filename (optional)\"}))),r.createElement(\"div\",{className:\"modal-footer\"},b?null:r.createElement(\"label\",{className:\"w-kv-check-all\"+(o[1].active?\" hide\":\"\")},r.createElement(\"input\",{type:\"checkbox\",checked:x,onChange:this.checkAll,disabled:!f}),\"Select all\"),r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),b?null:r.createElement(p,{getFilename:this.getInputValue,disabled:!M,type:this.isRuleList()?\"mock\":n.name,getData:this.getExportData,onComplete:this.onShare}),r.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",disabled:!M,onMouseDown:this.preventDefault,onClick:b?function(){b(A)}:this.onConfirm},b?\"Confirm\":\"Export (\"+M+\" / \"+(o[1].active?c:f)+\")\")),r.createElement(\"form\",{action:\"cgi-bin/\"+w+\"/export\",ref:\"exportData\",style:{display:\"none\"},target:\"downloadTargetFrame\"},r.createElement(\"input\",{ref:\"exportName\",type:\"hidden\",name:\"filename\"}),r.createElement(\"input\",{ref:\"data\",type:\"hidden\",name:w})))}});e.exports=A},function(e,t,n){var r=n(623);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-list-wrapper label{line-height:26px;width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:10px}.w-list-wrapper label input{margin-right:5px}.w-list-dialog{width:900px}.w-list-dialog .w-list-wrapper{max-height:420px;overflow-x:hidden;overflow-y:auto}.w-list-dialog .w-list-tips{max-height:90pt;overflow-x:hidden;overflow-y:auto}.w-list-tips>span{display:inline-block;background:var(--b-heavy);padding:3px 6px;border-radius:3px;margin:0 0 10px 10px}.w-list-tips-title{color:var(--c-error)}.w-list-dialog .modal-footer{position:relative}.w-list-counter{position:absolute;left:10px;top:15px}.w-rule-list-name.w-list-data{overflow-x:hidden;overflow-y:auto;max-height:420px}.w-rule-list-ctn{max-height:420px;overflow:auto;color:var(--c-default)}.w-rule-list-ctn .w-rule-list-item{font-weight:400;height:2pc;display:flex;align-items:center;white-space:nowrap;margin-bottom:0;border-bottom:1px solid var(--c-border)}.w-rule-list-ctn .w-rule-list-item span{display:inline-block}.w-rule-list-ctn .w-rule-list-item>span{width:2pc;height:100%;display:flex;align-items:center;justify-content:center;background:var(--b-title);flex-shrink:0}.w-rule-list-ctn .w-rule-list-item>div{display:flex;align-items:center;height:100%;padding:0 10px;flex:1;flex-shrink:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;background-color:var(--b-default)}.w-rule-list-ctn .w-rule-list-item>div::-webkit-scrollbar{display:none}.w-rule-list-ctn .w-rule-list-item>div span{margin-right:10px;color:var(--c-rule-filter)}.w-rule-list-ctn .w-rule-list-item>div span:nth-child(1){color:var(--c-default)}.w-rule-list-ctn .w-rule-list-item>div span:nth-child(2){color:var(--c-rule-props)}.w-rule-list-ctn .w-rule-list-item>div span:last-child{margin-right:0}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(213),i=r.createClass({displayName:\"Tabs\",render:function(){var e=this.props.tabs||[],t=this.props.onChange;return r.createElement(\"ul\",{className:\"nav nav-tabs w-tabs\"},e.map(function(e,n){var i=function(){!e.active&&t&&t(e)};return r.createElement(\"li\",{key:n,className:e.active?\"active\":\"\"},r.createElement(\"a\",{draggable:\"false\",onClick:i},e.icon?r.createElement(o,{name:e.icon}):null,e.name))}))}});e.exports=i},function(e,t,n){\"use strict\";var r=n(24),o=n(220),i=n(202),a=r.createClass({displayName:\"RuleList\",getInitialState:function(){return{active:\"Default\",checkedList:[]}},shouldComponentUpdate:i.shouldComponentUpdate,onClick:function(e){this.setState({active:e.target.getAttribute(\"data-name\")})},onChange:function(e,t){var n=this.state.checkedList,r=n.indexOf(t);e.target.checked?-1===r&&n.push(t):-1!==r&&n.splice(r,1),this.setState({checkedList:n}),this.props.onChange(n)},render:function(){var e=this,t=e.props,n=e.state,a=n.active,s=t.modal,l=n.checkedList,c=s.list,u=s.map[a];return u||(a=c[0],u=s.map[a]),r.createElement(o,{leftWidth:\"220\",className:t.hide?\" hide\":\"\"},r.createElement(\"div\",{className:\"w-list-data w-rule-list-name\"},c.map(function(t){if(i.isGroup(t.name))return null;var n=0;return t.checked=!1,t.rules.forEach(function(e){-1!==l.indexOf(e.join(\" \"))&&(n++,t.checked=!0)}),r.createElement(\"a\",{tabIndex:\"0\",key:t.name,draggable:\"false\",\"data-name\":t.name,className:(a===t.name?\"w-active\":\"\")+(n?\" w-bold\":\"\"),onClick:e.onClick},t.name+(n?\" (\"+n+\")\":\"\"))})),r.createElement(\"div\",{className:\"fill w-rule-list-ctn\"},u&&u.rules.map(function(t){var n=t.join(\" \"),o=-1!==l.indexOf(n);return r.createElement(\"label\",{key:n,className:\"w-rule-list-item\"+(o?\" w-bold\":\"\")},r.createElement(\"span\",null,r.createElement(\"input\",{type:\"checkbox\",checked:o,onChange:function(r){e.onChange(r,n,t,u.rawValues)}})),r.createElement(\"div\",null,t.map(function(e,t){return r.createElement(\"span\",{key:t},e)})))})))}});e.exports=a},function(e,t,n){\"use strict\";var r=n(24),o=n(200),i=n(206),a=n(202),s=n(213),l=r.createClass({displayName:\"ShareBtn\",save:function(){var e=this.props,t=e.getData;if(\"function\"==typeof t)return t(this.handleData);var n=e.data;\"function\"==typeof n&&(n=n()),this.handleData(n)},handleData:function(e){var t=this.props,n=t.onComplete,r=t.type+\"Share\";o.saveToService({type:r,filename:\"function\"==typeof t.getFilename?t.getFilename():t.filename,data:e,isShare:!0},function(e){var t=!e||0!==e.ec;\"function\"==typeof n&&n(t,e),t?i.error(e&&e.em||\"Sharing failed\"):(i.success(\"Shared successfully\"),a.showService(r,!0))})},render:function(){return o.whistleId?r.createElement(\"button\",{onClick:this.save,className:\"btn btn-warning w-save-to-service-btn\",draggable:\"false\",disabled:this.props.disabled},r.createElement(s,{name:\"cloud\"}),\"Share Via URL\"):null}});e.exports=l},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(18),a=n(200),s=n(199),l=n(202),c=n(213),u=r.createClass({displayName:\"FilterBtn\",getInitialState:function(){return{hasFilterText:!!a.filterIsEnabled()}},componentDidMount:function(){var e=this;s.on(\"filterChanged\",function(){var t=!!a.filterIsEnabled();t!==e.state.hasFilterText&&e.setState({hasFilterText:!!a.filterIsEnabled()})}),s.on(\"shakeSettings\",function(){setTimeout(function(){l.shakeElem(i(o.findDOMNode(e.refs.settings)))},100)})},render:function(){var e=this.props,t=e.hide,n=e.isNetwork,o=e.backRulesFirst||(n&&this.state.hasFilterText?\" w-menu-enable\":\"\");return r.createElement(\"a\",{ref:\"settings\",onClick:e.onClick,className:\"w-settings-menu\"+o,style:{display:t?\"none\":\"\"},draggable:\"false\"},r.createElement(c,{name:\"cog\"}),\"Settings\")}});e.exports=u},function(e,t,n){\"use strict\";var r=n(24),o=n(199),i=n(213),a=r.createClass({displayName:\"UpdateAllBtn\",getInitialState:function(){return{disabled:!0}},componentDidMount:function(){var e=this;o.on(\"setUpdateAllBtnState\",function(t,n){e.setState({disabled:!n})})},updateAllPlugins:function(){!this.state.disabled&&o.trigger(\"updateAllPlugins\")},render:function(){var e=this.state.disabled||this.props.hide;return r.createElement(\"a\",{onClick:this.updateAllPlugins,className:\"w-plugins-menu w-plugin-update-btn\"+(e?\" hide\":\"\"),draggable:\"false\"},r.createElement(i,{name:\"refresh\"}),\"Update\")}});e.exports=a},function(e,t,n){\"use strict\";function r(e){e=e||c.get(\"previewRulesName\");var t=s.getRulesModal();if(e&&t.getItem(e))return e;for(var n=t.getSelectedNames(),r=0,o=n.length;o>r;r++)if(e=n[r],\"Default\"!==e)return e;return\"Default\"}var o=n(24),i=n(57),a=n(198),s=n(200),l=n(223),c=n(210),u=n(199),d=n(202),p=n(209),h=n(214),g=/\\btemp\\/current_file_hash_placeholder\\b/,f=/\\btemp\\/current_file_hash_placeholder\\b/g,m=/^(?:[^\\n\\r\\S]*(```+)[^\\n\\r\\S]*(\\S+)[^\\n\\r\\S]*[\\r\\n]([\\s\\S]+?)[\\r\\n][^\\n\\r\\S]*\\1\\s*|[^\\r\\n]*)$/gm,A=o.createClass({displayName:\"RulesDialog\",getInitialState:function(){return{rulesName:r(),newRulesName:\"\"}},show:function(e,t){this._rules=e,this._values=t,this._hasChanged=!1,this.setValue(),this.refs.rulesDialog.show()},onRulesChange:function(e){var t=e.target.value,n=this;return t?this._hasChanged?void p.confirm(\"Unsaved changes will be lost. Continue?\",function(e){e&&(n._hasChanged=!1,n.setValue(t,!0))}):n.setValue(t,!0):void n.showCreateRules()},showCreateRules:function(){this.refs.createRules.show();var e=i.findDOMNode(this.refs.rulesName);setTimeout(function(){e.select(),e.focus()},300)},onRulesValueChange:function(e){this._hasChanged=!0,this.setState({rulesValue:e.getValue()})},onNewNameChange:function(e){var t=e.target.value.replace(/\\s+/g,\"\");this.setState({newRulesName:t})},createRules:function(){var e=this,t=e.state.newRulesName;s.rules.add({name:t,value:\"\"},function(n,r){n&&0===n.ec?(u.trigger(\"addNewRulesFile\",{filename:t,data:\"\"}),e.refs.createRules.hide(),e.setState({newRulesName:\"\",rulesName:t}),e.setValue(t,!0)):d.showSystemError(r)})},createTempFile:function(e){var t=this,n=t.state,r=t._values,o=n.rulesValue;if(!r||!r.isFile)return t.saveValue(e);var i,a=function(t,n,r){s.createTempFile(JSON.stringify({clientId:s.getPageId(),value:n,base64:t}),function(t,n){t&&0===t.ec?r&&e(g.test(o)?t.filepath:null):i||(i=!0,d.showSystemError(n))})};a(r.base64,r.value,!0);var l=r.list;l=Array.isArray(l)?l.slice(0,10):[],l.forEach(function(e){a(e)})},saveValue:function(e){var t=this,n=t._values,r=n&&n.name,o=n&&n.value;if(!r||n.isFile||!d.isString(r)||!d.isString(o))return e();var i=function(t){t&&s.values.add({name:r,value:o},function(t,n){t&&0===t.ec?(u.trigger(\"addNewValuesFile\",{filename:r,data:o}),e()):d.showSystemError(n)})},a=s.getValuesModal().getItem(r);return a&&a.value!==o?p.confirm(\"The name `\"+r+\"` is already in use. Overwrite?\",i):void i(!0)},save:function(){var e=this,t=e.state,n=t.rulesValue,r=t.rulesName;e.createTempFile(function(t){var o;if(t){o=null,n=n.replace(f,t);var i=(e._rules||\"\").replace(f,t);if(i){var a;n=n.replace(m,function(e,t,n){return n?e:e===i||e.trim().split(/\\s+/).join(\" \")===i?(a=!0,\"\"):e}),a&&(n=s.backRulesFirst?n.replace(/\\s+$/,\"\")+\"\\n\\n\"+i+\"\\n\":i+\"\\n\\n\"+n.replace(/^\\s+/,\"\"))}}else o=e._values;s.addRulesAndValues(JSON.stringify({clientId:s.getPageId(),rules:{name:r,value:n},values:o}),function(t,i){t&&0===t.ec?(u.trigger(\"addNewRulesFile\",{filename:r,data:n}),u.trigger(\"addMockRulesSuccess\"),o&&u.trigger(\"addNewValuesFile\",{filename:o.name,data:o.value}),e.refs.rulesDialog.hide(),u.trigger(\"hideMockDialog\")):d.showSystemError(i)})})},setValue:function(e,t){e?c.set(\"previewRulesName\",e):e=r();var n=s.getRulesModal(),o=n.getItem(e),i=o&&o.value||\"\",a=i?\"\\n\\n\":\"\\n\",l=this._rules;-1===i.indexOf(l)||/[\\r\\n]/.test(l)||(i=-1===i.indexOf(\"```\")?i.split(/\\r\\n|\\r|\\n/).map(function(e){return e.trim().split(/\\s+/).join(\" \")===l?\"\":e}).join(\"\\n\"):i.replace(m,function(e,t,n){return n?e:e.trim().split(/\\s+/).join(\" \")===l?\"\":e})),i=s.backRulesFirst?i.replace(/\\s+$/,\"\")+a+l+\"\\n\":l+a+i.replace(/^\\s+/,\"\");var u=this,d=this.refs.editor,p=function(){s.backRulesFirst&&d._editor.scrollTo(0,1e7),u.setState({rulesValue:i})};t?setTimeout(p,100):setTimeout(p,360),u.setState({rulesName:r(e)})},render:function(){var e=s.getRulesModal(),t=this.state,n=t.newRulesName,r=e.list;return o.createElement(a,{ref:\"rulesDialog\",wstyle:\"w-rules-dialog\"},o.createElement(\"div\",{className:\"modal-body\"},o.createElement(h,null),o.createElement(\"div\",{className:\"modal-title\"},\"Select Rules File:\",o.createElement(\"select\",{className:\"form-control\",onChange:this.onRulesChange,value:t.rulesName},r.map(function(e){return o.createElement(\"option\",{key:e,value:e},e)}),o.createElement(\"option\",{value:\"\"},\"+Create\")),o.createElement(\"button\",{className:\"btn btn-default\",onClick:this.showCreateRules},\"+Create\")),o.createElement(l,{value:t.rulesValue,onChange:this.onRulesValueChange,ref:\"editor\",mode:\"rules\",theme:c.get(\"rulesTheme\")||\"cobalt\",fontSize:c.get(\"rulesFontSize\")||\"14px\",lineNumbers:\"true\"===c.get(\"showRulesLineNumbers\"),lineWrapping:!!c.get(\"autoRulesLineWrapping\")})),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.save},\"Save\")),o.createElement(a,{ref:\"createRules\",wstyle:\"w-create-rules-dialog\"},o.createElement(\"div\",{className:\"modal-body\"},\"New Rules Filename:\",o.createElement(\"input\",{ref:\"rulesName\",style:{marginTop:6},className:\"form-control\",maxLength:\"64\",onChange:this.onNewNameChange,value:n,placeholder:\"Enter name\"})),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.createRules,disabled:!n||-1!==r.indexOf(n)},\"Confirm\"))))}}),M=o.createClass({displayName:\"MockDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e,t){this.refs.rulesDialog.show(e,t)},render:function(){return o.createElement(A,{ref:\"rulesDialog\"})}});e.exports=M},function(e,t,n){\"use strict\";n(631);var r=n(24),o=n(198),i=n(202),a=n(200),s=n(633),l=n(213),c=function(e){return e&&Date.now()-e>800},u=function(e,t){return t?e+(-1===e.indexOf(\"?\")?\"?\":\"&\")+\"history=\"+encodeURIComponent(t):e},d=r.createClass({displayName:\"SyncDialog\",getInitialState:function(){return{}},show:function(e,t,n,r){var o=this;o.rulesModal=t,o.valuesModal=n,o.plugin=e,i.isString(e.rulesUrl)||(e.rulesUrl=null),i.isString(e.valuesUrl)||(e.valuesUrl=null),(e.rulesUrl||e.valuesUrl)&&o.setState(e,\"function\"==typeof r?r:function(){o.refs.syncDialog.show()})},_syncRules:function(e){var t=this,n=t.state.rulesUrl;if(!t.loadingRules&&i.isString(n)){t.loadingRules=Date.now()||1,n=u(n,e);var r=a.createCgi(i.getPluginCgiUrl(t.state.moduleName,n));r(function(n,r){return t.loadingRules=!1,t.setState({}),n?(t.plugin.selectedRulesHistory=e,void t.refs.kvDialog.show(n,t.rulesModal,t.valuesModal,!1,e)):i.showSystemError(r)}),t.setState({})}},_syncValues:function(e){var t=this,n=t.state.valuesUrl;if(!t.loadingValues&&i.isString(n)){t.loadingValues=Date.now()||1,n=u(n,e);var r=a.createCgi(i.getPluginCgiUrl(t.state.moduleName,n));r(function(n,r){return t.loadingValues=!1,t.setState({}),n?(t.plugin.selectedValuesHistory=e,void t.refs.kvDialog.show(n,t.rulesModal,t.valuesModal,!0,e)):i.showSystemError(r)}),t.setState({})}},showKVDialog:function(e,t,n,r){this.refs.kvDialog.show(e,t,n,r)},syncRules:function(){this._syncRules(this.plugin.selectedRulesHistory)},syncValues:function(){this._syncValues(this.plugin.selectedValuesHistory)},onHistoryChange:function(e,t){t?this._syncValues(e):this._syncRules(e)},render:function(){var e=this.state,t=this.loadingRules,n=this.loadingValues;return r.createElement(o,{ref:\"syncDialog\",wstyle:\"w-sync-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(\"button\",{onClick:this.syncRules,disabled:t||!i.isString(e.rulesUrl),type:\"button\",className:\"btn btn-primary\"},r.createElement(l,{name:\"list\"}),\" \",c(t)?\"Loading\":\"Sync\",\" Rules\"),r.createElement(\"button\",{onClick:this.syncValues,disabled:n||!i.isString(e.valuesUrl),type:\"button\",className:\"btn btn-default\"},r.createElement(l,{name:\"folder-close\"}),\" \",c(n)?\"Loading\":\"Sync\",\" Values\")),r.createElement(\"div\",{className:\"modal-footer\"\n},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")),r.createElement(s,{onHistoryChange:this.onHistoryChange,ref:\"kvDialog\"}))}}),p=r.createClass({displayName:\"SyncDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e,t,n,r){this.refs.syncDialog.show(e,t,n,r)},syncRules:function(){this.refs.syncDialog.syncRules()},syncValues:function(){this.refs.syncDialog.syncValues()},showKVDialog:function(e,t,n,r){this.refs.syncDialog.showKVDialog(e,t,n,r)},render:function(){return r.createElement(d,{ref:\"syncDialog\"})}});e.exports=p},function(e,t,n){var r=n(632);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-sync-dialog .modal-dialog{width:420px}.w-sync-dialog .modal-body .btn{display:block;width:398px;line-height:40px;font-size:18px;margin:10px 0}.w-sync-dialog .modal-body .btn span{margin-right:10px}.w-history-record-list{margin:20px 0 15px 10px;width:635px;display:inline-block;font-weight:400;white-space:nowrap}\",\"\"])},function(e,t,n){\"use strict\";var r=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e};n(634);var o=n(24),i=n(198),a=n(202),s=n(199),l=n(209),c=n(213),u=n(214),d=o.createClass({displayName:\"KVDialog\",getInitialState:function(){return{list:[],history:[]}},show:function(e,t,n,o,i){this.isValues=o,this.refs.kvDialog.show(),this._hideDialog=!1;var s=[],l=e&&e.hideDefaultOption;if(e&&Array.isArray(e.list)&&\"object\"===r(e.data)){var c=0;e.list.forEach(function(e){e&&360>c&&\"string\"==typeof e&&e.length<=256&&(++c,s.push(e))}),e.selected&&(i=-1===s.indexOf(e.selected)?\"\":e.selected),e=e.data}var u=o?n:t;this.setState({hideDefaultOption:l,selectedHistory:-1===s.indexOf(i)?l?s[0]:\"\":i,history:s,modal:u,list:a.parseImportData(e||\"\",u,o)})},hide:function(){this.refs.kvDialog.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},selectHistory:function(e){var t=this.props.onHistoryChange;t&&t(e.target.value,this.isValues)},viewContent:function(e){a.openEditor(e.target.title)},\"export\":function(){var e={},t=[];this.state.list.forEach(function(n){n.checked&&(e[n.name]=n.value,t.push(n.name))}),e[\"\"]=t,a.download(e,(this.isValues?\"values_\":\"rules_\")+a.formatDate()+\".txt\")},confirm:function(){var e,t={},n=[],r=this;r.state.list.forEach(function(r){r.checked&&(e=e||r.isConflict,t[r.name]=r.value,n.push(r.name))}),t[\"\"]=n;var o=function(){return r.hide(),s.trigger(r.isValues?\"uploadValues\":\"uploadRules\",t)};return e?void l.confirm(\"Content conflict detected. Overwrite existing content?\",function(e){e&&o()}):o()},checkAll:function(e){var t=this.state.list;if(t&&t.length){var n=e.target.checked;t.forEach(function(e){e.checked=n}),this.setState({list:t})}},checkItem:function(e,t){t.checked=e.target.checked,this.setState({})},render:function(){var e=this,t=e.state,n=t.list||[],r=t.history,s=t.selectedHistory||\"\",l=n.length,d=!l,p=0,h=t.modal,g=t.hideDefaultOption,f=!d&&n.every(function(e){return e.checked}),m=e.isValues?\"Values\":\"Rules\";return o.createElement(i,{ref:\"kvDialog\",wstyle:\"w-kv-dialog\"},o.createElement(\"div\",{className:\"modal-header\"},o.createElement(\"h4\",null,\"Select \",m),o.createElement(u,{onClick:e.hide})),o.createElement(\"div\",{className:\"modal-body\"},r.length?o.createElement(\"label\",null,\"History:\",o.createElement(\"select\",{value:s,onChange:this.selectHistory,className:\"form-control w-history-record-list\"},g?null:o.createElement(\"option\",{value:\"\"},\"Select history\"),r.map(function(e,t){return o.createElement(\"option\",{key:t,value:e},e)}))):void 0,o.createElement(\"table\",{className:\"table\"},o.createElement(\"thead\",null,o.createElement(\"th\",{className:\"w-kv-box\"},o.createElement(\"input\",{type:\"checkbox\",checked:f,onChange:e.checkAll,disabled:d})),o.createElement(\"th\",{className:\"w-kv-name\"},\"Name\"),o.createElement(\"th\",{className:\"w-kv-operation\"},\"Content\")),o.createElement(\"tbody\",{className:\"w-hover-body\"},d?o.createElement(\"tr\",null,o.createElement(\"td\",{colSpan:\"3\",className:\"w-empty\"},\"Empty\")):n.map(function(t,n){var r=a.isGroup(t.name),i=\"string\"==typeof t.value?t.value:\"\",s=i.length>128;t.checked&&++p;var l,u=t.isConflict&&!r;if(u){var d=h&&h.get(t.name);l=d&&d.value,l&&(l=\"<<<<<<<<<<< <<<<<<<<<<< <<<<<<<<<<< OLD <<<<<<<<<<< <<<<<<<<<<< <<<<<<<<<<<\\n\\n\"+l+\"\\n\\n=========== ========== ========== BOUNDARY ========== ========== ==========\\n\\n\"+i+\"\\n\\n>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> NEW >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>>\")}return o.createElement(\"tr\",{key:n,className:t.isConflict?\"w-kv-conflict\":void 0},o.createElement(\"th\",{className:\"w-kv-box\"},o.createElement(\"input\",{type:\"checkbox\",checked:t.checked,onChange:function(n){e.checkItem(n,t)}})),o.createElement(\"td\",{title:t.name,className:\"w-kv-name\"},r?o.createElement(c,{name:\"triangle-right\",className:\"w-list-group-icon\"}):null,t.name,u?o.createElement(\"strong\",{onClick:e.viewContent,title:l},\"[Conflict]\"):null),o.createElement(\"td\",{className:\"w-kv-operation\"},o.createElement(\"pre\",null,s?i.substring(0,100)+\"...\":i),s?o.createElement(\"a\",{title:i,onClick:e.viewContent},\"View all\"):null))})))),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"label\",{className:\"w-kv-check-all\"},o.createElement(\"input\",{type:\"checkbox\",checked:f,onChange:e.checkAll,disabled:d}),\"Select all\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",disabled:!p,onClick:this[\"export\"]},\"Export Selected\"),o.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",disabled:!p,onClick:this.confirm},\"Add To \",m,\" \",l?\" (\"+p+\" / \"+l+\")\":null)))}});e.exports=d},function(e,t,n){var r=n(635);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-kv-dialog{z-index:calc(var(--z-modal) + 1)}.w-kv-dialog .modal-dialog{width:45pc}.w-kv-name{width:330px;white-space:normal;word-wrap:break-word;word-break:break-all}.w-kv-dialog .w-kv-box{width:36px}.w-kv-operation pre{max-height:70px}.w-kv-operation a{display:block;width:50px;margin-top:3px}.w-kv-name strong{color:var(--c-risk);margin-left:5px;cursor:pointer}.w-kv-dialog thead td,.w-kv-dialog thead th{padding:8px}.w-kv-check-all{float:left;margin:5px 0 0 9px;display:flex;align-items:center}.w-kv-check-all input{margin-right:8px}\",\"\"])},function(e,t,n){\"use strict\";n(637);var r=n(24),o=n(57),i=n(198),a=n(199),s=n(200),l=n(214),c=r.createClass({displayName:\"AccountDialog\",show:function(e){if(e){var t=this.getIframe();t.onload=s.handleIframeLoad,\"editor.html\"===e&&t.setAttribute(\"data-type\",\"fake\"),t.src=e}this.refs.dialog.show()},hide:function(){this.refs.dialog.hide()},getIframe:function(){return o.findDOMNode(this.refs.iframe)},getWindow:function(){return this.getIframe().contentWindow},shouldComponentUpdate:function(){return!1},openInNewWin:function(){a.trigger(\"openInNewWin\")},render:function(){var e=this.props,t=e.className,n=e.hideButton;return r.createElement(i,{ref:\"dialog\",wstyle:\"w-large-dialog\"+(t?\" \"+t:\"\")},n?null:r.createElement(\"a\",{className:\"w-open-win-btn\",onClick:this.openInNewWin},\"Open In New Window\"),r.createElement(l,null),r.createElement(\"div\",{className:\"modal-body w-fix-drag\"},r.createElement(\"iframe\",{ref:\"iframe\",className:\"modal-body\"})))}});e.exports=c},function(e,t,n){var r=n(638);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-large-dialog .modal-dialog{width:calc(100% - 75pt);height:calc(100% - 60px);min-width:60pc;min-height:520px}.w-large-dialog{overflow:auto!important}.w-service-dialog{z-index:var(--z-modal)}.w-editor-win{z-index:var(--z-modal-max)}.w-custom-dialog button.close,.w-large-dialog button.close{font-size:20px;line-height:6px;padding:4px 3px 8px;border-radius:20px;border:1px solid var(--c-border);background:var(--b-default);position:absolute;top:-9px;right:-9px;z-index:1}.w-custom-dialog button.close:hover,.w-large-dialog button.close:hover{color:var(--c-heavy)}.w-large-dialog .w-open-win-btn{position:absolute;top:-23px;right:15px;z-index:1;font-size:9pt;padding:3px 10px;display:inline-block;background:var(--b-title);color:var(--c-heavy);border-top-left-radius:3px;border-top-right-radius:3px;text-decoration:none}.w-large-dialog .w-open-win-btn:hover{color:var(--c-default)}.w-custom-dialog .modal-content,.w-large-dialog .modal-content{border-radius:5px;height:100%;padding:0;border:none}.w-custom-dialog iframe,.w-large-dialog iframe{border-radius:5px}.w-custom-dialog .modal-body,.w-large-dialog .modal-body{display:block;width:100%;height:100%;border:none;outline:0;padding:0}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(538),a=n(290),s=n(202),l=n(640),c=n(214),u=/^(k|v):/,d=r.createClass({displayName:\"JSONDialog\",getInitialState:function(){return{history:[]}},onFilter:function(e){e=e.trim();var t=this;t._type=0;var n=\"!\"===e[0];return n&&(e=e.substring(1).trim()),u.test(e)&&(t._type=\"k\"===RegExp.$1?1:2,e=e.substring(2)),clearTimeout(t.filterTimer),t._keyword===e&&t._not===n||(t._keyword=e,t._not=n,t._type&&\"!\"===e[0]&&(n=!0,e=e.substring(1).trim()),e&&(e={not:n,keyword:e.toLowerCase(),regexp:s.toRegExp(e)}),t._keywordObj=e,e)?void(t.filterTimer=setTimeout(function(){var e=t.state.data;t.setState({curData:e&&t.filterJson(e)})},600)):t.setState({curData:null})},filterJson:function(e){if(this._keywordObj){var t=s.filterJsonText(e.str,this._keywordObj,this._type);if(t)return{json:t,str:JSON.stringify(t,null,\"  \")}}},show:function(e,t){if(e){var n=this.state.history,r=this.state.historyIndex,o=n.length;null==r&&(r=o-1);var i=r>-1&&n[r],a=t?t[0]:null;if(!i||i[0]!==e||i[1]!==a){++r,r!==o&&n.splice(r,o-r),n.push([e,a]);var s=r-35;s>0&&n.splice(0,s)}this.state.historyIndex=r,this._show(e,t)}},_show:function(e,t){var n=this,r=this.state.data;if(this.setState({keyPath:Array.isArray(t)?t:null}),r&&r.text===e)return n.focus(),n.refs.jsonDialog.show();var o=s.parseJSON(e);return o||/[\\r\\n]/.test(e)||(/\\s/.test(e)||-1===e.indexOf(\"&\")?(-1!==e.indexOf(\";\")||-1!==e.indexOf(\"=\"))&&(o=s.parseQueryString(e,\";\",null,decodeURIComponent)):o=s.parseQueryString(e,null,null,decodeURIComponent)),o?(r={json:o,text:e,str:JSON.stringify(o,null,\"  \")},void this.setState({curData:this.filterJson(r),data:r},function(){n.refs.jsonDialog.show(),n.focus()})):s.parseRawJson(e)},showHistory:function(e){this.state.historyIndex=e;var t=this.state.history[e];this._show(t[0],null==t[1]?null:[t[1]])},onBack:function(){var e=this.state.historyIndex;e>0&&this.showHistory(e-1)},onForward:function(){var e=this.state.history,t=this.state.historyIndex+1;t<e.length&&this.showHistory(t)},focus:function(){var e=this;setTimeout(function(){e.refs.filterInput.focus()},600)},render:function(){var e=this.state,t=e.history,n=e.historyIndex;return r.createElement(o,{ref:\"jsonDialog\",wstyle:\"w-json-dialog\"},r.createElement(\"div\",{className:\"modal-body\"},r.createElement(l,{disabledBack:0>=n,disabledForward:n>=t.length-1,onBack:this.onBack,onForward:this.onForward}),r.createElement(c,null),r.createElement(\"div\",{className:\"v-box\",style:{width:880,height:560,marginTop:22,background:this._keywordObj?\"var(--b-filtered)\":void 0}},r.createElement(i,{keyPath:e.keyPath,dialog:!0,data:e.curData||e.data,viewSource:!0})),r.createElement(a,{ref:\"filterInput\",onChange:this.onFilter,placeholder:\" (as: xxx, k:xxx or v:xxx)\"})),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))}}),p=r.createClass({displayName:\"JSONDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e,t){this.refs.jsonDialog.show(e,t)},render:function(){return r.createElement(d,{ref:\"jsonDialog\"})}});e.exports=p},function(e,t,n){\"use strict\";var r=n(24),o=n(213),i=r.createClass({displayName:\"FbBtn\",onForward:function(){var e=this.props;!e.disabledForward&&e.onForward&&e.onForward()},onBack:function(){var e=this.props;!e.disabledBack&&e.onBack&&e.onBack()},render:function(){var e=this.props,t=e.disabledForward,n=e.disabledBack;return r.createElement(\"div\",{className:\"w-json-bar\"},r.createElement(o,{name:\"menu-left\",className:n?\"w-disabled\":\"\",title:n?\"\":\"Back\",onClick:this.onBack}),r.createElement(o,{name:\"menu-right\",className:t?\"w-disabled\":\"\",title:t?\"\":\"Forward\",onClick:this.onForward}))}});e.exports=i},function(e,t,n){\"use strict\";function r(e){var t=e&&e.trim();return t&&\"{\"===t[0]&&\"}\"===t[t.length-1]?e:R}function o(e){return e?null:D}function i(e){return e=e&&e.trim()||\"\",\"localhost\"===e||Q.test(e)||z.test(e)?e:\"127.0.0.1:8080\"}function a(e){return e?e.trim():\"\"}function s(e,t,n){return\"\\n\"+t+\" \"+e+\"\\n\"+n+\"\\n\"+t}function l(e,t){if(e=a(e),!e||!t)return\"\";var n=\"```\";if(-1===t.indexOf(n))return s(e,n,t);for(var r=t.match(L).map(a),o=0;11>o;o++){if(-1===r.indexOf(n))return s(e,n,t);n+=\"`\"}return s(e,\"````````````\",t)}function c(e,t){var n=e.req,r=e.res||\"\";switch(t){case\"blank\":return\"\";case\"url\":return e.url;case\"method\":return n.method;case\"reqHeaders\":return JSON.stringify(n.headers,null,\"  \");case\"resHeaders\":return e.res.headers?JSON.stringify(r.headers,null,\"  \"):\"\";case\"reqBody\":return m.getBody(n,!0);case\"resBody\":return m.getBody(r);case\"reqJson\":return m.getJsonStr(n,!0,decodeURIComponent);case\"resJson\":return m.getJsonStr(r);case\"reqRaw\":return m.getReqRawHeaders(e)+\"\\r\\n\\r\\n\"+m.getBody(n,!0);case\"resRaw\":return m.getResRawHeaders(e)+\"\\r\\n\\r\\n\"+m.getBody(r);case\"statusCode\":return r.statusCode}return\"\"}function u(e){return\"(\"===e[0]&&\")\"===e[e.length-1]}var d=n(24),p=n(57),h=n(18),g=n(198),f=n(340),m=n(202),A=n(200),M=n(288).getGroupRules,w=n(209),v=n(206),b=n(199),y=n(586),x=n(213),T=n(511),C=n(214),N=p.findDOMNode,I=64,E='javascript:\"<style>html,body{padding:0;margin:0}</style><textarea></textarea>\"',D={display:\"none\"},S={padding:0,border:\"none\",width:840,height:360,margin:0,verticalAlign:\"top\"},L=/^\\s*?`{3,}\\s*?$/gm,k=[\"http://\",\"https://\",\"ws://\",\"wss://\",\"tunnel://\",\"redirect://\",\"locationHref://\",\"statusCode://\",\"style://\",\"pipe://\",\"host://\",\"xhost://\",\"proxy://\",\"xproxy://\",\"http-proxy://\",\"xhttp-proxy://\",\"https-proxy://\",\"xhttps-proxy://\",\"socks://\",\"xsocks://\",\"pac://\",\"weinre://\",\"log://\",\"excludeFilter://\",\"includeFilter://\",\"ignore://\",\"skip://\",\"enable://\",\"disable://\",\"delete://\",\"method://\",\"replaceStatus://\",\"referer://\",\"auth://\",\"ua://\",\"cache://\",\"attachment://\",\"forwardedFor://\",\"responseFor://\",\"reqDelay://\",\"resDelay://\",\"reqSpeed://\",\"resSpeed://\",\"reqType://\",\"resType://\",\"reqCharset://\",\"resCharset://\",\"reqWrite://\",\"resWrite://\",\"reqWriteRaw://\",\"resWriteRaw://\",\"sniCallback://\",\"lineProps://\"],j=[\"Map Local\",\"Map Remote\",\"DNS Spoofing\",\"Modify URL\",\"Modify Method\",\"Request Headers\",\"Response Headers\",\"Request Body\",\"Response Body\",\"Response 404\",\"Response 500\",\"Abort Request\",\"Abort Response\",\"Delay Request\",\"Delay Response\"],U=[\"http://\",\"https://\",\"ws://\",\"wss://\"],B=/^(?:http|ws)s?:\\/\\//,R=\"{\\n  \\n}\",z=/^\\[([\\w:]+)\\](:\\d{1,5})?$/,Q=/^[a-z][a-z\\d_:-]+(?:\\.[a-z][a-z\\d_:-]+)+?$/,O=d.createClass({displayName:\"MockDialog\",getInitialState:function(){return{rules:\"\",dataSrc:\"resBody\",valueType:\"file\",protocol:\"file://\",inlineValue:\"\",comment:\"\"}},show:function(e,t){if(e){var n=e.res.headers,r=m.getContentType(n),o=m.getFilename(e,!0)||\"\";t=t||\"resBody\",r=r?r.toLowerCase():\"\",\"img\"===r?(r=n[\"content-type\"],r=r.split(\"/\")[1].split(\";\")[0].trim().toLowerCase().replace(/^x-/i,\"\")):\"text\"===r?r=\"txt\":!r&&/\\.([\\w-]+)$/.test(o)&&(r=RegExp.$1),r=r?\".\"+r:\"\",this.refs.mockDialog.show(),this.setState({hasChanged:!1,item:e,pattern:e.url,dataSrc:t,suffixType:r,valueType:\"file\",keyName:o.substring(0,I),inlineKey:\"mock_\"+m.getTempName()+r,protocol:\"resRaw\"===t?\"rawfile://\":\"file://\"},this.updateRules);var i=N(this.refs.url);setTimeout(function(){i.select(),i.focus()},600),this._textarea.value=c(e,t)}},onValueTypeChange:function(e){var t=e.target.value,n=this.state.protocol;t=-1===k.indexOf(n)?t:\"inline\",\"key\"!==t||this.isValuesKey()||m.shakeElem(h(N(this.refs.keyName))),this.setState({valueType:t},this.updateRules)},onProtoChange:function(e){var t=e.target.value,n=this.state.inlineValue;switch(t){case\"Abort Request\":this.setState({protocol:\"enable://\",valueType:\"inline\",inlineValue:\"abort\"},this.updateRules);break;case\"Abort Response\":this.setState({protocol:\"enable://\",valueType:\"inline\",inlineValue:\"abortRes\"},this.updateRules);break;case\"Delay Request\":this.setState({protocol:\"reqDelay://\",valueType:\"inline\",inlineValue:n>0?n:\"5000\"},this.updateRules);break;case\"Delay Response\":this.setState({protocol:\"resDelay://\",valueType:\"inline\",inlineValue:n>0?n:\"5000\"},this.updateRules);break;case\"Modify URL\":this.setState({protocol:\"urlParams://\",valueType:\"inline\",inlineValue:r(n)},this.updateRules);break;case\"Request Headers\":this.setState({protocol:\"reqHeaders://\",valueType:\"inline\",inlineValue:r(n)},this.updateRules);break;case\"Response Headers\":this.setState({protocol:\"resHeaders://\",valueType:\"inline\",inlineValue:r(n)},this.updateRules);break;case\"Request Body\":this.setState({protocol:\"reqMerge://\",valueType:\"inline\",inlineValue:r(n)},this.updateRules);break;case\"Response Body\":this.setState({protocol:\"resMerge://\",valueType:\"inline\",inlineValue:r(n)},this.updateRules);break;case\"Response 404\":this.setState({protocol:\"statusCode://\",valueType:\"inline\",inlineValue:\"404\"},this.updateRules);break;case\"Response 500\":this.setState({protocol:\"statusCode://\",valueType:\"inline\",inlineValue:\"500\"},this.updateRules);break;case\"DNS Spoofing\":this.setState({protocol:\"host://\",valueType:\"inline\",inlineValue:i(n)},this.updateRules);break;case\"Map Remote\":this.setState({protocol:\"https://\",valueType:\"inline\",inlineValue:\"\"},this.updateRules);break;case\"Modify Method\":this.setState({protocol:\"method://\",valueType:\"inline\",inlineValue:\"\"},this.updateRules);break;case\"Map Local\":this.setState({protocol:\"file://\",valueType:\"inline\",inlineValue:\"\"},this.updateRules);break;default:this.setState({protocol:t},this.updateRules)}},getValues:function(){var e=this.getValueType(),t=\"file\"===e;if(t||\"key\"===e){var n={isFile:t,name:this.getKeyName()},r=this._textarea.value,o=this.state.dataSrc,i=this.state.item;return t&&r===c(i,o)&&(\"reqBody\"===o?n.base64=i.req.base64:\"resBody\"===o&&(n.base64=i.res.base64)),n.base64||(n.value=r),n}},\"export\":function(){var e=[this.wrapComment()],t=this.getValues();t&&e.push(t),b.trigger(\"showExportDialog\",[\"mock\",e])},isValuesKey:function(e){return\"{\"===(e||this.state.dataSrc)[0]},selectAllText:function(e){e.target.select()},updateRules:function(){var e=this.state,t=a(e.pattern);if(!t)return this.setState({rules:\"\"});var n=e.protocol||\"file://\",r=t+\" \"+n,o=this.getValueType(),i=e.suffixType;if(\"file\"===o)r+=\"temp/current_file_hash_placeholder\"+(i||\"\");else if(\"key\"===o)r+=this.isValuesKey()?e.dataSrc:e.keyName?\"{\"+e.keyName+\"}\":\"\";else{var s=e.inlineValue;/\\s/.test(s)?(s=l(e.inlineKey,s),r+=(s?\"{\"+e.inlineKey+\"}\":\"\")+s):-1!==U.indexOf(n)&&B.test(s)?r=t+\" \"+s:r+=s}this.setState({rules:r})},onPatternChange:function(e){var t=e.target.value.replace(/\\s+/g,\"\");this.setState({pattern:t},this.updateRules)},onInlineValueChange:function(e){var t=e.target.value;this.setState({inlineValue:t},this.updateRules)},onKeyNameChange:function(e){var t=e.target.value.replace(/\\s+/g,\"\");this.setState({keyName:t},this.updateRules)},onSourceChange:function(e){var t=this,n=e.target.value,r=function(){if(t.setState({dataSrc:n},t.updateRules),t.isValuesKey(n)){var e=A.valuesModal,r=e.getItem(n.slice(1,-1));t._textarea.value=r&&r.value||\"\"}else t._textarea.value=c(t.state.item,n)};return t.state.hasChanged?void w.confirm(\"Unsaved changes will be lost. Continue?\",function(e){e&&(r(),t.setState({hasChanged:!1}))}):r()},componentDidMount:function(){var e=this,t=N(e.refs.iframe),n=function(){var n=t.contentWindow.document.querySelector(\"textarea\"),r=n&&n.style;e._textarea=n,r&&(r.resize=\"none\",r.border=\"none\",r.width=S.width+\"px\",r.height=S.height+\"px\",r.padding=\"5px\",r.border=\"1px solid var(--c-border, #ccc)\",r.borderRadius=\"3px\",n.maxLength=3145728,n.placeholder=\"Enter value\",n.addEventListener(\"input\",function(){e.setState({hasChanged:!0})}),n.addEventListener(\"focus\",e.hideParams),n.onkeydown=function(t){(t.ctrlKey||t.metaKey)&&83===t.keyCode&&t.preventDefault(),m.handleFormat(t,e.formatValue),m.handleTab(t)})};t.onload=n,n(),b.on(\"hideMockDialog\",function(){e.refs.mockDialog.hide()}),b.on(\"addMockRulesSuccess\",function(){\"file\"===e.getValueType()&&(e.state.comment=\"\")}),h(document).on(\"click mousedown\",function(t){var n=h(t.target);n.closest(\".w-com-params\").length||n.closest(\".w-com-params-editor\").length||n.closest(\".w-com-dialog\").length||n.closest(\".w-win-dialog\").length||e.hideParams()})},formatValue:function(){var e=this._textarea;try{var t=e.value.trim();if(\"{\"===t[0]||\"[\"===t[0]){var n=JSON.stringify(JSON.parse(t),null,\"  \");e.value!==n&&(e.value=n,this.setState({hasChanged:!0}))}}catch(r){v.error(r.message)}},clearValue:function(){this._textarea.value=\"\"},removeRules:function(){var e=this;w.confirm(\"Do you confirm the deletion of the rules?\",function(t){t&&e.setState({pattern:\"\"},e.updateRules)})},showParams:function(){var e=N(this.refs.url).value.replace(/#.*$/,\"\"),t=e.indexOf(\"?\"),n=-1!==t,r=n?e.substring(t+1):\"\",o=m.parseQueryString(r,null,null,decodeURIComponent);this.refs.paramsEditor.update(o),this.state.showParams?this.setState({hasQuery:n}):this.setState({showParams:!0,hasQuery:n})},hideParams:function(){this.state.showParams&&this.setState({showParams:!1})},toggleParams:function(){this.state.showParams?this.hideParams():this.showParams()},clearQuery:function(){var e=this;w.confirm(\"Do you confirm the deletion of all params?\",function(t){t&&(e.refs.paramsEditor.clear(),e.hideParams())})},addQueryParam:function(){this.refs.paramsEditor.onAdd()},onParamsChange:function(){var e=this.refs.paramsEditor.toString(),t=N(this.refs.url);this.setState({hasQuery:!!e,pattern:m.replacQuery(t.value,e)},this.updateRules)},getValueType:function(){var e=this.state.valueType;if(\"file\"!==e)return e;var t=this.state.protocol;return-1===k.indexOf(t)?e:\"inline\"},save:function(e){var t=this,n=t.state.rules,r=t.getKeyName(),o=t.getValueType(),i=\"key\"===o,a=function(r){return r?n&&e!==!0?b.trigger(\"showRulesDialog\",{rules:t.wrapComment(),values:t.getValues()}):void t.saveValue():void 0};if(i){if(!r)return N(t.refs.keyName).focus(),v.error(\"The key name is required\");if(e===!0){var s=A.getValuesModal().getItem(r);if(s&&s.value!==t._textarea.value)return w.confirm(\"The name '\"+r+\"' is already in use. Overwrite?\",a)}}a(!0)},getKeyName:function(){var e=this.state;return this.isValuesKey()?e.dataSrc.slice(1,-1):e.keyName},saveValueOnly:function(){return\"key\"===this.getValueType()||this.isValuesKey()?void this.save(!0):(m.shakeElem(h(N(this.refs.valueType))),v.info(\"Switch to 'Key Value' mode, and set the corresponding key name\"))},saveValue:function(){var e=this,t=e._textarea.value,n=e.getKeyName();A.values.add({name:n,value:t},function(r,o){r&&0===r.ec?(b.trigger(\"addNewValuesFile\",{filename:n,data:t}),e.refs.mockDialog.hide()):m.showSystemError(o)})},trimInline:function(){var e=this.state.inlineValue;this.setState({inlineValue:e.trim()},this.updateRules)},clearInline:function(){this.setState({inlineValue:\"\"},this.updateRules)},asValue:function(){var e=this.state.inlineValue;/\\s/.test(e)||(u(e)||(e=\"(\"+e+\")\"),this.setState({inlineValue:e},this.updateRules))},valueNotChanged:function(){return this.isValuesKey()?!this.state.hasChanged:\"key\"!==this.getValueType()},onComment:function(e){this.setState({comment:e.target.value})},wrapComment:function(){var e=this.state,t=e.rules;if(!t||\"file\"!==this.getValueType())return t;var n=e.comment.trim();return n?\"# \"+n+\"\\n\"+t:t},render:function(){var e=this.state,t=this.getValueType(),n=\"file\"===t,r=\"inline\"!==t,i=e.inlineValue,a=e.rules,s=e.showParams,l=e.hasQuery,c=e.dataSrc||\"\",p=o(a),h=M(),m=A.getValuesModal();return a=this.wrapComment(),d.createElement(g,{ref:\"mockDialog\",wstyle:\"w-mock-dialog\"},d.createElement(\"div\",{className:\"modal-body\"+(n?\" w-mock-file\":\"\")},d.createElement(C,null),d.createElement(\"div\",{className:\"w-mock-row\"},d.createElement(\"span\",null,d.createElement(T,{docsUrl:\"rules/pattern.html\"}),\"URL Pattern:\"),d.createElement(\"input\",{ref:\"url\",onChange:this.onPatternChange,onFocus:this.selectAllText,placeholder:\"Enter url pattern\",value:e.pattern,className:\"form-control w-url-pattern\",maxLength:\"1200\"}),d.createElement(\"button\",{className:\"btn btn-default w-com-params\",onClick:this.toggleParams},\"Params\")),d.createElement(\"div\",{className:\"w-mock-row\"},d.createElement(\"span\",null,\"Operation:\"),d.createElement(\"select\",{className:\"form-control w-mock-protocol\",onChange:this.onProtoChange,value:e.protocol},j.map(function(e){return d.createElement(\"option\",{key:e,value:e},e)}),d.createElement(\"option\",{disabled:!0},\"─────────\"),h.map(function(e){var t=e[1];return t.length?d.createElement(\"optgroup\",{key:e[0],label:e[0]},t.map(function(e){return d.createElement(\"option\",{key:e,value:e},e)})):null})),d.createElement(\"select\",{ref:\"valueType\",onChange:this.onValueTypeChange,value:t,className:\"form-control w-mock-value-options\"},d.createElement(\"option\",{value:\"file\"},\"System File\"),d.createElement(\"option\",{value:\"key\"},\"Key Value\"),d.createElement(\"option\",{value:\"inline\"},\"Inline Value\")),d.createElement(\"select\",{className:\"form-control w-mock-value-type\",onChange:this.onSourceChange,value:c,style:o(r),title:c},d.createElement(\"option\",{value:\"blank\"},\"Blank\"),d.createElement(\"option\",{value:\"url\"},\"URL\"),d.createElement(\"option\",{value:\"method\"},\"Method\"),d.createElement(\"option\",{value:\"statusCode\"},\"Status Code\"),d.createElement(\"option\",{value:\"reqHeaders\"},\"Request Headers\"),d.createElement(\"option\",{value:\"resHeaders\"},\"Response Headers\"),d.createElement(\"option\",{value:\"reqBody\"},\"Request Body\"),d.createElement(\"option\",{value:\"resBody\"},\"Response Body\"),d.createElement(\"option\",{value:\"reqJson\"},\"Request JSON\"),d.createElement(\"option\",{value:\"resJson\"},\"Response JSON\"),d.createElement(\"option\",{value:\"reqRaw\"},\"Request Raw\"),d.createElement(\"option\",{value:\"resRaw\"},\"Response Raw\"),m.list.map(function(e){return e=\"{\"+e+\"}\",d.createElement(\"option\",{value:e},e)})),d.createElement(\"label\",{className:\"w-mock-comment\"},\"# Comment:\",d.createElement(\"input\",{className:\"form-control\",placeholder:\"Enter rule comment\",value:e.comment,onChange:this.onComment,maxLength:32})),d.createElement(\"textarea\",{onChange:this.onInlineValueChange,className:\"w-mock-inline\",placeholder:\"Enter value\",style:o(!r),maxLength:\"1200\",value:i}),d.createElement(\"div\",{style:o(!r&&i),className:\"w-mock-action\"},d.createElement(\"a\",{onClick:this.asValue,style:o(!/\\s/.test(i)&&!u(i))},\"AsValue\"),d.createElement(\"a\",{onClick:this.trimInline,style:o(/^\\s|\\s$/.test(i))},\"Trim\"),d.createElement(\"a\",{onClick:this.clearInline},\"Clear\")),d.createElement(\"input\",{ref:\"keyName\",onChange:this.onKeyNameChange,placeholder:\"Enter key name\",value:this.isValuesKey()?c.slice(1,-1):e.keyName,readOnly:this.isValuesKey(),onFocus:this.selectAllText,className:\"form-control w-mock-key-name\",style:o(r&&!n),maxLength:I})),d.createElement(\"div\",{className:\"w-mock-row\",style:o(r)},d.createElement(\"span\",null,\"Value:\"),d.createElement(\"div\",{className:\"w-fake-iframe w-fix-drag\"},d.createElement(\"iframe\",{ref:\"iframe\",\"data-type\":\"fake\",onLoad:A.handleIframeLoad,src:E,style:S})),d.createElement(\"div\",{style:o(r),className:\"w-mock-action\"},d.createElement(\"a\",{onClick:this.formatValue},\"Format\"),d.createElement(\"a\",{onClick:this.clearValue},\"Clear\"))),d.createElement(\"div\",{className:\"w-mock-row\",style:p},d.createElement(\"span\",{style:{marginTop:8}},\"Rules:\"),d.createElement(\"pre\",{className:\"w-mock-preview\"},a),d.createElement(x,{name:\"trash\",onClick:this.removeRules}),d.createElement(\"div\",{className:\"w-mock-rules-action\"},d.createElement(f,{value:a})))),d.createElement(\"div\",{className:\"modal-footer\"},d.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),d.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:this[\"export\"],disabled:!a},\"Export\"),r?d.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",onClick:this.saveValueOnly},\"Save As Values\"):null,n?d.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.save,disabled:!a,title:a?\"\":\"Please enter URL Pattern first\"},\"Save As Rules\"):d.createElement(\"button\",{type:\"button\",className:\"btn btn-primary\",onClick:this.save,disabled:!a&&this.valueNotChanged()},a?\"Save As Rules\"+(!r||this.valueNotChanged()?\"\":\" & Values\"):\"Save As Values\")),d.createElement(\"div\",{className:\"w-layer w-com-params-editor v-box\"+(s?\"\":\" hide\")},d.createElement(\"div\",{className:\"w-filter-bar\"},d.createElement(C,{onClick:this.hideParams,className:\"w-close-params\"}),d.createElement(\"a\",{style:{display:l?null:\"none\"},className:\"w-params-clear-btn\",onClick:this.clearQuery},d.createElement(x,{name:\"trash\"}),\"Clear\"),d.createElement(\"a\",{onClick:this.addQueryParam},\"+Param\")),d.createElement(y,{ref:\"paramsEditor\",onChange:this.onParamsChange})))}}),H=d.createClass({displayName:\"MockDialogWrap\",shouldComponentUpdate:function(){return!1},show:function(e,t){this.refs.mockDialog.show(e,t)},render:function(){return d.createElement(O,{ref:\"mockDialog\"})}});e.exports=H},function(e,t,n){\"use strict\";function r(e,t){\"function\"==typeof e&&e(a(t))}n(643);var o=n(24),i=n(198),a=n(300),s=n(200),l=n(213),c=n(214),u=o.createClass({displayName:\"IframeDialog\",getInitialState:function(){return{}},show:function(e){var t=this;t._hideDialog=!1,t.setState(e,function(){t.refs.iframeDialog.show()})},hide:function(){this.refs.iframeDialog.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},render:function(){var e=this.state,t=e.disabled,n=e.name,a=e.url,u=e.favicon?o.createElement(\"img\",{src:e.favicon}):null,d=\"w-plugins-tab inline-align-items\"+(t?\" w-plugin-tab-disabled\":\"\");return window.onWhistlePluginOptionModalReady=r,o.createElement(i,{ref:\"iframeDialog\",wstyle:\"w-iframe-dialog\",width:e.width||\"max(calc(100% - 240px), 720px)\"},o.createElement(\"div\",{className:\"modal-header\"},o.createElement(\"h4\",null,o.createElement(\"span\",{className:d},t?o.createElement(l,{\"data-name\":n,name:\"ban-circle\"}):u,n||\"Untitled\")),o.createElement(c,null)),o.createElement(\"div\",{className:\"modal-body w-fix-drag\",style:{height:e.height||\"max(calc(100vh - 120px), 600px)\"}},o.createElement(\"iframe\",{src:a,onLoad:s.handleIframeLoad})))}});e.exports=u},function(e,t,n){var r=n(644);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-iframe-dialog .modal-dialog{width:max(calc(100% - 240px), 720px)}.w-iframe-dialog .modal-body{padding:0;margin:0;width:100%;height:max(calc(100vh - 120px), 600px)}.w-iframe-dialog .modal-body iframe{width:100%;height:100%;display:block;border:none;outline:0}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(200),i=n(202),a=n(213),s=r.createClass({displayName:\"ServiceBtn\",showService:function(){i.showService()},render:function(){if(!o.whistleId)return null;var e=o.hasWhistleToken;return r.createElement(\"a\",{onClick:this.showService,className:\"w-plugins-menu w-service-btn\",draggable:\"false\",title:e?\"Whistle Service\":\"Whistle Service (not logged in)\"},r.createElement(a,{name:\"cloud\",className:e?\"\":\"w-disabled\"}),\"Service\")}});e.exports=s},function(e,t,n){\"use strict\";function r(e){return\"network\"===e?\".txt,.json,.saz,.har\":\"console\"===e||\"server\"===e?\".log\":\"composer\"===e?\".txt,.json,.har\":\".txt,.json\"}function o(e){if(\"{\"===e[0]||\"[\"===e[0])try{return JSON.parse(e)}catch(t){}}n(647);var i=n(24),a=n(57),s=n(198),l=n(200),c=n(202),u=n(199),d=n(206),p=n(197),h=n(649),g=n(213),f=n(214),m=a.findDOMNode,A=i.createClass({\ndisplayName:\"ImportDialog\",getInitialState:function(){return{}},show:function(e){var t=this;t.refs.importDialog.show(),setTimeout(function(){var e=m(t.refs.input);e.focus(),e.select()},500),e=e||\"network\",t.setState({name:e,accept:r(e),importCURL:\"composer\"===e,title:c.getDialogTitle(e)})},hide:function(){this.refs.importDialog.hide()},showService:function(){c.showService(this.state.name+\"/history\")},importRemoteUrl:function(e){if(!e||\"click\"===e.type||13===e.keyCode){var t=this,n=m(t.refs.input),r=n.value.trim();return r?void l.getRemoteData(r,function(e,n){n&&(t.hide(),u.trigger(t.state.name+\"ImportData\",[n]))}):(d.error(\"The url or file path is required\"),n.value=\"\",void n.focus())}},handleFile:function(e){e&&(this.hide(),u.trigger(this.state.name+\"ImportFile\",[e]))},componentDidMount:function(){var e=this;u.on(\"importFile\",function(t,n){e.handleFile(n)})},shouldComponentUpdate:function(){return this.refs.importDialog.isVisible()},uploadFile:function(){var e=m(this.refs.importFile);this.handleFile(e.files[0]),m(this.refs.importFile).value=\"\"},selectFile:function(){m(this.refs.importFile).click()},importCURL:function(e){if(e=e.trim(),!e)return d.error(\"The text is required\"),!1;try{var t=o(e)||h(e);if(!t||!t.url)return d.error(\"Not cURL text\"),!1;t.isHexText=!1,t.headers=c.objectToString(t.headers),u.trigger(\"setComposerData\",[t])}catch(n){return d.error(n.message),!1}},showImportCURL:function(){this.refs.editorDialog.show()},render:function(){var e=this.state;return i.createElement(s,{ref:\"importDialog\",wstyle:\"w-ie-dialog w-import-dialog\"},i.createElement(\"div\",{className:\"modal-header\"},i.createElement(\"h4\",null,e.title),i.createElement(f,null)),i.createElement(\"div\",{className:\"modal-body\"},i.createElement(\"input\",{ref:\"input\",maxLength:\"2048\",onKeyDown:this.importRemoteUrl,placeholder:\"Enter url or file path\"})),i.createElement(\"div\",{className:\"modal-footer\"},i.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),e.importCURL?i.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\",onClick:this.showImportCURL},i.createElement(g,{name:\"file\"}),\"Import cURL\"):null,l.whistleId?i.createElement(\"button\",{type:\"button\",className:\"btn btn-warning\",\"data-dismiss\":\"modal\",onClick:this.showService},i.createElement(g,{name:\"cloud\"}),\"Import From Service\"):null,i.createElement(\"button\",{type:\"button\",className:\"btn btn-info\",onClick:this.selectFile},i.createElement(g,{name:\"folder-open\"}),\"Upload\"),i.createElement(\"button\",{type:\"button\",className:\"btn btn-primary w-fmt-btn\",onMouseDown:c.preventBlur,onClick:this.importRemoteUrl},\"Import\")),i.createElement(\"form\",{ref:\"importFileForm\",encType:\"multipart/form-data\",style:{display:\"none\"}},i.createElement(\"input\",{ref:\"importFile\",onChange:this.uploadFile,name:\"fileInput\",type:\"file\",accept:e.accept})),i.createElement(p,{ref:\"editorDialog\",title:\"Import cURL\",hideFormat:\"1\",placeholder:\"Enter cURL text\",textEditor:!0,onConfirm:this.importCURL}))}});e.exports=A},function(e,t,n){var r=n(648);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-ie-dialog .modal-dialog{width:600px}.w-ie-dialog .modal-body{background-color:var(--b-bar);padding:20px 10px}.w-ie-dialog input{width:578px;height:36px;line-height:36px;padding:5px;border:1px solid var(--c-border)}.w-ie-dialog .modal-footer{white-space:nowrap}\",\"\"])},function(e,t){\"use strict\";function n(e){for(var t=[],n=\"\",r=function(e){if(null!=e[5])throw new Error(\"Unmatched quote\");var r=e[1],o=e[2],i=e[3],a=e[4],s=e[6];if(r)n+=r;else{var l=o||i||a;l&&(n+=l)}null!=s&&(t.push(n),n=\"\")};e.length>0;){var o=e.match(i);o&&null!=o.index&&null!=o[0]?(r(o),e=e.slice(o.index+o[0].length)):e=\"\"}return n&&t.push(n),t}function r(e){return n(e).reduce(function(e,t){return t?(t.indexOf(\"-X\")?e.push(t):(e.push(\"-X\"),(t=t.substring(2))&&e.push(t)),e):e},[])}var o={\"-A\":\"user-agent\",\"--user-agent\":\"user-agent\",\"-H\":\"headers\",\"--header\":\"headers\",\"-d\":\"data\",\"--data\":\"data\",\"--data-ascii\":\"data\",\"--data-raw\":\"data\",\"--data-binary\":\"data\",\"--data-urlencode\":\"data\",\"-u\":\"user\",\"--user\":\"user\",\"-X\":\"method\",\"--request\":\"method\",\"-b\":\"cookie\",\"--cookie\":\"cookie\"},i=/\\s*(?:([^\\s\\\\'\"]+)|'((?:[^'\\\\]|\\\\.)*)'|\"((?:[^\"\\\\]|\\\\.)*)\"|(\\\\.?)|(\\S))(\\s|$)?/;e.exports=function(e){if(e=e.trim(),!e.indexOf(\"curl \")){var t=r(e),n={method:\"GET\",headers:{}},i=n.headers,a=\"\";return t.forEach(function(e){if(/^https?:\\/\\//.test(e))return void(n.url=e);if(\"--compressed\"===e)return void(i[\"accept-encoding\"]=i[\"accept-encoding\"]||\"deflate, gzip\");if(\"-I\"===e||\"--head\"===e)return void(n.method=\"HEAD\");if(o[e])return void(a=o[e]);if(a){switch(a){case\"method\":n.method=e.toUpperCase();break;case\"user\":try{i.authorization=\"Basic \"+btoa(e)}catch(t){}break;case\"user-agent\":i[\"user-agent\"]=e;break;case\"cookie\":i.cookie=e;break;case\"headers\":var r=e.indexOf(\": \");if(-1!==r){e=e.replace(/(^|.)\\\\\"/gm,function(e,t){return\"\\\\\"===t?'\\\\\"':(t||\"\")+'\"'});var s=e.substring(0,r).trim().toLowerCase();i[s]=e.substring(r+2).trim()}break;case\"data\":(\"GET\"==n.method||\"HEAD\"==n.method)&&(n.method=\"POST\"),i[\"content-type\"]=i[\"content-type\"]||\"application/x-www-form-urlencoded\",n.body=n.body?n.body+\"&\"+e:e}a=\"\"}}),n}}},function(e,t,n){\"use strict\";var r=n(24),o=n(57),i=n(198),a=n(626),s=n(202),l=n(214),c=o.findDOMNode,u=r.createClass({displayName:\"ExportDialog\",getInitialState:function(){return{filename:\"\"}},show:function(e,t){var n=this;n.refs.exportDialog.show(),setTimeout(function(){var e=c(n.refs.input);e.focus(),e.select()},500),e=e||\"network\",n.setState({name:e,title:s.getDialogTitle(e,\"Export\"),showOptions:\"network\"===e,data:t})},getInputValue:function(){return s.formatFilename(c(this.refs.input).value.trim())},getFilename:function(){var e=this.state.name,t=\"console\"===e||\"server\"===e?\".log\":\".txt\",n=this.getInputValue();if(n)return/\\.(txt|json)/i.test(n)||(n+=t),n;switch(e){case\"networkSettings\":n=\"network_settings_\";break;case\"composer\":n=\"composer_\";break;case\"console\":n=\"console_\";break;case\"server\":n=\"server_\";break;case\"rulesSettings\":n=\"rules_settings_\";break;case\"valuesSettings\":n=\"values_settings_\";break;case\"mock\":n=\"mock_\";break;default:n=\"network_\"}return n+s.formatDate()+t},hide:function(){this.refs.exportDialog.hide()},\"export\":function(e){if(!e||\"click\"===e.type||13===e.keyCode){var t=this.state.data;\"function\"==typeof t&&(t=t()),this.hide(),s.download(t,this.getFilename()),c(this.refs.input).value=\"\"}},filterFilename:function(e){this.setState({filename:s.formatFilename(e.target.value)})},onShare:function(e){e||(this.hide(),c(this.refs.input).value=\"\")},shouldComponentUpdate:function(){return this.refs.exportDialog.isVisible()},render:function(){var e=this.state,t=e.showOptions;return r.createElement(i,{ref:\"exportDialog\",wstyle:\"w-ie-dialog\"+(t?\" w-export-network\":\"\")},r.createElement(\"div\",{className:\"modal-header\"},r.createElement(\"h4\",null,e.title),r.createElement(l,null)),r.createElement(\"div\",{className:\"modal-body\"},r.createElement(\"input\",{ref:\"input\",value:e.filename,onChange:this.filterFilename,onKeyDown:this[\"export\"],placeholder:\"Enter filename (optional)\",className:\"form-control\",maxLength:\"64\"})),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Cancel\"),r.createElement(a,{type:e.name,data:e.data,getFilename:this.getInputValue,onComplete:this.onShare}),r.createElement(\"button\",{type:\"button\",className:\"btn btn-primary w-fmt-btn\",\"data-dismiss\":\"modal\",onClick:this[\"export\"]},\"Export\")))}});e.exports=u},function(e,t,n){\"use strict\";function r(e){return\"crt\"===e||\"pem\"===e?e:\"cer\"}var o=n(24),i=n(652),a=n(522),s=n(200),l=n(210),c=n(202),u=n(57),d=n(18),p=n(199),h=n(213),g=n(511),f=n(214),m=o.createClass({displayName:\"HttpsSettings\",getInitialState:function(){return{caType:r(l.get(\"caType\"))}},selectCAType:function(e){var t=r(e.target.value);this.setState({caType:t}),l.set(\"caType\",t)},selectCAUrl:function(e){this.setState({caFullUrl:e.target.value})},show:function(){d(u.findDOMNode(this.refs.rootCADialog)).modal(\"show\")},showCustomCertsInfo:function(){var e=this;e.loadingCerts||(e.loadingCerts=!0,s.certs.all(function(t,n){return e.loadingCerts=!1,t?void e.refs.certsInfoDialog.show(t.certs,t.dir):void c.showSystemError(n)}))},componentDidMount:function(){p.on(\"showCustomCerts\",this.showCustomCertsInfo)},render:function(){var e=this.props,t=e.caHash,n=e.port,r=e.caUrlList,l=e.multiEnv,c=e.interceptHttpsConnects,u=e.enableHttp2,d=e.onEnableHttps,p=e.onEnableHttp2,m=this.state,A=m.caType,M=m.caFullUrl,w=\"cgi-bin/rootca\",v=\"http://rootca.pro/\";return\"cer\"!==A&&(w+=\"?type=\"+A,v+=A),o.createElement(\"div\",{ref:\"rootCADialog\",className:\"modal fade w-https-dialog\"},o.createElement(\"div\",{className:\"modal-dialog\"},o.createElement(\"div\",{className:\"modal-content\"},o.createElement(\"div\",{className:\"modal-body\"},o.createElement(f,null),o.createElement(\"div\",{style:{marginBottom:10}},o.createElement(g,{docsUrl:\"gui/https.html\"}),o.createElement(\"a\",{className:\"w-download-rootca\",title:v,href:w,target:\"downloadTargetFrame\"},\"Download RootCA\"),o.createElement(\"select\",{className:\"w-root-ca-type\",value:A,onChange:this.selectCAType},o.createElement(\"option\",{value:\"crt\"},\"rootCA.crt\"),o.createElement(\"option\",{value:\"cer\"},\"rootCA.cer\"),o.createElement(\"option\",{value:\"pem\"},\"rootCA.pem\"))),o.createElement(\"div\",{className:\"w-root-ca-url-wrap\"},o.createElement(\"select\",{className:\"w-root-ca-url\",value:M,onChange:this.selectCAUrl},o.createElement(\"option\",{value:\"\"},v,\" (PROXY REQUIRED)\"),r.map(function(e){return e=\"h\"===e[0]?e:\"http://\"+e+\":\"+n,e+=\"/cgi-bin/rootca\"+(\"cer\"===A?\"\":\"?type=\"+A),o.createElement(\"option\",{value:e},e)})),o.createElement(h,{title:\"Copy Root CA URL\",name:\"copy\",className:\"w-copy-text-with-tips\",\"data-clipboard-text\":M||v})),o.createElement(\"a\",{href:w,target:\"downloadTargetFrame\"},o.createElement(a,{url:M||v+t})),o.createElement(\"div\",{className:\"w-https-settings\"},o.createElement(\"p\",null,o.createElement(\"label\",{title:l?\"Use 'pattern enable://capture' in rules to enable HTTPS\":void 0},o.createElement(\"input\",{disabled:l,checked:c,onChange:d,type:\"checkbox\",className:\"w-va-mdl\"}),o.createElement(\"span\",{className:\"w-va-mdl w-mrl-5\"},\"Enable HTTPS (Capture Tunnel Traffic)\"))),o.createElement(\"p\",null,o.createElement(\"label\",null,o.createElement(\"input\",{checked:s.supportH2&&u,onChange:p,type:\"checkbox\",className:\"w-va-mdl\"}),o.createElement(\"span\",{className:\"w-va-mdl w-mrl-5\"},\"Enable HTTP/2\"))),o.createElement(\"a\",{draggable:\"false\",style:{color:s.hasInvalidCerts?\"var(--c-error)\":void 0},onClick:this.showCustomCertsInfo},\"Custom Certs Settings\"),o.createElement(i,{ref:\"certsInfoDialog\"}))),o.createElement(\"div\",{className:\"modal-footer\"},o.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\")))))}});e.exports=m},function(e,t,n){\"use strict\";function r(e,t){return t=t||e.filename,t+\".\"+(e.type||\"crt\")}function o(e,t){var n=new FileReader;n.readAsText(e),n.onload=function(){t(n.result)}}n(653);var i=n(24),a=n(57),s=n(202),l=n(198),c=n(655),u=n(209),d=n(200),p=n(206),h=n(213),g=n(511),f=n(214),m=a.findDOMNode,A=131072,M=i.createClass({displayName:\"CertsInfoDialog\",getInitialState:function(){return{list:[]}},show:function(e,t){var n,o=[];this._certsDir=this._certsDir||t,Object.keys(e).forEach(function(t){var a,s=e[t],l=new Date(s.notBefore),c=new Date(s.notAfter),u=\"\",d=Date.now();l.getTime()>d?(a=!0,u=\"Invalid\"):c.getTime()<d&&(a=!0,u=\"Expired\");var p={dir:s.dir,filename:t,domain:s.dnsName,disabled:s.disabled,mtime:s.mtime,type:s.type,validity:l.toLocaleString()+\" ~ \"+c.toLocaleString(),status:u||i.createElement(h,{name:\"ok\"}),isInvalid:a};\"root\"===t?(p.displayName=\"root (Root CA)\",n=p,p.readOnly=!0,p.isRoot=!0):(\"z\"===t[0]&&\"/\"===t[1]&&(t=t.substring(2),p.readOnly=!0),p.displayName=r(p,t),o.push(p))}),o.sort(function(e,t){return e.readOnly?t.readOnly?0:-1:t.readOnly?1:s.compare(t.mtime,e.mtime)}),n&&o.unshift(n),this.refs.certsInfoDialog.show(),this._hideDialog=!1,this.setState({list:o})},hide:function(){this.refs.certsInfoDialog.hide(),this._hideDialog=!0},showRemoveTips:function(e){var t=(e.dir||\"\").replace(/\\\\/g,\"/\");t+=/\\/$/.test(t)?\"\":\"/\";var n=t+r(e),o=t+e.filename+\".key\";this.refs.tipsDialog.show({title:\"Delete the following files and restart whistle:\",tips:o+\"\\n\"+n,dir:e.dir})},handleCgi:function(e,t){return e?void this.show(e):s.showSystemError(t)},removeCert:function(e){var t=this;u.confirm(\"Do you confirm the deletion of '\"+r(e)+\"'?\",function(n){n&&d.certs.remove({filename:e.filename,type:e.type},t.handleCgi)})},shouldComponentUpdate:function(){return this._hideDialog===!1},formatFiles:function(e){for(var t,n=0,r=e.length;r>n;n++){var o=e[n];if(o.size>A||!(o.size>0))return void p.error(\"Maximum file size: 128KB\");var i=o.name;if(!/\\.(crt|cer|pem|key)/.test(i))return void p.error(\"Supported file formats: .key, .crt, .cer, .pem\");var a=RegExp.$1;if(i=i.slice(0,-4),!i||i.length>128)return void p.error(\"Filename must be between 1-128 characters\");t=t||{};var s=t[i]||{};s[\"key\"==a?\"key\":\"cert\"]=o,\"key\"!==a&&(s.type=a),t[i]=s}if(t){var l,c=[],d=[];if(Object.keys(t).forEach(function(e){var n=t[e];n.key&&n.cert?(l=l||{},l[e]=n):n.key?d.push(e+\".[crt/cer/pem]\"):c.push(e+\".key\")}),c.length||d.length){var h=\"\";c.length&&(h+=\"Missing key files: \"+c.join(\", \")),d.length&&(h+=(h?\"\\n\":\"\")+\"Missing cert files: \"+d.join(\", \")),u.alert(h)}return l}},handleChange:function(e){var t=this,n=m(t.refs.uploadCerts),r=n.files&&t.formatFiles(n.files);if(n.value=\"\",r){if(r.root){var i=t._certsDir||\"~/.WhistleAppData/custom_certs\";u.alert(\"Root CA must be manually copied to the following directory and Whistle restarted:\\n\"+i),delete r.root}var a=function(){d.uploadCerts(r,t.handleCgi)},s=Object.keys(r),l=2*s.length;s.map(function(e){var t=r[e];o(t.key,function(e){t.key=e,0===--l&&a()}),o(t.cert,function(e){t.cert=e,0===--l&&a()})})}},handleActive:function(e){var t=e.target,n=t.checked,r=t.getAttribute(\"data-filename\"),o=JSON.stringify({filename:r,disabled:!n});d.certs.active(o,this.handleCgi)},showUpload:function(){m(this.refs.uploadCerts).click()},showService:function(){s.showService(\"certs/history\")},render:function(){var e=this,t=e.state.list||[];return i.createElement(l,{ref:\"certsInfoDialog\",wstyle:\"w-certs-dialog\"},i.createElement(\"div\",{className:\"modal-body\"},i.createElement(f,{onClick:e.hide}),i.createElement(\"h4\",{className:\"w-certs-title\"},i.createElement(g,{docsUrl:\"gui/https.html#custom-certs\"}),\"Custom Certs Settings\"),i.createElement(\"table\",{className:\"table w-hover-body\"},i.createElement(\"thead\",null,i.createElement(\"th\",{className:\"w-certs-order\"},\"#\"),i.createElement(\"th\",{className:\"w-certs-active\"},\"Active\"),i.createElement(\"th\",{className:\"w-certs-filename\"},\"Filename\"),i.createElement(\"th\",{className:\"w-certs-domain\"},\"DNS Name\"),i.createElement(\"th\",{className:\"w-certs-validity\"},\"Validity\"),i.createElement(\"th\",{className:\"w-certs-status\"},\"Status\")),i.createElement(\"tbody\",null,t.length?t.map(function(t,n){return i.createElement(\"tr\",{className:(t.isInvalid?\"w-cert-invalid\":\"\")+(t.disabled?\" w-certs-disabled\":\"\")},i.createElement(\"th\",{className:\"w-certs-order\"},n+1),i.createElement(\"td\",{className:\"w-certs-active\"},t.isRoot?null:i.createElement(\"input\",{type:\"checkbox\",\"data-filename\":t.filename,onChange:e.handleActive,checked:!t.disabled})),i.createElement(\"td\",{className:\"w-certs-filename\",title:t.filename},t.displayName||t.filename,i.createElement(\"br\",null),i.createElement(\"a\",{className:t.readOnly?null:\"w-delete\",onClick:function(){t.readOnly?e.showRemoveTips(t):e.removeCert(t)}},t.readOnly?\"View path\":\"Delete\")),i.createElement(\"td\",{className:\"w-certs-domain\",title:t.domain},t.isRoot?null:t.domain),i.createElement(\"td\",{className:\"w-certs-validity\",title:t.validity},t.validity),i.createElement(\"td\",{className:\"w-certs-status\"},t.status))}):i.createElement(\"tr\",null,i.createElement(\"td\",{colSpan:\"5\",className:\"w-empty\"},\"Empty\"))))),i.createElement(\"div\",{className:\"modal-footer\"},i.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),d.whistleId?i.createElement(\"button\",{type:\"button\",className:\"btn btn-warning\",\"data-dismiss\":\"modal\",onClick:this.showService},i.createElement(h,{name:\"cloud\"}),\"Import From Service\"):null,i.createElement(\"input\",{ref:\"uploadCerts\",style:{display:\"none\"},type:\"file\",accept:\".crt,.cer,.pem,.key\",multiple:\"multiple\",onChange:e.handleChange}),i.createElement(\"button\",{type:\"button\",style:{display:d.isDiableCustomCerts()?\"none\":void 0,marginLeft:5},className:\"btn btn-primary\",onClick:e.showUpload},i.createElement(h,{name:\"folder-open\"}),\"Upload\")),i.createElement(c,{ref:\"tipsDialog\"}))}});e.exports=M},function(e,t,n){var r=n(654);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-certs-dialog{z-index:var(--z-modal)}.w-certs-dialog .w-help-icon{font-size:15px;margin-top:-2px}.w-certs-dialog .modal-dialog{width:1030px}.w-certs-dialog .w-certs-active{width:72px;text-align:center}.w-certs-order{width:50px}.w-certs-validity{width:338px}.w-certs-status{width:75pt}.w-certs-status .glyphicon-ok{color:var(--c-ok)}.w-cert-invalid td,.w-cert-invalid th{color:var(--c-error)}.w-certs-filename a{font-size:9pt;cursor:pointer}.w-certs-filename .glyphicon-lock{margin-right:5px}.w-certs-dialog td{overflow:hidden;text-overflow:ellipsis}.w-certs-filename{width:200px}.w-certs-dialog td,.w-certs-dialog th{vertical-align:middle!important}.w-certs-disabled .w-certs-domain,.w-certs-disabled .w-certs-filename,.w-certs-disabled .w-certs-order,.w-certs-disabled .w-certs-validity{color:var(--c-disabled)}\",\"\"])},function(e,t,n){\"use strict\";var r=n(24),o=n(198),i=n(214),a=r.createClass({displayName:\"TipsDialog\",getInitialState:function(){return{}},show:function(e){this._hideDialog=!1,this.setState(e),this.refs.tipsDialog.show()},hide:function(){this.refs.tipsDialog.hide(),this._hideDialog=!0},shouldComponentUpdate:function(){return this._hideDialog===!1},render:function(){var e=this.state;return r.createElement(o,{ref:\"tipsDialog\",wstyle:\"w-dns-servers-dialog w-tips-dialog\"},r.createElement(\"div\",{className:\"modal-header\"},r.createElement(\"h4\",null,e.title),r.createElement(i,null)),r.createElement(\"pre\",{className:\"modal-body\"},e.tips),r.createElement(\"div\",{className:\"modal-footer\"},r.createElement(\"button\",{type:\"button\",className:\"btn btn-default\",\"data-dismiss\":\"modal\"},\"Close\"),r.createElement(\"button\",{type:\"button\",\"data-dismiss\":\"modal\",className:\"btn btn-primary w-copy-text-with-tips\",\"data-clipboard-text\":e.dir},\"Copy directory\")))}});e.exports=a},function(e,t,n){\"use strict\";n(657);var r,o,i=n(24),a=n(18),s=n(199),l=n(636),c=n(200),u=n(300).getServiceBridge,d=n(202),p=/^\\/?login(?:\\?|$)/,h=i.createClass({displayName:\"ServiceDialog\",componentDidMount:function(){var e=this;s.on(\"showService\",e.showService),s.on(\"hideService\",e.hideService),s.on(\"whistleIdChanged\",function(){var t=e.getServiceFunc(\"onWhistleIdChange\");t&&t(c.whistleId)}),s.on(\"hasWhistleTokenChanged\",function(){var t=e.getServiceFunc(\"onHasWhistleTokenChange\");if(t){var n=c.hasWhistleToken,r=a(\".w-service-dialog\");n&&r.hasClass(\"w-login-dialog\")&&e.showService(),t(n)}e.setState({})})},getServiceFunc:function(e){return o=o||d.getServiceApi(this.refs.serviceDialog.getWindow(),r),e&&o?(e=o[e],\"function\"==typeof e?e.bind(o):null):o},showService:function(e,t){var n=this,o=n.refs.serviceDialog,i=a(\".w-service-dialog\");if(p.test(t)){var s=n.getServiceFunc(\"showLoginDialog\");if(s)return s(),i.removeClass(\"w-login-dialog\"),o.show();i.addClass(\"w-login-dialog\")}else i.removeClass(\"w-login-dialog\");r=r||u(n.hideService),o.show(d.getServiceUrl(o.getWindow(),t,r))},hideService:function(){this.refs.serviceDialog.hide()},render:function(){var e=\"w-service-dialog\"+(c.hasWhistleToken?\"\":\" w-login-dialog\");return i.createElement(l,{className:e,ref:\"serviceDialog\",hideButton:\"1\"})}});e.exports=h},function(e,t,n){var r=n(658);\"string\"==typeof r&&(r=[[e.id,r,\"\"]]);n(13)(r,{});r.locals&&(e.exports=r.locals)},function(e,t,n){t=e.exports=n(7)(),t.push([e.id,\".w-service-btn{font-weight:700}.w-service-btn .w-disabled{cursor:pointer!important}.w-large-dialog.w-login-dialog .modal-dialog{width:420px;height:520px;min-width:420px;min-height:520px}\",\"\"])}]);"
  },
  {
    "path": "biz/webui/htdocs/preview.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>PREVIEW</title>\n</head>\n<body style=\"overscroll-behavior-x: none;\">\n  <script>\n    (function(global,factory){typeof exports===\"object\"&&typeof module!==\"undefined\"?module.exports=factory(global):typeof define===\"function\"&&define.amd?define(factory):factory(global)})(typeof self!==\"undefined\"?self:typeof window!==\"undefined\"?window:typeof global!==\"undefined\"?global:this,function(global){\"use strict\";var _Base64=global.Base64;var version=\"2.4.5\";var buffer;if(typeof module!==\"undefined\"&&module.exports){try{buffer=require(\"buffer\").Buffer}catch(err){}}var b64chars=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\\uD800-\\uDBFF][\\uDC00-\\uDFFFF]|[^\\x00-\\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?\"=\":b64chars.charAt(ord>>>6&63),padlen>=1?\"=\":b64chars.charAt(ord&63)];return chars.join(\"\")};var btoa=global.btoa?function(b){return global.btoa(b)}:function(b){return b.replace(/[\\s\\S]{1,3}/g,cb_encode)};var _encode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(u){return(u.constructor===buffer.constructor?u:buffer.from(u)).toString(\"base64\")}:function(u){return(u.constructor===buffer.constructor?u:new buffer(u)).toString(\"base64\")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(String(u)):_encode(String(u)).replace(/[+\\/]/g,function(m0){return m0==\"+\"?\"-\":\"_\"}).replace(/=/g,\"\")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp([\"[À-ß][-¿]\",\"[à-ï][-¿]{2}\",\"[ð-÷][-¿]{3}\"].join(\"|\"),\"g\");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join(\"\")};var atob=global.atob?function(a){return global.atob(a)}:function(a){return a.replace(/[\\s\\S]{1,4}/g,cb_decode)};var _decode=buffer?buffer.from&&Uint8Array&&buffer.from!==Uint8Array.from?function(a){return(a.constructor===buffer.constructor?a:buffer.from(a,\"base64\")).toString()}:function(a){return(a.constructor===buffer.constructor?a:new buffer(a,\"base64\")).toString()}:function(a){return btou(atob(a))};var decode=function(a){return _decode(String(a).replace(/[-_]/g,function(m0){return m0==\"-\"?\"+\":\"/\"}).replace(/[^A-Za-z0-9\\+\\/]/g,\"\"))};var noConflict=function(){var Base64=global.Base64;global.Base64=_Base64;return Base64};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode,noConflict:noConflict};if(typeof Object.defineProperty===\"function\"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,\"fromBase64\",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,\"toBase64\",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,\"toBase64URI\",noEnum(function(){return encode(this,true)}))}}if(global[\"Meteor\"]){Base64=global.Base64}if(typeof module!==\"undefined\"&&module.exports){module.exports.Base64=global.Base64}else if(typeof define===\"function\"&&define.amd){define([],function(){return global.Base64})}return{Base64:global.Base64}});  \n  </script>\n  <script>\n    (function(r){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=r()}else if(typeof define===\"function\"&&define.amd){define([],r)}else{var e;if(typeof window!==\"undefined\"){e=window}else if(typeof global!==\"undefined\"){e=global}else if(typeof self!==\"undefined\"){e=self}else{e=this}e.base64js=r()}})(function(){var r,e,n;return function(){function r(e,n,t){function o(f,i){if(!n[f]){if(!e[f]){var u=\"function\"==typeof require&&require;if(!i&&u)return u(f,!0);if(a)return a(f,!0);var v=new Error(\"Cannot find module '\"+f+\"'\");throw v.code=\"MODULE_NOT_FOUND\",v}var d=n[f]={exports:{}};e[f][0].call(d.exports,function(r){var n=e[f][1][r];return o(n||r)},d,d.exports,r,e,n,t)}return n[f].exports}for(var a=\"function\"==typeof require&&require,f=0;f<t.length;f++)o(t[f]);return o}return r}()({\"/\":[function(r,e,n){\"use strict\";n.byteLength=d;n.toByteArray=h;n.fromByteArray=p;var t=[];var o=[];var a=typeof Uint8Array!==\"undefined\"?Uint8Array:Array;var f=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";for(var i=0,u=f.length;i<u;++i){t[i]=f[i];o[f.charCodeAt(i)]=i}o[\"-\".charCodeAt(0)]=62;o[\"_\".charCodeAt(0)]=63;function v(r){var e=r.length;if(e%4>0){throw new Error(\"Invalid string. Length must be a multiple of 4\")}var n=r.indexOf(\"=\");if(n===-1)n=e;var t=n===e?0:4-n%4;return[n,t]}function d(r){var e=v(r);var n=e[0];var t=e[1];return(n+t)*3/4-t}function c(r,e,n){return(e+n)*3/4-n}function h(r){var e;var n=v(r);var t=n[0];var f=n[1];var i=new a(c(r,t,f));var u=0;var d=f>0?t-4:t;for(var h=0;h<d;h+=4){e=o[r.charCodeAt(h)]<<18|o[r.charCodeAt(h+1)]<<12|o[r.charCodeAt(h+2)]<<6|o[r.charCodeAt(h+3)];i[u++]=e>>16&255;i[u++]=e>>8&255;i[u++]=e&255}if(f===2){e=o[r.charCodeAt(h)]<<2|o[r.charCodeAt(h+1)]>>4;i[u++]=e&255}if(f===1){e=o[r.charCodeAt(h)]<<10|o[r.charCodeAt(h+1)]<<4|o[r.charCodeAt(h+2)]>>2;i[u++]=e>>8&255;i[u++]=e&255}return i}function s(r){return t[r>>18&63]+t[r>>12&63]+t[r>>6&63]+t[r&63]}function l(r,e,n){var t;var o=[];for(var a=e;a<n;a+=3){t=(r[a]<<16&16711680)+(r[a+1]<<8&65280)+(r[a+2]&255);o.push(s(t))}return o.join(\"\")}function p(r){var e;var n=r.length;var o=n%3;var a=[];var f=16383;for(var i=0,u=n-o;i<u;i+=f){a.push(l(r,i,i+f>u?u:i+f))}if(o===1){e=r[n-1];a.push(t[e>>2]+t[e<<4&63]+\"==\")}else if(o===2){e=(r[n-2]<<8)+r[n-1];a.push(t[e>>10]+t[e>>4&63]+t[e<<2&63]+\"=\")}return a.join(\"\")}},{}]},{},[])(\"/\")});\n  </script>\n  <script>\n    window.onload = function() {\n      var base64Text = location.hash.substring(1);\n      var charset;\n      if (base64Text && /\\?\\?\\?WHISTLE_PREVIEW_CHARSET=([A-Z\\d_-]+)\\?\\?\\?/.test(location.search)) {\n        charset = RegExp.$1;\n      }\n      if (!charset) {\n        return;\n      }\n      if (base64Text.indexOf('data:') === 0) {\n        var style = 'display: -webkit-box; -webkit-box-align: center; -webkit-align-items: center;'\n          + '-webkit-box-pack: center; -webkit-justify-content: center; margin: 0px; background: #0e0e0e;';\n        document.write('<html><head></head><body style=\"' + style + '\">'\n          + '<img src=\"' + base64Text + '\"></body></html>');\n        return;\n      }\n      var pushState = history.pushState;\n      var replaceState = history.replaceState;\n      if (typeof pushState === 'function') {\n        try {\n          history.pushState = history.replaceState = function() {};\n        } catch(e) {}\n      }\n      setTimeout(function() {\n        try {\n          history.pushState = pushState;\n          history.replaceState = replaceState;\n        } catch(e) {}\n      }, 6000);\n      try {\n        if (charset.replace('-', '') === 'UTF8') {\n          document.write(Base64.decode(base64Text));\n          charset = null;\n        }\n      } catch(e) {}\n      if (charset) {\n        var decoder = new TextDecoder(charset);\n        var bytes = base64js.toByteArray(base64Text);\n        document.write(decoder.decode(new window.Uint8Array(bytes)));\n      }\n      window.Base64 = null;\n      window.base64js = null;\n    };\n  </script>\n</body>\n</html>"
  },
  {
    "path": "biz/webui/htdocs/src/css/about.css",
    "content": ".w-about-dialog .modal-dialog {\n  width: 390px;\n}\n.w-about-dialog .modal-dialog .modal-body {\n  padding-bottom: 15px;\n}\n.w-about-dialog-ctn {\n  display: inline-block;\n  padding-top: 10px;\n  margin-left: 20px;\n}\n.w-about-dialog-title {\n  margin-bottom: 10px;\n  display: block;\n}\n.w-about-dialog img {\n  width: 60px;\n  vertical-align: top;\n  margin-top: 10px;\n}\n.w-about-menu {\n  position: relative;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/base.css",
    "content": ":root {\n  --z-max: 1000000100;\n  --z-modal: 1051;\n  --z-modal-max: 1059;\n  --z-cm: 999999;\n  --z-menu: 1001;\n  --z-ctx-menu: 99999999;\n\n  --c-default: #000;\n  --c-active: #fff;\n  --c-btn: #fff;\n  --c-heavy: #333;\n  --c-editor: #eee;\n  --c-forbidden: #808000;\n  --c-disabled: #aaa;\n  --c-link: #337ab7;\n  --c-link-hover: #23527c;\n  --c-border: #ccc;\n  --c-error: red;\n  --c-risk: #f66;\n  --c-danger: #ff3a90;\n  --c-gray: #808080;\n  --c-thin: #555;\n  --c-ok: #5bbd72;\n  --c-has: #000093;\n  --c-warn: #5c3b00;\n  --c-warn-active: yellow;\n  --c-cobalt: #fff;\n\n  --c-post: #4f91cf;\n  --c-del: #ce5442;\n  --c-head: #55b499;\n  --c-options: #f0ad4e;\n  --c-trace: #5bc0de;\n  --c-patch: #8f58af;\n  --c-put: #e49b3b;\n  --c-tag: #46b8da;\n  --c-tagx: #eea236;\n  --c-rule-filter: #845dc4;\n  --c-rule-props: #ff9e59;\n  --c-req: #0d8f0d;\n  --c-res: #e75b0f;\n\n  --c-tree-success: #3c763d;\n  --c-tree-info: #31708f;\n  --c-tree-warning: #8a6d3b;\n  --c-tree-danger: #a94442;\n  --c-tree-forbidden: #aa6708;\n  --c-tree-mark: #3b88fd;\n\n  --c-j0: #002b36;\n  --c-j1: #073642;\n  --c-j2: #586e75;\n  --c-j3: #657b83;\n  --c-j4: #839496;\n  --c-j5: #93a1a1;\n  --c-j6: #eee8d5;\n  --c-j7: #fdf6e3;\n  --c-j8: #dc322f;\n  --c-j9: #cb4b16;\n  --c-ja: #b58900;\n  --c-jb: #859900;\n  --c-jc: #2aa198;\n  --c-jd: #268bd2;\n  --c-je: #6c71c4;\n  --c-jf: #d33682;\n\n  /* 文字颜色 */\n  --c-success: #3c763d;\n  --c-primary: #2e6da4;\n  --c-primary-hover: #23527c;\n  --c-primary-active: #1d4566;\n  --c-danger-hover: #ac2925;\n  --c-danger-active: #761c19;\n  --c-warning: #eea236;\n  --c-warning-hover: #d58512;\n  --c-warning-active: #985f0d;\n  --c-info: #46b8da;\n  --c-info-hover: #269abc;\n  --c-info-active: #1b6d85;\n\n  /* 背景颜色 */\n  --b-success: #dff0d8;\n  --b-success-hover: #d0e9c6;\n  --b-warning: #f0ad4e;\n  --b-warning-hover: #ec971f;\n  --b-warning-active: #d58512;\n  --b-info: #5bc0de;\n  --b-info-hover: #31b0d5;\n  --b-info-active: #269abc;\n  --b-primary: #337ab7;\n  --b-primary-hover: #286090;\n  --b-primary-active: #204d74;\n  --b-danger: #d9534f;\n  --b-danger-hover: #c9302c;\n  --b-danger-active: #ac2925;\n\n  --b-alert-info: #d9edf7;\n  --b-default: #fff;\n  --b-error: #f2dede;\n  --b-error-hover: #dab9b9;\n  --b-active: #ddd;\n  --b-gray: #ccc;\n  --b-btn-hover: #e6e6e6;\n  --b-btn-active: #d6e2fb;\n  --b-hover: #f5f5f5;\n  --b-heavy: #eee;\n  --b-heavy-active: #e0e0e0;\n  --b-disabled: #f5f5f5;\n  --b-bar: #fafafa;\n  --b-mark: #3b88fd;\n  --b-title: #f1f3f4;\n  --b-img: #0e0e0e;\n  --b-frames: #ff8000;\n  --b-prop: #e1e3e6;\n  --b-ok: #5bbd72;\n  --b-editor: #002240;\n  --b-filtered: #ffffe0;\n  --b-warn: #fffbe6;\n  --b-req: #e2f7da;\n  --b-req-hover: #cee7c0;\n  --b-blink: #ffeb3b;\n  --b-tree-active: #8fbc8f;\n\n  --b-tl-dns: #8cd2c6;\n  --b-tl-req: #fdfdb2;\n  --b-tl-res: #fbb361;\n  --b-tl-load: #7eabe1;\n\n  --b-list-hover: #e8e8e8;\n  --b-list-warn: #f8efcf;\n  --b-list-info: #c5e2f2;\n  --b-list-danger-hover: #e8c9c9;\n  --b-list-mark: #2978f0;\n  --b-modal: rgba(0, 0, 0, 0.5);\n\n  --b-cobalt: #002240;\n\n  --s-rgba5: rgba(0, 0, 0, 0.05);\n  --s-rgba8: rgba(0, 0, 0, 0.08);\n  --s-rgba12: rgba(0, 0, 0, 0.12);\n  --s-rgba15: rgba(0, 0, 0, 0.15);\n  --s-rgba20: rgba(0, 0, 0, 0.2);\n  --s-fc: rgba(102, 175, 233, .6);\n\n  --b-rgba50: rgba(0, 0, 0, 0.5);\n  --b-rgba70: rgba(0, 0, 0, 0.7);\n  --b-rgba15: rgba(0, 0, 0, 0.15);\n  --b-rgba30: rgba(0, 0, 0, 0.3);\n}\n\nhtml,\nbody,\n.main {\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n  background: var(--b-default);\n  color: var(--c-heavy);\n}\n\n#container {\n  min-width: 1078px; /* 兼容竖屏 */\n  min-height: 560px;\n}\n::-webkit-scrollbar {\n  width: 10px;\n  height: 7px;\n}\n::-webkit-scrollbar-button {\n  width: 10px;\n  height: 1px;\n}\n::-webkit-scrollbar-thumb {\n  background-clip: padding-box;\n  background-color: var(--b-rgba50);\n  border-radius: 8px;\n  min-height: 30px;\n}\n::-webkit-scrollbar-thumb:hover {\n  background-clip: padding-box;\n  background-color: var(--b-rgba70);\n  border-radius: 8px;\n}\n::-webkit-scrollbar-track,\n::-webkit-scrollbar-thumb {\n  border-left: 2px solid transparent;\n  border-right: 2px solid transparent;\n}\n::-webkit-scrollbar-track:hover {\n  background-clip: padding-box;\n  background-color: var(--s-rgba15);\n}\n\nbody::-webkit-scrollbar {\n  height: 0;\n}\n\n.cm-error-rule {\n  text-decoration: line-through !important;\n  color: #af2018 !important;\n  font-style: italic;\n}\npre {\n  background: transparent;\n  border: none;\n  padding: 0;\n  margin: 0;\n  font-size: 12px;\n  font-family: inherit;\n  display: block;\n  white-space: pre-wrap;\n  white-space: -moz-pre-wrap;\n  white-space: -pre-wrap;\n  white-space: -o-pre-wrap;\n  word-wrap: break-word;\n}\n.n-monospace {\n  font-family: monospace;\n}\nbody .CodeMirror pre {\n  font-family: consolas, monospace;\n}\ninput[type='checkbox'],\ninput[type='radio'] {\n  margin: 0;\n  vertical-align: text-bottom;\n}\n.glyphicon-ban-circle {\n  color: var(--c-risk);\n}\n\n.w-filter-input {\n  width: 100%;\n  background: var(--b-img);\n  height: 30px;\n  color: var(--c-cobalt);\n  padding: 2px 5px;\n  border: none;\n  border-top: 1px solid var(--c-border);\n  border-bottom: 1px solid var(--c-border);\n}\n.w-shadow {\n  border: 1px solid var(--c-border) !important;\n  -webkit-box-shadow: 0 6px 12px var(--s-rgba20) !important;\n  box-shadow: 0 6px 12px var(--s-rgba20) !important;\n  border-radius: 1px !important;\n}\ntextarea[readonly] {\n  outline: none;\n}\ntextarea {\n  resize: none;\n  display: block;\n  font-size: 12px;\n}\n.hide {\n  display: none !important;\n}\n\n.visible {\n  visibility: visible!important;\n}\n.w-hidden {\n  visibility: hidden!important;\n}\n\n.box {\n  display: -webkit-box;\n  display: -moz-box;\n  display: box;\n}\n.v-box {\n  -moz-box-orient: vertical;\n  -webkit-box-orient: vertical;\n  box-orient: vertical;\n  display: -moz-box;\n  display: -webkit-box;\n  display: box;\n}\n.fill {\n  -moz-box-flex: 1;\n  -webkit-box-flex: 1;\n  box-flex: 1;\n}\n.box > .fill {\n  width: 0;\n}\n.v-box > .fill {\n  height: 0;\n} /**不加这个滚动会有问题**/\n.table {\n  table-layout: fixed;\n  margin: 0 !important;\n}\n.table .w-empty {\n  text-align: center;\n  font-style: italic;\n  color: var(--c-disabled);\n  border-bottom: none !important;\n  padding: 20px 0 !important;\n}\n.w-empty-data {\n  position: absolute;\n  width: 100px;\n  text-align: center;\n  color: var(--c-disabled);\n  top: 50%;\n  left: 50%;\n  margin: -20px 0 0 -50px;\n}\n.w-expand-collapse {\n  margin-left: 5px;\n  height: 20px;\n  line-height: 14px;\n  font-size: 12px;\n  color: var(--c-default);\n}\n\n.modal-dialog .modal-body,\n.modal-dialog .modal-footer {\n  padding: 10px;\n}\n.modal-footer .btn {\n  padding: 5px 10px;\n}\n\n.cm-s-ambiance ::-webkit-scrollbar-thumb,\n.cm-s-blackboard ::-webkit-scrollbar-thumb,\n.cm-s-cobalt ::-webkit-scrollbar-thumb,\n.cm-s-erlang-dark ::-webkit-scrollbar-thumb,\n.cm-s-lesser-dark ::-webkit-scrollbar-thumb,\n.cm-s-midnight ::-webkit-scrollbar-thumb,\n.cm-s-monokai ::-webkit-scrollbar-thumb,\n.cm-s-night ::-webkit-scrollbar-thumb,\n.cm-s-dark ::-webkit-scrollbar-thumb,\n.cm-s-twilight ::-webkit-scrollbar-thumb,\n.cm-s-vibrant-ink ::-webkit-scrollbar-thumb,\n.cm-s-xq-dark ::-webkit-scrollbar-thumb {\n  background-color: rgba(255, 255, 255, 0.5);\n}\n\n.cm-s-ambiance ::-webkit-scrollbar-thumb:hover,\n.cm-s-blackboard ::-webkit-scrollbar-thumb:hover,\n.cm-s-cobalt ::-webkit-scrollbar-thumb:hover,\n.cm-s-erlang-dark ::-webkit-scrollbar-thumb:hover,\n.cm-s-lesser-dark ::-webkit-scrollbar-thumb:hover,\n.cm-s-midnight ::-webkit-scrollbar-thumb:hover,\n.cm-s-monokai ::-webkit-scrollbar-thumb:hover,\n.cm-s-night ::-webkit-scrollbar-thumb:hover,\n.cm-s-dark ::-webkit-scrollbar-thumb:hover,\n.cm-s-twilight ::-webkit-scrollbar-thumb:hover,\n.cm-s-vibrant-ink ::-webkit-scrollbar-thumb:hover,\n.cm-s-xq-dark ::-webkit-scrollbar-thumb:hover {\n  background-color: rgba(255, 255, 255, 0.7);\n}\n\n.cm-s-ambiance ::-webkit-scrollbar-track:hover,\n.cm-s-blackboard ::-webkit-scrollbar-track:hover,\n.cm-s-cobalt ::-webkit-scrollbar-track:hover,\n.cm-s-erlang-dark ::-webkit-scrollbar-track:hover,\n.cm-s-lesser-dark ::-webkit-scrollbar-track:hover,\n.cm-s-midnight ::-webkit-scrollbar-track:hover,\n.cm-s-monokai ::-webkit-scrollbar-track:hover,\n.cm-s-night ::-webkit-scrollbar-track:hover,\n.cm-s-dark ::-webkit-scrollbar-track:hover,\n.cm-s-twilight ::-webkit-scrollbar-track:hover,\n.cm-s-vibrant-ink ::-webkit-scrollbar-track:hover,\n.cm-s-xq-dark ::-webkit-scrollbar-track:hover {\n  background-color: rgba(255, 255, 255, 0.15);\n}\n\n.w-hide {\n  display: none!important;\n}\n\n.w-offline-status .w-menu a,\n.w-offline-status .w-left-menu a {\n  color: var(--c-disabled) !important;\n}\n\nbody .cm-s-cobalt span.cm-number {\n  color: #ff1605;\n}\n.w-hide-no-value .w-no-value {\n  display: none !important;\n}\n\n.w-detail .w-dock-btn {\n  border: none;\n  background: transparent;\n  padding: 4px 0;\n  width: 20px;\n  text-align: center;\n  position: absolute;\n  right: 0;\n  top: 0;\n  outline: none;\n}\n.w-detail .w-dock-btn .glyphicon {\n  margin: 0 !important;\n}\n.w-detail .w-dock-btn:hover {\n  background: var(--b-hover);\n}\n\n.btn.active,\n.btn:active {\n  box-shadow: none !important;\n}\n\n.CodeMirror-hints {\n  z-index: var(--z-cm) !important;\n}\n\n.w-mark th,\n.w-mark td {\n  background-color: var(--b-mark) !important;\n  color: var(--c-active) !important;\n}\n\n.ReactVirtualized__Grid.ReactVirtualized__List {\n  outline: none;\n}\n\n.w-not-allowed {\n  cursor: not-allowed;\n}\n\n.w-bold {\n  font-weight: bold !important;\n}\n\na {\n  cursor: pointer;\n}\n\n.w-delete {\n  color: var(--c-risk)!important;\n}\n\n.w-delete:hover {\n  color: var(--c-error)!important;\n}\n\n.w-editor-dialog .modal-dialog {\n  width: 960px;\n}\n\n.w-editor-dialog .modal-body {\n  background: var(--b-bar);\n}\n\n.w-editor-dialog textarea {\n  width: 940px;\n  height: 520px;\n  padding: 5px;\n  border-radius: 3px;\n  border: 1px solid var(--c-border);\n}\n\n.w-big-editor-dialog .modal-dialog {\n  width: 1000px;\n}\n\n.w-big-editor-dialog textarea {\n  width: 980px;\n  height: 550px;\n}\n\n.w-win-dialog {\n  z-index: calc(var(--z-modal-max) + 1);\n}\n\n.w-win-dialog .modal-dialog {\n  width: 480px;\n}\n\n.w-win-dialog pre {\n  font-size: 15px;\n  padding: 12px 10px;\n  line-height: 2;\n  margin: 0;\n  word-break: break-word;\n}\n\n.w-win-dialog .modal-body {\n  margin: 0;\n}\n\n.w-gray {\n  color: var(--c-gray);\n  margin-left: 5px;\n}\n\n.w-tabs > li > a {\n  padding: 5px 10px;\n}\n\n.w-tabs > li > a > span {\n  margin-right: 5px;\n}\n\n.modal.in {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n.glyphicon-cloud {\n  vertical-align: text-bottom;\n  padding-bottom: 1px;\n  margin-right: 3px;\n}\n\n.glyphicon-gift {\n  margin-right: 3px;\n}\n\n.glyphicon-folder-open {\n  margin-right: 6px;\n}\n\n.w-fake-iframe {\n  display: inline-block;\n  vertical-align: top;\n}\n\n.w-fix-drag {\n  position: relative;\n}\n\n.w-fix-drag::after {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n}\n\n.w-fix-drag:hover::after {\n  display: none;\n}\n\n.modal-header h4 {\n  font-size: 15px;\n  font-weight: 500;\n  display: inline;\n}\n\n.w-align-items {\n  display: flex!important;\n  align-items: center;\n}\n\n.flex-1 {\n  flex: 1;\n}\n\n.w-align-items input {\n  margin-right: 5px;\n}\n\n.w-fmt-btn .glyphicon {\n  margin-right: 5px;\n}\n\n.w-cell-img {\n  max-height: 17px;\n  max-width: 20px;\n  margin: -3px 6px 0 0;\n}\n\n.inline-align-items {\n  display: inline-flex;\n  align-items: center;\n}\n\n.w-shake-horizontal {\n    animation: shakeHorizontal 0.5s ease-in-out;\n}\n\n@keyframes shakeHorizontal {\n    0%, 100% { transform: translateX(0); }\n    20%, 60% { transform: translateX(-5px); }\n    40%, 80% { transform: translateX(5px); }\n}\n\nselect.form-control {\n  box-shadow: none;\n}\n\n.w-bold {\n  font-weight: bold !important;\n}\n\n.w-help-icon {\n  color: var(--c-heavy);\n  font-size: 14px;\n  margin-right: 5px;\n}\n.w-help-icon:hover {\n  color: var(--c-link);\n  text-decoration: none;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/btn-group.css",
    "content": ".w-tabs-sm {\n  border-top: 1px solid var(--c-border);\n  border-bottom: 1px solid var(--c-border);\n  width: 100%;\n}\n.w-tabs-sm .btn {\n  border-radius: 0;\n  border-bottom: none;\n  border-top: none;\n  padding: 4px 10px 3px;\n  overflow: hidden;\n  font-size: 14px;\n}\n.w-tabs-sm .btn:first-child {\n  border-left: none;\n}\n\n.w-btn-group-sm {\n  border-bottom: 1px solid var(--c-border);\n  width: 100%;\n}\n.w-btn-group-sm .btn {\n  padding: 2px 10px 1px !important;\n  border-radius: 0;\n  border-top: none;\n  border-bottom: none;\n}\n.w-btn-group-sm .btn:first-child {\n  border-left: none;\n}\n.w-btn-group-sm.small .btn {\n  padding: 0 8px !important;\n  font-size: 12px;\n  line-height: 16px;\n}\n\n.w-detail .w-addon-btn {\n  max-width: 85px;\n  max-width: calc(100% - 475px);\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: none;\n}\n\n.w-show-addon .w-addon-btn {\n  display: inline-block!important;\n\n}\n\n.w-show-addon .w-tabs-sm .btn {\n  padding: 4px 9px 3px;\n}\n\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/certs.css",
    "content": ".w-certs-dialog {\n  z-index: var(--z-modal);\n}\n\n.w-certs-dialog .w-help-icon {\n  font-size: 15px;\n  margin-top: -2px;\n}\n\n.w-certs-dialog .modal-dialog {\n  width: 1030px;\n}\n\n.w-certs-dialog .w-certs-active {\n  width: 72px;\n  text-align: center;\n}\n\n.w-certs-order {\n  width: 50px;\n}\n.w-certs-validity {\n  width: 338px;\n}\n.w-certs-status {\n  width: 100px;\n}\n\n.w-certs-status .glyphicon-ok {\n  color: var(--c-ok);\n}\n.w-cert-invalid th,\n.w-cert-invalid td {\n  color: var(--c-error);\n}\n\n.w-certs-filename a {\n  font-size: 12px;\n  cursor: pointer;\n}\n\n.w-certs-filename .glyphicon-lock {\n  margin-right: 5px;\n}\n\n.w-certs-dialog td {\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.w-certs-filename {\n  width: 200px;\n}\n\n.w-certs-dialog th,\n.w-certs-dialog td {\n  vertical-align: middle !important;\n}\n\n.w-certs-disabled .w-certs-order,\n.w-certs-disabled .w-certs-filename,\n.w-certs-disabled .w-certs-domain,\n.w-certs-disabled .w-certs-validity {\n  color: var(--c-disabled);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/composer.css",
    "content": ".w-detail-com {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-detail-com textarea {\n  display: block;\n  width: 100%;\n  resize: none;\n  border: none;\n  padding: 5px;\n}\n.w-com-url {\n  padding: 2px 2px 2px 0;\n  border-bottom: 1px solid var(--c-border);\n  position: relative;\n}\n\n.w-com-url .w-filter-hint {\n  width: calc(100% - 218px);\n  z-index: 2;\n  top: 30px;\n  bottom: unset;\n  left: 113px;\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-filter-hint > ul {\n  overflow-x: hidden;\n  overflow-y: auto;\n  max-height: var(--h-hints, 360px);\n}\n\n.w-filter-hint > ul li {\n  cursor: default;\n}\n\n.w-com-url .w-filter-hint a {\n  text-decoration: none;\n  left: 2px;\n}\n\n.w-com-input,\n.w-com-method,\n.w-com-execute {\n  height: 26px;\n}\n.w-com-input {\n  padding: 0 5px;\n  display: block;\n  border: 1px solid var(--c-border);\n  border-left: none;\n  border-right: none;\n}\n.w-com-history-btn {\n  height: 26px;\n  width: 24px;\n  text-align: center;\n  margin: 0 1px 0 2px!important;\n  line-height: 26px;\n  cursor: pointer;\n  font-size: 15px;\n  color: var(--c-thin);\n}\n.w-com-history-btn:hover {\n  color: var(--c-link);\n  background: var(--b-hover);\n}\n.w-com-method {\n  display: block;\n  width: 85px;\n  margin: 0 0 0 2px;\n  padding: 0;\n  font-size: 12px;\n  font-weight: bold;\n  border-radius: 0;\n  border-top-left-radius: 4px;\n  border-bottom-left-radius: 4px;\n}\n.w-com-execute, .w-com-params {\n  padding: 0 8px;\n  line-height: 24px;\n  margin-left: 4px;\n}\n\n.w-com-params {\n  border-radius: 0;\n  margin-left: 0;\n  border-top-right-radius: 4px;\n  border-bottom-right-radius: 4px;\n  background-color: var(--b-bar);\n  font-size: 12px;\n  font-weight: bold;\n}\n\n.w-com-params-editor {\n  position: absolute;\n  width: 500px;\n  max-width: calc(100% - 40px);\n  height: 240px;\n  z-index: 10;\n  top: 30px;\n  right: 40px;\n}\n\n.w-com-params-editor .w-filter-bar {\n  border-bottom: 1px solid var(--c-border);\n}\n\n.w-com-params-editor .w-filter-bar a {\n  left: unset;\n  right: 5px;\n  top: 1px;\n}\n\n.w-com-params-editor .w-filter-bar .w-params-clear-btn {\n  right: 60px;\n  white-space: nowrap;\n  top: -1px;\n}\n\n.w-com-params-editor .w-filter-bar .w-com-params-clear {\n  right: 60px;\n}\n\n.w-com-rules-label {\n  padding-right: 12px;\n  border-right: 1px solid var(--c-border);\n}\n\n.w-com-proxy-rules {\n  padding-left: 15px;\n  font-weight: 400;\n}\n\n.w-com-params-editor .w-filter-bar {\n  text-align: left;\n}\n\n.w-com-tabs {\n  position: relative;\n  padding: 0 !important;\n}\n.w-com-tabs .btn {\n  position: absolute;\n  top: 1px;\n  right: 10px;\n  font-size: 13px;\n  line-height: 14px;\n}\n\n.w-com-tabs button {\n  line-height: 16px;\n  padding: 0 10px !important;\n}\n.w-com-tabs button:hover {\n  background: var(--b-btn-hover);\n}\n.w-com-tabs .w-tab-btn {\n  outline: none;\n  border: none;\n  background: transparent;\n  height: 100%;\n}\n.w-com-tabs .w-tab-btn:hover {\n  color: var(--c-link);\n}\n.w-com-tabs .w-tab-btn.w-active {\n  background: var(--b-active);\n}\n.w-com-tabs .w-tab-btn:disabled {\n  color: var(--c-disabled) !important;\n  cursor: not-allowed;\n}\n\n.w-com-res {\n  position: relative;\n}\n\n.w-com-res-forbidden th,\n.w-com-res-forbidden pre {\n  color: var(--c-forbidden)!important;\n}\n\n.w-com-res-error th,\n.w-com-res-error pre {\n  color: var(--c-error) !important;\n}\n.w-com-bar {\n  border-bottom: 1px solid var(--c-border);\n  line-height: 18px;\n  height: 18px;\n  overflow: hidden;\n  background-color: var(--b-bar);\n  position: relative;\n}\n\n.w-com-bar .btn {\n  line-height: 13px;\n  height: 15px;\n  padding: 0 5px;\n  font-weight: normal;\n  font-size: 11px;\n  border-radius: 2px;\n  outline: none;\n  margin-right: 5px;\n  margin-bottom: 1px;\n  overflow: hidden;\n}\n.w-com-body .w-record-status {\n  font-size: 13px;\n}\n.w-com-body .w-com-bar {\n  text-align: right;\n  height: 19px;\n}\n.w-com-bar,\n.w-com-bar label {\n  font-size: 12px;\n  font-weight: normal;\n}\n.w-com-bar label {\n  margin-left: 10px;\n  cursor: pointer;\n  display: inline-flex;\n  align-items: center;\n  margin-bottom: 0;\n}\n.w-com-bar input,\n.w-com-rules .w-detail-inspectors-title input {\n  margin: 0 5px 0 0;\n  padding: 0;\n}\n\n.w-com-res {\n  position: relative;\n}\ntextarea.w-com-rules {\n  margin-bottom: 2px;\n}\n.w-com-bar label:first-child {\n  font-weight: bold;\n}\n.w-com-bar label:hover {\n  color: var(--c-default);\n}\n.w-com-bar .w-com-label {\n  font-weight: bold;\n  display: inline-flex;\n  align-items: center;\n  padding: 0 0 0 15px;\n  border-left: 1px solid var(--c-border);\n  cursor: default;\n  color: var(--c-default) !important;\n}\n.w-com-body .w-com-encoding {\n  position: absolute;\n  top: 1px;\n  left: 60px;\n}\n.w-com-body > .w-com-bar > .w-com-label {\n  padding: 0 10px !important;\n  position: absolute;\n  left: 0;\n  top: 1px;\n  font-weight: bold;\n  border: none;\n  padding: 0;\n  margin: 0;\n  border-right: 1px solid var(--c-border);\n}\n.w-com-rules .w-detail-inspectors-title {\n  padding-left: 10px !important;\n}\n.w-com-rules .w-detail-inspectors-title label {\n  margin: 0;\n  display: inline-flex;\n  align-items: center;\n}\n\n.w-com-rules .w-detail-inspectors-title label:hover {\n  color: var(--c-default);\n}\n\n.w-com-headers .w-com-bar {\n  display: flex;\n  align-items: center;\n}\n\n.w-com-headers textarea[readonly],\n.w-com-body textarea[readonly],\n.w-detail-com input[readonly],\n.w-com-rules textarea:disabled,\n.w-com-rules textarea[readonly],\n.w-mock-key-name[readonly] {\n  background: var(--b-disabled) !important;\n  color: var(--c-disabled) !important;\n  cursor: default;\n}\n\n.w-com-cookies-dialog table th {\n  padding: 5px 0 5px 8px;\n}\n.w-com-cookie-order {\n  width: 50px;\n}\n.w-com-cookie-value {\n  white-space: break-spaces;\n  word-break: break-all;\n}\n.w-com-cookie-operation {\n  width: 160px;\n}\n.w-com-history-operation .btn, .w-com-cookie-operation a {\n  padding: 2px 10px !important;\n  margin-right: 10px;\n  display: inline-block;\n}\n.w-com-history-dialog .modal-dialog {\n  width: 1060px;\n}\n\n.w-com-cookies-dialog .modal-dialog {\n  width: 720px;\n}\n\n.w-com-bar .w-com-hex-text {\n  position: absolute;\n  left: 75px;\n  top: 0px;\n}\n\n.w-com-bar .w-com-crlf {\n  top: 0;\n  left: 150px;\n  position: absolute;\n}\n\n.w-com-bar .w-com-hex-text.w-checked,\n.w-com-bar .w-com-gzip.w-checked,\n.w-com-bar .w-com-crlf.w-checked {\n  font-weight: bold;\n}\n\n.w-com-back-btn {\n  position: absolute;\n  padding: 2px 0;\n  width: 40px;\n  text-align: center;\n  position: absolute;\n  left: 0;\n  top: 0;\n  z-index: 1;\n  height: 23px;\n  border: 0;\n  border-radius: 0;\n  background-color: var(--b-filtered);\n}\n\n.w-com-use-h2 {\n  right: 5px;\n}\n\n.w-com-history {\n  right: 2px;\n  position: absolute;\n}\n\n.w-com-pretty,\n.w-com-use-h2, .w-com-enable-body {\n  margin-left: 13px!important;\n  font-weight: normal;\n}\n\n.w-com-tabs input {\n  margin-right: 5px;\n}\n\n.w-com-methods .modal-dialog {\n  width: 500px;\n}\n\n.w-com-methods textarea {\n  width: 100%;\n  height: 200px;\n  font-size: 14px;\n}\n\n.w-com-tab-list {\n  border-bottom: 1px solid var(--c-border);\n  height: 23px;\n  background-color: var(--b-title);\n  min-width: 560px;\n}\n\n.w-com-tab-list button {\n  border: none;\n  border-right: 1px solid var(--c-border);\n  height: 22px;\n  line-height: 18px;\n  font-size: 12px;\n  padding: 0 12px;\n  border-radius: 0;\n  font-weight: bold;\n}\n\n.w-com-list {\n  position: relative;\n}\n.w-mask-iframe {\n  background-color: transparent;\n  position: absolute;\n  left: 0;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  z-index: 1;\n}\n\n.w-com-list .w-custom-tabs {\n  height: 22px;\n}\n\n.w-hide-history {\n  position: absolute;\n  top: 0;\n  right: 5px;\n  font-size: 16px;\n  cursor: pointer;\n  line-height: 1;\n}\n\n.w-history-title {\n  background: var(--b-title);\n  padding: 3px 5px;\n  overflow: hidden;\n  position: sticky;\n  top: 0;\n  font-weight: 500;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  color: var(--c-default);\n}\n\n.w-history-item, .w-history-title {\n  border-bottom: 1px solid var(--c-border);\n}\n\n.w-history-item {\n  padding: 6px 5px 8px 10px;\n  overflow: hidden;\n  cursor: default;\n  background-color: var(--b-bar);\n}\n\n.w-history-item div {\n  overflow : hidden;\n  text-overflow: ellipsis;\n  display: -webkit-box;\n  -webkit-line-clamp: 5;\n  -webkit-box-orient: vertical;\n  max-height: 86px;\n  word-wrap: break-word;\n  word-break: break-all;\n}\n\n.w-history-item p {\n  margin: 0;\n  padding: 2px 0 0;\n    white-space: nowrap;\n}\n\n.w-history-item i {\n  display: inline-block;\n  font-style: normal;\n  padding: 0 5px;\n  line-height: 16px;\n  margin-right: 5px;\n  font-size: 9px;\n  border-radius: 3px;\n  font-weight: normal;\n  line-height: 12px;\n  font-weight: 500;\n}\n\n.w-history-item i.w-req-method-tag-GET {\n  background: var(--c-ok);\n  color: var(--c-active);\n  border-color: var(--c-ok);\n}\n\n.w-history-item i.w-req-method-tag-POST {\n  background: var(--c-post);\n  color: var(--c-active);\n  border-color: var(--c-post);\n}\n\n.w-history-item i.w-req-method-tag-DELETE {\n  background: var(--c-del);\n  color: var(--c-active);\n  border-color: var(--c-del);\n}\n\n.w-history-item i.w-req-method-tag-HEAD {\n  background: var(--c-head);\n  color: var(--c-active);\n  border-color: var(--c-head);\n}\n\n.w-history-item i.w-req-method-tag-OPTIONS {\n  background: var(--c-options);\n  color: var(--c-active);\n  border-color: var(--c-options);\n}\n\n.w-history-item i.w-req-method-tag-TRACE {\n  background: var(--c-trace);\n  color: var(--c-active);\n  border-color: var(--c-trace);\n}\n\n.w-history-item i.w-req-method-tag-PATCH {\n  background: var(--c-patch);\n  color: var(--c-active);\n  border-color: var(--c-patch);\n}\n\n.w-history-item i.w-req-method-tag-PUT {\n  background: var(--c-put);\n  color: var(--c-active);\n  border-color: var(--c-put);\n}\n\n.w-history-item i:last-child {\n  margin-right: 0;\n}\n\n.w-history-item:hover {\n  background: var(--b-hover);\n  color: var(--c-default);\n}\n.w-history-item.w-selected {\n  background: var(--b-active);\n  color: var(--c-default);\n}\n\n.w-req-protocol-tag {\n  border: 1px solid var(--c-tag);\n}\n\n.w-req-method-tag {\n  border: 1px solid var(--c-ok);\n}\n\n.w-req-type-tag {\n  border: 1px solid var(--c-tagx);\n}\n\n.w-com-history-data {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  z-index: 9;\n  opacity: 0;\n  transition: ease 0.6s;\n  visibility: hidden;\n}\n\n.w-com-history-data.w-show {\n  opacity: 1;\n  visibility: visible;\n}\n\n.w-com-history-data .close {\n  position: absolute;\n  right: 10px;\n  top: 0;\n  cursor: pointer;\n  z-index: 1;\n  font-size: 20px;\n}\n\n.w-com-history-footer .close {\n  top: 6px;\n}\n\n.w-com-history-list {\n  border-right: 1px solid var(--c-border);\n  overflow-x: hidden;\n  overflow-y: auto;\n  height: 100%;\n  background: var(--b-bar);\n  position: relative;\n  font-size: 12px;\n}\n\n.w-com-history-ctn pre {\n  display: block;\n  padding: 0 5px;\n  overflow: auto;\n  white-space: pre;\n  background-color: var(--b-disabled);\n  position: relative;\n}\n\n.w-com-history-ctn pre .w-load-tips {\n  position: absolute;\n  z-index: 1;\n  background: var(--b-rgba30);\n  left: 0;\n  right: 0;\n  top: 0;\n  bottom: 0;\n  color: var(--c-active);\n  font-size: 14px;\n  display: none;\n  align-items: center;\n  justify-content: center;\n}\n\n.w-com-history-ctn pre.w-show-loading {\n  overflow: hidden;\n}\n\n.w-com-history-ctn pre.w-show-loading .w-load-tips {\n  display: flex;\n}\n\n.w-com-history-footer {\n  line-height: 33px;\n  white-space: nowrap;\n  overflow: hidden;\n  border-bottom: 1px solid var(--c-border);\n  padding: 0 0 2px;\n  position: relative;\n}\n\n.w-com-history-footer .btn {\n  height: 24px;\n  line-height: 22px;\n  padding: 0 6px;\n  margin-left: 10px;\n  font-size: 13px;\n}\n\n.w-com-btns {\n  position: absolute;\n  top: 0;\n  right: 0;\n}\n\n.w-com-btns a {\n  color: var(--c-default);\n  font-weight: normal;\n  margin-right: 10px;\n  text-decoration: none;\n}\n\n.w-com-btns a:last-child {\n  margin-right: 5px;\n}\n\n.w-com-btns a:hover {\n  color: var(--c-link);\n}\n\n.w-com-disable-body .w-props-editor {\n  opacity: .7;\n}\n\n.w-view-inspectors-btn {\n  position: absolute;\n  top: 27px;\n  right: 5px;\n  z-index: 1;\n  display: block;\n  padding: 0 5px;\n  background: var(--b-bar);\n  border-radius: 3px;\n  font-size: 12px;\n  text-decoration: none!important;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/context-menu.css",
    "content": "/* Context Menu Style by marvin1023 */\n.w-ctx-menu {\n  position: absolute;\n  z-index: var(--z-ctx-menu);\n}\n\n.w-ctx-menu-list {\n  list-style: none outside none;\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  box-sizing: border-box;\n  border: 1px solid var(--c-border);\n  box-shadow: 2px 2px 2px var(--s-rgba15);\n  background: var(--b-default);\n  min-width: 110px;\n  border-radius: 4px;\n}\n\n.w-ctx-menu-list .w-ctx-item-tt input {\n  margin-right: 5px;\n  vertical-align: text-top;\n}\n\n.w-ctx-menu-list .w-ctx-item-tt .glyphicon {\n  margin-right: 5px;\n}\n\n.w-ctx-menu-list .glyphicon-play {\n  line-height: 28px;\n  font-size: 10px;\n  margin-left: 10px;\n  transform: scale(.8);\n}\n.w-ctx-menu-list label {\n  margin: 0 !important;\n  font-weight: normal !important;\n  display: block;\n}\n.w-ctx-menu-list .w-ctx-selected {\n  font-weight: bold!important;\n}\n\n.w-ctx-menu-list .w-ctx-checked {\n  display: inline-block;\n  width: 20px;\n  visibility: hidden;\n}\n\nbody .w-ctx-menu-list .w-ctx-menu-list {\n  margin-left: 3px;\n  display: none;\n  top: -1px;\n  z-index: calc(var(--z-ctx-menu) + 2);\n}\nbody .w-ctx-menu-list .w-ctx-menu-gap {\n  position: absolute;\n  margin-left: -2px;\n  height: 35px;\n  top: -1px;\n  left: 100%;\n  width: 6px;\n  display: none;\n  z-index: calc(var(--z-ctx-menu) + 1);\n}\n.w-ctx-menu-item:hover .w-ctx-menu-list,\n.w-ctx-menu-item:hover .w-ctx-menu-gap {\n  display: block;\n}\n.w-ctx-menu-item:hover .w-ctx-menu-list {\n  width: auto;\n}\n.w-ctx-menu-item {\n  display: flex;\n  line-height: 30px;\n  padding: 0 15px;\n  justify-content: space-between;\n  position: relative;\n  cursor: default;\n  white-space: nowrap;\n}\n.w-ctx-menu-item:hover {\n  background: var(--b-title);\n}\n\n.w-ctx-item-sep {\n  border-top: 1px solid var(--c-border);\n  margin-top: 5px;\n}\n\n.w-ctx-item-disabled {\n  color: var(--c-gray);\n}\n.w-ctx-item-disabled:hover {\n  background: var(--b-default) !important;\n}\n\n\n.w-ctx-menu-item .w-ctx-menu-list {\n  position: absolute;\n  left: 100%;\n  top: 0;\n}\n\n.w-ctx-sub-menu-left .w-ctx-sub-menu-list .w-ctx-menu-list {\n  left: -132px;\n  top: -61px;\n}\n\n.w-keep-history-data .w-ctx-sub-menu-list .w-ctx-menu-list {\n  top: -61px;\n}\n\n.w-ctx-menu-left .w-ctx-menu-item .w-ctx-menu-list {\n  right: 102%;\n  left: unset;\n}\n\n.w-ctx-menu-left .w-ctx-menu-list .w-ctx-menu-gap {\n  left: -3px;\n}\n\n.w-ctx-radio-list {\n  min-width: 165px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/detail.css",
    "content": ".w-detail {\n  border-left: 1px solid var(--c-border);\n  overflow-x: auto;\n  overflow-y: hidden;\n}\n.w-detail-ctn {\n  position: relative;\n  overflow: auto;\n}\n.w-detail-divider {\n  position: absolute;\n  left: 0;\n  top: 0;\n  width: 5px;\n  height: 100%;\n  cursor: ew-resize;\n}\n.w-detail-ctn,\n.w-detail > .w-tabs-sm,\n.w-detail > .w-order-table {\n  min-width: 560px;\n  border-top: none;\n}\n\n.w-show-history .w-detail-ctn {\n  min-width: auto;\n}\n\n.w-inspectors-ctx-menu .w-ctx-menu-list {\n  min-width: 100px;\n}\n\n.w-detail-inspectors {\n  min-width: 560px;\n}\n\n.w-detail-bottom.w-detail {\n  border-left: none;\n}\n\n.w-detail-custom-tab {\n  max-width: 12.1%;\n  max-width: calc(100% - 492px);\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.w-req {\n  max-width: calc(100% - 450px);\n}\n\n.w-plugins-tabs-list {\n  width: 106px;\n  border-right: 1px solid var(--c-border);\n  overflow-x: hidden;\n  overflow-y: auto;\n  background-color: var(--b-title);\n}\n\n.w-plugins-tabs-list .btn {\n  border-radius: 0;\n  border: none;\n  border-bottom: 1px solid var(--c-border);\n  white-space: normal;\n  word-wrap: break-word;\n  word-break: break-all;\n  font-size: 12px;\n  padding: 3px 5px;\n  line-height: 18px;\n  display: block;\n  width: 100%;\n}\n\n.w-custom-tab-panel .w-tab-frame {\n  min-width: 560px;\n}\n\ndiv.w-detail-inspectors-tabs {\n  padding: 0 !important;\n}\n\n.w-detail-inspectors-tabs button {\n  border: none;\n  border-right: 1px solid var(--c-border);\n  height: 20px;\n  line-height: 16px;\n  font-size: 12px;\n  padding: 0 12px;\n  border-radius: 0;\n  font-weight: bold;\n}\n\n.w-detail .glyphicon-menu-hamburger {\n  color: var(--b-frames);\n}\n\n.w-detail-inspectors-tabs button.w-spec-active {\n  background-color: var(--b-active);\n}\n\n.w-detail-inspectors-title.w-detail-inspectors-res {\n  padding-left: 12px !important;\n}\n\n.w-custom-tabs {\n  overflow-y: hidden;\n  overflow-x: auto;\n  white-space: nowrap;\n  height: 20px;\n}\n\n.w-custom-tabs button {\n  font-weight: normal !important;\n}\n\n.w-no-frames {\n  position: absolute;\n  width: 100px;\n  height: 20px;\n  line-height: 20px;\n  left: 50%;\n  top: 50%;\n  font-size: 14px;\n  margin: -10px 0 0 -50px;\n  text-align: center;\n  color: var(--c-gray);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/divider.css",
    "content": ".w-divider-left,\n.w-divider-right {\n  overflow: hidden;\n  position: relative;\n}\n.w-divider-con.box > .w-divider-right {\n  min-width: 5px !important;\n}\n.w-divider-con.v-box > .w-divider-right {\n  min-height: 5px !important;\n}\n.w-divider {\n  position: absolute;\n  z-index: 1;\n}\n.w-divider-con.box > div > .w-divider {\n  top: 0;\n  width: 6px;\n  height: 100%;\n  cursor: ew-resize;\n}\n.w-divider-con.box > .w-divider-left > .w-divider {\n  right: 0;\n  border-right: 1px solid var(--c-border);\n}\n.w-divider-con.box > .w-divider-right > .w-divider {\n  left: 0;\n  border-left: 1px solid var(--c-border);\n}\n.w-divider-con.v-box > div > .w-divider {\n  left: 0;\n  height: 5px;\n  width: 100%;\n  cursor: ns-resize;\n}\n.w-divider-con.v-box > .w-divider-left > .w-divider {\n  bottom: 0;\n  border-bottom: 1px solid var(--c-border);\n}\n.w-divider-con.v-box > .w-divider-right > .w-divider {\n  top: 0;\n  border-top: 1px solid var(--c-border);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/dropdown.css",
    "content": ".w-dropdown-text {\n  max-width: 300px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  cursor: pointer;\n}\n.w-dropdown .dropdown-menu .divider {\n  margin: 0 !important;\n}\n.w-dropdown .dropdown-menu {\n  width: auto !important;\n  max-width: 300px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  min-width: auto !important;\n  display: none;\n  max-height: 360px;\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-dropdown {\n  display: inline-block;\n}\n\n.w-dropdown .dropdown-menu li {\n  padding: 0 10px;\n  cursor: pointer;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  white-space: nowrap;\n}\n.w-dropdown .dropdown-menu li:hover {\n  background: var(--b-hover);\n}\n.w-dropdown .dropdown-menu a {\n  padding: 0 10px;\n  line-height: 20px;\n  display: block;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/editor-settings.css",
    "content": ".w-editor-settings label,\n.w-editor-settings-box label {\n  font-weight: normal;\n  white-space: nowrap;\n}\n.w-editor-settings label .w-label {\n  display: inline-block;\n  width: 60px;\n  text-align: right;\n  margin-right: 5px;\n}\n.w-editor-settings select {\n  width: 186px;\n}\n.w-rules-settings-dialog .w-editor-settings select {\n  width: 260px;\n}\n.w-editor-settings-box {\n  padding-left: 65px;\n  margin: 10px !important;\n}\n.w-editor-settings .form-control {\n  display: inline-block;\n  margin-left: 5px;\n}\n.w-editor-settings p {\n  margin: 5px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/editor.css",
    "content": ".CodeMirror-hints li.CodeMirror-hint-active:after {\n  content: 'F1';\n  position: absolute;\n  right: 0;\n  font-size: 12px;\n}\n\n.CodeMirror-hints .CodeMirror-hint {\n  max-width: unset;\n  position: relative;\n  padding-right: 20px;\n  font-family: consolas, monospace;\n  font-size: 14px;\n}\n\n.CodeMirror-search-label {\n  white-space: nowrap;\n  width: calc(100% - 320px);\n}\n\n.CodeMirror-search-label .CodeMirror-search-field {\n  width: calc(100% - 50px)!important;\n}\n\n.cm-s-cobalt.CodeMirror {\n  color: var(--c-cobalt);\n  background: var(--b-cobalt);\n}\n\n.cm-s-cobalt .CodeMirror-gutters {\n  background: var(--b-cobalt);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/files-dialog.css",
    "content": ".w-files-dialog .modal-dialog {\n  width: 900px;\n}\n\n.w-files-order {\n  width: 36px;\n}\n.w-files-date {\n  width: 205px;\n}\n.w-files-path {\n  word-break: break-all;\n  word-wrap: break-word;\n}\n.w-files-operation {\n  width: 240px;\n}\n\n.w-files-operation a {\n  margin-right: 15px;\n  display: inline-block;\n  white-space: nowrap;\n}\n\n.w-files-operation a:last-child {\n  color: var(--c-risk);\n}\n\n.w-files-operation a:last-child:hover {\n  color: var(--c-error);\n}\n\n.w-files-dialog thead th {\n  padding: 8px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/filter-input.css",
    "content": ".w-filter-con {\n  position: relative;\n}\n.w-filter-con .w-clear-input {\n  position: absolute;\n  right: 5px;\n  bottom: 10px;\n  line-height: 16px;\n  display: none;\n}\n\n\n.w-filter-hint, .w-layer {\n  background-color: var(--b-default);\n  border-radius: 3px;\n  -webkit-box-shadow: 0 3px 6px -4px var(--s-rgba12),\n    0 6px 16px 0 var(--s-rgba8), 0 9px 28px 8px var(--s-rgba5);\n  box-shadow: 0 3px 6px -4px var(--s-rgba12),\n    0 6px 16px 0 var(--s-rgba8), 0 9px 28px 8px var(--s-rgba5);\n}\n\n.w-filter-hint {\n  margin: 0 3px;\n  bottom: 35px;\n  width: 99%;\n  padding-bottom: 3px;\n  color: var(--c-thin);\n  line-height: 1.5;\n  position: absolute;\n  overflow: hidden;\n  font-size: 14px;\n}\n\n.w-filter-show-types .w-filter-hint {\n  bottom: 60px;\n}\n\n.w-filter-hint ul,\n.w-filter-hint li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.w-filter-hint li {\n  line-height: 24px;\n  font-size: 12px;\n  border-radius: 3px;\n  padding: 0 5px;\n}\n\n.w-filter-bar {\n  text-align: right;\n  height: 20px;\n  position: relative;\n}\n\n.w-filter-bar .close {\n  text-align: center;\n  top: 0;\n  right: 5px;\n  position: absolute;\n}\n\n.w-filter-bar .w-close-params {\n  right: unset;\n  top: -4px;\n  left: 5px;\n}\n\n.w-filter-bar .w-clear-hints {\n  top: -3px;\n}\n\n.w-filter-bar a {\n  left: 3px;\n  position: absolute;\n  font-size: 12px;\n}\n.w-filter-bar a span {\n  width: 18px;\n  line-height: 18px;\n  text-align: center;\n  display: inline-block;\n  cursor: pointer;\n}\n\n.w-filter-hint li:hover,\n.w-filter-hint .w-active {\n  background-color: var(--b-hover) !important;\n}\n\n.w-filter-type-bar {\n  display: flex;\n  height: 26px;\n  align-items: center;\n  white-space: nowrap;\n  overflow-x: auto;\n  background: var(--b-bar);\n}\n\n.w-filter-type-bar span {\n  margin-left: 10px;\n  border: 1px solid var(--c-border);\n  border-radius: 3px;\n  padding: 0 6px;\n  height: 18px;\n  font-size: 12px;\n  box-sizing: border-box;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background-color: var(--b-default);\n  min-width: 32px;\n}\n\n.w-filter-type-bar span:hover {\n  border-color: var(--b-btn-hover);\n  background: var(--b-btn-hover);\n}\n\n.w-filter-type-bar span:active, .w-filter-type-bar span.w-active {\n  border-color: var(--b-btn-active);\n  background: var(--b-btn-active);\n  color: var(--c-default);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/frames.css",
    "content": ".w-frames {\n  font-size: 12px;\n  min-width: 560px;\n}\n.w-frames-action {\n  background: var(--b-title);\n  position: relative;\n  border-bottom: 1px solid var(--c-border);\n  height: 22px;\n  line-height: 20px;\n  min-width: 560px;\n}\n.w-frames-action > .w-menu-wrapper:first-child > a:first-child {\n  margin-left: 10px;\n}\n\n.w-frames-action a {\n  margin-left: 15px;\n  text-decoration: none !important;\n  color: var(--c-default);\n  vertical-align: super;\n}\n.w-frames-action .w-menu-item {\n  top: 23px;\n}\n.w-frames-action .w-menu-wrapper {\n  display: inline-block;\n}\n.w-frames-action .w-connect-abort {\n  right: 157px;\n}\n.w-frames-action a:last-child {\n  right: 10px;\n}\n.w-frames-action a:hover {\n  color: var(--c-link);\n}\n\n.w-frames-list {\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n\n.w-frames-list ul {\n  list-style: none;\n  margin: 0;\n  padding: 0 0 5px;\n}\n\n.w-frames-list ul .glyphicon {\n  margin-right: 10px;\n}\n\n.w-frames-list li {\n  cursor: default;\n  margin: 0;\n  padding: 0 5px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  height: 24px;\n  line-height: 23px;\n  list-style: none;\n  border-bottom: 1px solid var(--c-border);\n  background: var(--b-default);\n}\n.w-frames-list li[class=\"\"]:hover {\n  background: var(--b-hover);\n}\n\n.w-frames-list .w-frames-send:hover {\n  background: var(--b-req-hover);\n}\n\n.w-frames-list .w-conn-error:hover {\n  background: var(--b-error-hover);\n}\n\n.w-frames-list .w-conn-closed:hover {\n  background: var(--b-gray);\n}\n\n\n.w-frames-action .w-switch-btn .w-menu-item {\n  left: 10px;\n}\n\n.w-frames-action .w-dropdown {\n  margin-left: 15px;\n}\n\n.w-not-decompressed {\n  font-style: italic;\n}\n\n.w-not-decompressed em {\n  color: var(--c-error);\n  margin-right: 5px;\n}\n\n.w-frames-action .w-filter-con {\n  margin-right: 160px;\n  border-right: 1px solid var(--c-border);\n}\n\n.w-frames-list .w-frames-send {\n  background: var(--b-req);\n}\n.w-frames-list .w-conn-error {\n  background: var(--b-error);\n  color: var(--c-error);\n}\n.w-frames-list .w-conn-closed {\n  background: var(--b-active);\n  color: var(--c-error);\n}\n.w-conn-closed span {\n  color: var(--c-error);\n}\n.w-frames-list .w-frames-selected {\n  background: var(--c-link) !important;\n  color: var(--c-active) !important;\n}\n\n.w-frames-list .w-frames-selected .glyphicon {\n  color: var(--c-active);\n}\n\n.w-frames-com-action {\n  border-bottom: 1px solid var(--c-border);\n  position: relative;\n  text-indent: 10px;\n  line-height: 22px;\n  height: 23px;\n  background: var(--b-title);\n}\n\n.w-frames-com-action .btn-group,\n.w-frames-com-action .w-format-json-btn {\n  margin: 1px 15px 1px 0;\n  float: right;\n  font-size: 12px !important;\n}\n.w-frames-com-action .btn {\n  padding: 0 5px !important;\n  height: 20px;\n  line-height: 19px;\n}\n.w-frames-com-action .dropdown-menu {\n  min-width: 0;\n  right: 0;\n  font-size: 12px;\n}\n.w-frames-com-action .dropdown-menu a {\n  padding: 0;\n}\n\n.w-detail-inspectors .glyphicon-arrow-right,\n.w-frames-list .glyphicon-arrow-right,\n.w-com-tabs .glyphicon-arrow-right,\n.w-frames-com-action .glyphicon-arrow-right {\n  color: var(--c-req);\n}\n\n.w-detail-inspectors .glyphicon-arrow-left,\n.w-frames-list .glyphicon-arrow-left,\n.w-com-tabs .glyphicon-arrow-left,\n.w-frames-com-action .glyphicon-arrow-left {\n  color: var(--c-res);\n}\n.w-frames-data textarea {\n  border: none;\n}\n\n.w-frames-list .w-frames-ignore,\n.w-frames-list .w-frames-ignore .glyphicon {\n  color: var(--c-disabled) !important;\n  font-style: italic !important;\n}\n\n.w-frames-list .w-frames-ignore.w-frames-selected,\n.w-frames-list .w-frames-ignore.w-frames-selected .glyphicon {\n  color: var(--c-active) !important;\n}\n.w-frames-com-action .w-frames-hex-data {\n  margin-left: -10px;\n  font-weight: normal;\n}\n\n.w-frames-crlf {\n  font-weight: normal;\n  margin-left: 1px;\n}\n.w-frames-com-action .w-frames-checked {\n  font-weight: bold;\n}\n.w-frames-com-action input[type='checkbox'] {\n  vertical-align: text-bottom;\n  margin-right: 5px;\n}\n\n.w-frames-com textarea {\n  padding: 5px;\n}\n\n.w-frames-bin {\n  font-weight: 500;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/iframe-dialog.css",
    "content": "\n.w-iframe-dialog .modal-dialog {\n  width: max(calc(100% - 240px), 720px);\n}\n\n.w-iframe-dialog .modal-body {\n  padding: 0;\n  margin: 0;\n  width: 100%;\n  height: max(calc(100vh - 120px), 600px);\n}\n\n.w-iframe-dialog .modal-body iframe {\n  width: 100%;\n  height: 100%;\n  display: block;\n  border: none;\n  outline: none;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/iframe.css",
    "content": ".w-iframe {\n  position: relative;\n  min-width: 560px;\n}\n\n.w-iframe > iframe.fill {\n  display: block;\n  width: 100%;\n  height: 100%;\n  outline: none;\n  border: none;\n  margin: 0;\n  padding: 0;\n}\n\n.w-iframe[allow-dragover=\"1\"] > iframe.fill {\n  pointer-events: none;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/image-view.css",
    "content": ".w-image-view {\n  font-size: 0;\n  white-space: nowrap;\n  overflow: auto;\n  text-align: center;\n  position: relative;\n}\n.w-image-bg {\n  background: var(--b-img);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n.w-image-view.w-image-webview {\n  padding: 0;\n  display: flex;\n}\n.w-image-view img {\n  max-width: 100%;\n}\n.w-image-view .w-image-link {\n  display: inline-block;\n  width: 300px;\n  line-height: 30px;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  margin: -15px 0 0 -150px;\n  font-size: 14px;\n}\n\n.w-image-view:hover .w-textarea-bar {\n  display: block;\n}\n\n.w-image-webview .fill {\n  width: 100%;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/import-dialog.css",
    "content": ".w-ie-dialog .modal-dialog {\n  width: 600px;\n}\n\n.w-ie-dialog .modal-body {\n  background-color: var(--b-bar);\n  padding: 20px 10px;\n}\n\n.w-ie-dialog input {\n  width: 578px;\n  height: 36px;\n  line-height: 36px;\n  padding: 5px;\n  border: 1px solid var(--c-border);\n}\n\n.w-ie-dialog .modal-footer {\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/index.css",
    "content": ".w-menu {\n  height: 34px;\n  border-bottom: 1px solid var(--c-border);\n  background: var(--b-title);\n  padding-right: 80px;\n  position: relative;\n  z-index: var(--z-menu);\n  white-space: nowrap;\n}\n.w-menu a {\n  text-decoration: none !important;\n  color: var(--c-heavy);\n  padding: 0 8px;\n  line-height: 26px;\n  height: 26px;\n  display: inline-block;\n  height: 31px;\n  line-height: 31px;\n  overflow: hidden;\n}\n.w-menu .glyphicon,\n.w-detail .glyphicon {\n  margin-right: 3px;\n}\n.w-menu .glyphicon-folder-open,\n.w-menu .glyphicon-folder-close {\n  margin-right: 6px;\n}\n.w-menu .w-online {\n  position: absolute;\n  top: 0;\n  right: 0;\n}\n.w-menu .w-online,\n.w-menu a:hover,\n.w-left-menu a:hover,\n.w-left-menu a:hover .w-disabled {\n  color: var(--c-link);\n}\n.w-left-menu a:hover, .w-menu a:hover {\n  background: var(--b-btn-hover);\n}\n.w-menu .w-offline {\n  color: var(--c-disabled) !important;\n  cursor: default;\n}\n.w-menu .w-disabled,\n.w-frames-action .w-disabled {\n  color: var(--c-disabled) !important;\n  cursor: not-allowed !important;\n}\n.w-left-menu .w-disabled {\n  color: var(--c-disabled);\n}\n.w-menu .w-menu-enable .glyphicon-cog {\n  color: var(--c-risk) !important;\n}\n.w-menu .w-new-version-icon {\n  position: absolute;\n  top: 2px;\n  left: 17px;\n  width: 5px;\n  height: 5px;\n  border-radius: 5px;\n  background: var(--c-error);\n}\n.w-menu .w-menu-wrapper {\n  display: inline-block;\n  position: relative;\n  height: 30px;\n}\n\n.w-show-update-tips {\n  font-weight: bold;\n}\n\n.w-input-menu-item {\n  display: block;\n  position: absolute;\n  background: var(--b-default);\n  border: 1px solid var(--c-border);\n  border-radius: 2px;\n  z-index: 101;\n  top: 33px;\n  display: none;\n  white-space: nowrap;\n}\n.w-input-menu-item input {\n  width: 246px;\n  height: 32px;\n  border: 1px solid var(--c-border);\n  border-radius: 0!important;\n  border-radius: 2px;\n  padding: 0 5px;\n  vertical-align: middle;\n}\n.w-input-menu-item .btn {\n  height: 32px;\n  padding: 0 12px;\n  vertical-align: middle;\n  border-radius: 0;\n  border-top-right-radius: 2px;\n  border-bottom-right-radius: 2px;\n  margin-left: 1px;\n  border-color: var(--c-border);\n}\n\n.w-create-rules-input {\n  left: 310px;\n}\n.w-create-values-input {\n  left: 300px;\n}\n.w-edit-rules-input {\n  left: 382px;\n}\n.w-edit-values-input {\n  left: 372px;\n}\n\n.w-show-left-menu .w-create-rules-input {\n  left: 210px;\n}\n.w-show-left-menu .w-create-values-input {\n  left: 200px;\n}\n.w-show-left-menu .w-edit-rules-input {\n  left: 282px;\n}\n.w-show-left-menu .w-edit-values-input {\n  left: 272px;\n}\n\n.w-menu-wrapper .w-menu-item {\n  display: none !important;\n}\n.w-menu-wrapper-show .w-menu-item {\n  display: block !important;\n}\n\n.w-values-list .glyphicon-ok {\n  display: none !important;\n}\n.w-values-list a {\n  font-weight: normal !important;\n}\n.w-disabled .glyphicon-ok,\n.w-disabled .glyphicon-stop {\n  color: var(--c-disabled) !important;\n}\n\n.w-rules-settings-dialog .modal-dialog {\n  width: 400px;\n}\n.w-values-settings-dialog .modal-dialog {\n  width: 300px;\n}\n.w-show-update-tips-dialog .modal-dialog {\n  width: 336px;\n}\n.w-show-update-tips-dialog .modal-body p {\n  margin-bottom: 5px;\n}\n.w-https-dialog .modal-dialog {\n  width: 342px;\n}\n.w-https-help {\n  margin-left: 150px;\n}\n.w-https-dialog .modal-dialog div {\n  white-space: nowrap;\n}\n.w-https-dialog a {\n  display: inline-block;\n}\n.w-download-rootca,\n.w-https-help {\n  line-height: 30px;\n}\n.w-https-settings label {\n  font-weight: normal;\n}\n\n.w-https-settings p {\n  margin: 10px 0;\n}\n\n.w-is-link {\n  cursor: pointer;\n  text-decoration: underline;\n}\n\n.w-choose-filte-type .modal-dialog {\n  width: 435px;\n}\n.w-choose-filte-type-label {\n  white-space: nowrap;\n}\n.w-choose-filte-type-label .form-control {\n  margin-left: 10px;\n  width: 260px;\n  display: inline-block;\n  font-weight: normal;\n}\n.w-choose-filte-type-label select.form-control {\n  margin: 0 5px;\n  width: 80px;\n}\n\n.w-confirm-import-dialog .modal-dialog {\n  width: 320px;\n  font-weight: bold;\n}\n\n.w-switch-layout {\n  padding: 0 15px !important;\n  margin-right: 0 !important;\n}\n.w-left-menu {\n  background: var(--b-title);\n  border-left: 1px solid var(--c-border);\n  border-right: 1px solid var(--c-border);\n  display: none;\n}\n.w-show-left-menu .w-left-menu {\n  display: block;\n  border-left: none;\n}\n.w-show-left-menu .w-nav-menu {\n  display: none;\n}\n.w-left-menu a {\n  display: block;\n  position: relative;\n  padding: 10px 0 8px;\n  width: 47px;\n  overflow: hidden;\n  text-align: center;\n  text-decoration: none !important;\n  color: var(--c-default);\n}\n.w-left-menu a span {\n  margin-right: 5px;\n}\n.w-left-menu a .w-left-menu-name {\n  display: block;\n  font-style: normal;\n  font-size: 12px;\n  zoom: 1;\n  transform: scale(0.8, 0.8);\n  width: 65px;\n  margin-left: -10px;\n  white-space: nowrap;\n}\n.w-left-menu-tips {\n  padding: 6px 10px;\n  display: none;\n  left: 47px;\n  top: 3px;\n  position: absolute;\n  z-index: 1;\n  border-radius: 4px;\n}\n.w-left-menu a:hover .w-left-menu-tips {\n  display: block;\n  color: var(--c-default);\n  background-color: var(--b-title);\n  font-weight: bold;\n  border: 1px solid var(--c-border);\n}\n.w-left-menu a .w-left-menu-tips:hover {\n  display: none;\n}\n.w-menu .w-import-menu,\n.w-menu .w-export-menu,\n.w-menu .w-remove-menu-list {\n  display: none;\n}\n.w-show-left-menu .w-import-menu,\n.w-show-left-menu .w-export-menu,\n.w-show-left-menu .w-remove-menu-list {\n  display: inline-block;\n}\n\n.w-menu-changed {\n  position: absolute;\n  top: 2px;\n  left: 5px;\n  color: var(--c-error);\n}\n.w-replay-count-dialog .modal-content {\n  width: 236px!important;\n  white-space: nowrap;\n}\n.w-replay-count-dialog .modal-dialog {\n  width: 236px!important;\n}\n.w-replay-count-dialog .modal-content label {\n  width: auto;\n}\n.w-replay-count-dialog input {\n  display: inline-block;\n  width: 80px;\n  text-align: center;\n  margin: 0 10px;\n}\n.w-import-remote-dialog .modal-content {\n  width: 525px;\n}\n.w-import-remote-dialog input {\n  width: 500px;\n  height: 30px;\n  line-height: 30px;\n  padding: 5px;\n  border: 1px solid var(--c-border);\n}\n\n.w-enable-https-btn, .w-https-menu .glyphicon-lock {\n  color: var(--c-forbidden);\n}\n\n.w-enable-https-btn {\n  cursor: pointer;\n}\n\n.w-hover-left-menu {\n  position: absolute;\n  z-index: 1;\n  height: auto;\n  border-bottom: 1px solid var(--c-border);\n  min-height: 0;\n}\n\n.w-menu .w-tree-view-active {\n  color: var(--c-link);\n}\n\n.w-menu-selected {\n  background-color: var(--b-active)!important;\n  color: var(--c-default)!important;\n}\n\n.w-https-dialog select {\n  border: 1px solid var(--c-border);\n  border-radius: 3px;\n  height: 24px;\n  font-size: 12px;\n  padding-left: 5px;\n}\n\n.w-root-ca-type {\n  width: 92px;\n  float: right;\n  margin: 3px 15px 0 0;\n}\n\n.w-root-ca-url-wrap {\n  display: flex;\n  align-items: center;\n  margin: -3px 0 10px;\n}\n\n.w-root-ca-url-wrap .glyphicon {\n  margin-top: -2px;\n}\n\n.w-root-ca-url {\n  display: block;\n  width: 292px;\n}\n\n\n.w-fieldset pre {\n  max-height: 220px;\n  overflow: auto;\n  padding: 10px;\n}\n.w-fieldset {\n  border: 1px solid var(--c-border);\n}\n.w-fieldset legend {\n  font-size: 12px;\n  padding: 0 5px;\n  border: none;\n  width: auto;\n  margin: 0 10px;\n  font-weight: bold;\n}\n\n.w-fieldset legend .glyphicon {\n  margin-left: 5px;\n  cursor: pointer;\n}\n\n@media screen and (max-width: 1105px) {\n  .w-menu.w-top a {\n    padding: 0 7px;\n  }\n}\n\n.w-show-left-menu .w-help-name, .w-show-left-menu .w-https-name {\n  display: inline!important;\n}\n\n.w-va-mdl {\n  vertical-align: middle;\n}\n\n.w-mrl-5 {\n  margin-left: 5px;\n}\n\n.w-json-bar {\n  position: absolute;\n  top: 5px;\n  left: 10px;\n  font-size: 18px;\n}\n\n.w-json-bar .w-disabled {\n  color: var(--c-disabled)!important;\n  background: transparent!important;\n}\n\n.w-json-bar .glyphicon-menu-right {\n  margin-left: 15px;\n}\n\n.w-json-bar .glyphicon {\n  display: inline-block;\n  width: 30px;\n  height: 30px;\n  line-height: 30px;\n  text-align: center;\n}\n\n.w-json-bar .glyphicon:hover {\n  border-radius: 30px;\n  background: var(--b-title);\n}\n\n.w-import-remote-dialog .glyphicon-cloud {\n  vertical-align: middle;\n  padding-bottom: 1px;\n  margin-right: 5px;\n}\n\n.w-qrcode-wrap {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  color: var(--c-thin);\n  cursor: default;\n}\n\n.w-qrcode-wrap img {\n  width: 100%;\n  height: 100%;\n}\n\n\n.w-hover-body tr:hover th, .w-hover-body tr:hover td {\n  background: var(--b-hover);\n}\n\n.w-req-table .w-hover-body tr:hover th, .w-req-table .w-hover-body tr:hover td {\n  background: var(--b-list-hover) !important;\n}\n\n.w-hover-body tr.warning:hover th, .w-hover-body tr.warning:hover td {\n  background: var(--b-list-warn) !important;\n}\n.w-hover-body tr.info:hover th, .w-hover-body tr.info:hover td {\n  background: var(--b-list-info) !important;\n}\n.w-hover-body tr.success:hover th, .w-hover-body tr.success:hover td {\n  background: var(--b-success-hover) !important;\n}\n.w-hover-body tr.danger:hover th, .w-hover-body tr.danger:hover td {\n  background: var(--b-list-danger-hover) !important;\n}\n.w-hover-body tr.w-mark:hover th, .w-hover-body tr.w-mark:hover td {\n  background: var(--b-list-mark) !important;\n}\n\n.w-confim-reload-note {\n  color: var(--c-error);\n  margin: 0;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/json-viewer.css",
    "content": ".w-props-wrap:hover .w-textarea-bar {\n  display: inline-block;\n}\n.w-json-viewer-str,\n.w-json-viewer-tree {\n  padding: 5px;\n  overflow: auto;\n}\n.w-json-viewer-tree > ul {\n  background: none !important;\n  font-size: 12px;\n  margin: 0 !important;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/kv.css",
    "content": ".w-kv-dialog {\n  z-index: calc(var(--z-modal) + 1);\n}\n\n.w-kv-dialog .modal-dialog {\n  width: 720px;\n}\n.w-kv-name {\n  width: 330px;\n  white-space: normal;\n  word-wrap: break-word;\n  word-break: break-all;\n}\n.w-kv-dialog .w-kv-box {\n  width: 36px;\n}\n\n.w-kv-operation pre {\n  max-height: 70px;\n}\n.w-kv-operation a {\n  display: block;\n  width: 50px;\n  margin-top: 3px;\n}\n.w-kv-name strong {\n  color: var(--c-risk);\n  margin-left: 5px;\n  cursor: pointer;\n}\n\n.w-kv-dialog thead th, .w-kv-dialog thead td {\n  padding: 8px;\n}\n\n.w-kv-check-all {\n  float: left;\n  margin: 5px 0 0 9px;\n  display: flex;\n  align-items: center;\n}\n\n.w-kv-check-all input {\n  margin-right: 8px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/large-dialog.css",
    "content": ".w-large-dialog .modal-dialog {\n  width: calc(100% - 100px);\n  height: calc(100% - 60px);\n  min-width: 960px;\n  min-height: 520px;\n}\n\n.w-large-dialog {\n  overflow: auto!important;\n}\n\n.w-service-dialog {\n  z-index: var(--z-modal);\n}\n\n.w-editor-win {\n  z-index: var(--z-modal-max);\n}\n\n.w-large-dialog button.close, .w-custom-dialog button.close {\n  font-size: 20px;\n  line-height: 6px;\n  padding: 4px 3px 8px;\n  border-radius: 20px;\n  border: 1px solid var(--c-border);\n  background: var(--b-default);\n  position: absolute;\n  top: -9px;\n  right: -9px;\n  z-index: 1;\n}\n\n.w-large-dialog button.close:hover, .w-custom-dialog button.close:hover {\n  color: var(--c-heavy);\n}\n\n.w-large-dialog .w-open-win-btn {\n  position: absolute;\n  top: -23px;\n  right: 15px;\n  z-index: 1;\n  font-size: 12px;\n  padding: 3px 10px;\n  display: inline-block;\n  background: var(--b-title);\n  color: var(--c-heavy);\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n  text-decoration: none;\n}\n\n.w-large-dialog .w-open-win-btn:hover {\n  color: var(--c-default);\n}\n\n.w-large-dialog .modal-content, .w-custom-dialog .modal-content {\n  border-radius: 5px;\n  height: 100%;\n  padding: 0;\n  border: none;\n}\n\n.w-large-dialog iframe, .w-custom-dialog iframe {\n  border-radius: 5px;\n}\n\n.w-large-dialog .modal-body, .w-custom-dialog .modal-body {\n  display: block;\n  width: 100%;\n  height: 100%;\n  border: none;\n  outline: none;\n  padding: 0;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/list-dialog.css",
    "content": ".w-list-wrapper label {\n  line-height: 26px;\n  width: 200px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  margin-left: 10px;\n}\n.w-list-wrapper label input {\n  margin-right: 5px;\n}\n.w-list-dialog {\n  width: 900px;\n}\n.w-list-dialog .w-list-wrapper {\n  max-height: 420px;\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-list-dialog .w-list-tips {\n  max-height: 120px;\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-list-tips > span {\n  display: inline-block;\n  background: var(--b-heavy);\n  padding: 3px 6px;\n  border-radius: 3px;\n  margin: 0 0 10px 10px;\n}\n.w-list-tips-title {\n  color: var(--c-error);\n}\n.w-list-dialog .modal-footer {\n  position: relative;\n}\n.w-list-counter {\n  position: absolute;\n  left: 10px;\n  top: 15px;\n}\n\n.w-rule-list-name.w-list-data {\n  overflow-x: hidden;\n  overflow-y: auto;\n  max-height: 420px;\n}\n\n.w-rule-list-ctn {\n  max-height: 420px;\n  overflow: auto;\n  color: var(--c-default);\n}\n\n.w-rule-list-ctn .w-rule-list-item {\n  font-weight: normal;\n  height: 32px;\n  display: flex;\n  align-items: center;\n  white-space: nowrap;\n  margin-bottom: 0;\n  border-bottom: 1px solid var(--c-border);\n}\n\n.w-rule-list-ctn .w-rule-list-item span {\n  display: inline-block;\n\n}\n\n.w-rule-list-ctn .w-rule-list-item > span {\n  width: 32px;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: var(--b-title);\n  flex-shrink: 0;\n}\n\n.w-rule-list-ctn .w-rule-list-item > div {\n  display: flex;\n  align-items: center;\n  height: 100%;\n  padding: 0 10px;\n  flex: 1;\n  flex-shrink: 0;\n  overflow-x: auto;\n  overflow-y: hidden;\n  scrollbar-width: none;\n  background-color: var(--b-default);\n}\n\n.w-rule-list-ctn .w-rule-list-item > div::-webkit-scrollbar {\n  display: none; /* Chrome Safari */\n}\n\n.w-rule-list-ctn .w-rule-list-item > div span {\n  margin-right: 10px;\n  color: var(--c-rule-filter);\n}\n\n.w-rule-list-ctn .w-rule-list-item > div span:nth-child(1) {\n  color: var(--c-default);\n}\n\n.w-rule-list-ctn .w-rule-list-item > div span:nth-child(2) {\n  color: var(--c-rule-props);\n}\n\n.w-rule-list-ctn .w-rule-list-item > div span:last-child {\n  margin-right: 0;\n}\n\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/list.css",
    "content": ".w-divider-con .w-divider {\n  border: none;\n}\n.w-list-data {\n  background: var(--b-bar);\n  overflow-x: hidden;\n  overflow-y: auto;\n  outline: none;\n}\n.w-list-data a {\n  display: block;\n  padding-left: 10px;\n  line-height: 32px;\n  position: relative;\n  border-bottom: 1px solid var(--c-border);\n  color: var(--c-default);\n  text-decoration: none;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  background: var(--b-default);\n  outline: none;\n  cursor: default;\n}\n.w-list-data a .glyphicon-ok {\n  position: absolute;\n  top: 50%;\n  right: 10px;\n  margin-top: -8px;\n  color: var(--c-ok);\n  display: none;\n}\n\n.w-list-content {\n  border-bottom: 1px solid var(--c-border);\n  border-right: 1px solid var(--c-border);\n  cursor: text;\n}\n\n.w-list-data .w-changed:before {\n  content: '*';\n  color: var(--c-error);\n  margin-right: 2px;\n}\n.w-list-data .w-list-group.w-changed:before {\n  margin-right: 0;\n}\n.w-list-data a:hover {\n  color: var(--c-link);\n}\n\n.w-list-data a:hover:not(.w-list-group) {\n  background: var(--b-hover);\n}\n.w-list-data .w-active {\n  background: var(--c-link) !important;\n  color: var(--c-active) !important;\n}\n.w-list-data .w-selected {\n  font-weight: 500;\n}\n.w-list-data .w-selected .glyphicon-ok {\n  display: inline-block;\n}\n.w-list-data span.w-whistle-tpl {\n  display: block;\n  width: 8px;\n  height: 8px;\n  position: absolute;\n  right: 3px;\n  top: 2px;\n  background: center center no-repeat;\n  background-size: 100%;\n}\n\n.w-list-data .w-list-group {\n  background: var(--b-heavy);\n  color: var(--c-heavy);\n  cursor: default;\n  padding-left: 5px;\n}\n\n.w-list-data .w-list-group.w-list-group-active {\n  background: var(--b-heavy-active);\n}\n\n.w-list-data .w-list-group:hover, .w-list-data .w-list-group:active {\n  color: var(--c-default);\n}\n\n.w-list-group .glyphicon, .w-list-group-icon.glyphicon-triangle-right {\n  font-size: 10px;\n  opacity: .5;\n  transform: scale(0.8);\n  margin-right: 2px;\n}\n\n.w-list-group-icon.glyphicon-triangle-right {\n  margin-right: -2px;\n}\n\n.w-list-sub {\n  padding-left: 20px!important;\n}\n\n.w-list-group .w-group-child-num {\n  position: absolute;\n  right: 6px;\n  font-size: 10px;\n  font-weight: 500;\n  color: var(--c-thin);;\n}\n\n.w-list-group .w-exists-selected {\n  color: var(--c-ok);\n}\n\n.w-list-group.w-group-empty {\n  background-color: var(--b-hover);\n  color: var(--c-gray);\n}\n\n.w-has-selected-rules .CodeMirror {\n  border-top: 3px solid var(--b-ok);\n}\n\n.w-has-selected-rules.w-has-selected-disabled .CodeMirror {\n  border-top-color: var(--c-border);\n}\n\n\n.nav-tabs > .w-nav-normal-tab:first-child {\n  font-weight: bold;\n}\n\n.w-enabled-rules-btn {\n  height: 28px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  background: var(--b-title);\n  border-bottom: 1px solid var(--c-border);\n  cursor: pointer;\n  font-weight: bold;\n}\n\n.w-enabled-rules-btn .glyphicon-ok[data-name=\"enabledRules\"] {\n  color: var(--c-ok);\n  margin-right: 5px;\n  margin-top: -2px;\n}\n\n.w-enabled-rules-btn.w-disabled {\n  cursor: not-allowed;\n  color: var(--c-gray);\n}\n\n.w-enabled-rules-dialog .modal-dialog {\n  width: 80%;\n  min-width: 860px;\n}\n\n.w-enabled-rules-dialog .w-props pre {\n  font-size: 13px;\n  line-height: 1.5;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/menu-item.css",
    "content": ".w-menu-item {\n  position: absolute;\n  background: var(--b-default);\n  border: 1px solid var(--c-border);\n  z-index: var(--z-menu);\n  top: 31px;\n  border-radius: 2px;\n  outline: none;\n}\n.w-menu-item .glyphicon-cloud {\n  vertical-align: text-bottom;\n}\n.w-menu-item a {\n  display: block;\n  max-width: 160px;\n  text-overflow: ellipsis;\n  overflow: hidden;\n  padding: 0 6px;\n  font-weight: normal;\n  white-space: nowrap;\n  margin: 0 !important;\n}\n.w-menu-auto .w-menu-item a {\n  max-width: inherit;\n}\n.w-menu-item .w-menu-options {\n  border-bottom: 1px dashed var(--c-border);\n  max-height: 500px;\n  overflow-x: hidden;\n  overflow-y: auto;\n}\n.w-menu-item a .glyphicon,\n.w-menu-item input {\n  margin-right: 8px;\n  font-size: 12px;\n  vertical-align: text-bottom;\n}\n.w-menu-item a .glyphicon {\n  vertical-align: initial;\n}\n.w-menu-item a .glyphicon-ok,\n.w-menu-item a .glyphicon-plus {\n  color: var(--c-ok);\n}\n.w-create-menu-item a .glyphicon-plus {\n  color: var(--c-default);\n}\n.w-network-menu-item .w-menu-options a {\n  max-width: 320px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/message.css",
    "content": ".w-message {\n  position: fixed;\n  top: 30px;\n  left: 50%;\n  z-index: calc(var(--z-max) + 2);\n  padding: 8px 20px !important;\n  max-width: 460px;\n  white-space: pre-wrap;\n  cursor: default;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/modal.css",
    "content": ".w-dialog-for-plguin .modal-header {\n  display: none;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/network-settings.css",
    "content": ".w-ns-dialog fieldset {\n  border: 1px solid var(--c-border);\n  padding-bottom: 10px;\n}\n.w-ns-dialog legend {\n  border: none;\n  margin: 0 10px;\n  padding: 0 10px;\n  width: auto;\n}\n.w-ns-dialog legend label {\n  font-size: 13px;\n  width: auto;\n  padding: 0;\n}\n.w-ns-dialog label {\n  font-size: 13px;\n  font-weight: normal;\n  line-height: 30px;\n  height: 30px;\n  display: inline-block;\n  width: 96px;\n  margin-right: 10px;\n  font-weight: bold;\n  text-align: right;\n}\n.w-ns-columns {\n  margin-top: 10px;\n}\n.w-ns-filter {\n  border: none !important;\n}\n.w-ns-filter legend {\n  margin: 0;\n  padding: 0;\n}\n.w-ns-filter legend label {\n  margin-right: 5px;\n  vertical-align: middle;\n}\n.w-ns-filter textarea[disabled] {\n  background: var(--b-disabled);\n}\n.w-ns-columns label {\n  white-space: nowrap;\n  padding-left: 10px;\n  margin-left: 10px;\n  text-align: left;\n  width: 120px;\n}\n.w-ns-columns label .glyphicon-edit {\n  margin-left: 5px;\n  cursor: pointer;\n}\n.w-ns-columns label .glyphicon-edit:hover {\n  color: var(--c-link);\n}\n.w-ns-columns .btn {\n  margin-right: 0;\n  text-align: center;\n  color: var(--c-thin);\n  width: 72px;\n}\n.w-ns-columns legend label {\n  margin-left: 0;\n}\n.w-ns-dialog .modal-dialog {\n  width: 600px;\n}\n.w-ns-dialog input[type='checkbox'] {\n  margin-right: 5px;\n}\n.w-ns-dialog textarea {\n  width: 578px;\n  height: 90px;\n  border: 1px solid var(--c-border);\n  overflow: auto;\n  margin: 0;\n  padding: 5px 10px;\n  border-radius: 5px;\n}\n.w-ns-dialog div input[type='text'] {\n  width: 436px;\n}\n.w-ns-own {\n  width: auto !important;\n  margin: 10px 0 0;\n  white-space: nowrap;\n  display: flex!important;\n  text-align: left!important;\n  align-items: center;\n}\n.w-ns-own select {\n  margin-left: 20px;\n  width: 160px;\n  display: inline-block;\n}\n.w-ns-own .glyphicon-tree-conifer {\n  margin: -3px 2px 0 0;\n}\n.w-network-custom-col {\n  display: inline-block;\n  max-width: 90px;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.w-ns-columns label span {\n  vertical-align: middle;\n  overflow: hidden;\n  font-weight: normal;\n}\n.w-ns-edit label {\n  white-space: nowrap;\n  margin-bottom: 10px;\n}\n.w-ns-edit label span {\n  display: inline-block;\n  width: 100px;\n}\n.w-ns-edit label .form-control {\n  display: inline-block;\n  margin: 0 10px;\n  font-weight: normal;\n}\n.w-ns-edit label select {\n  width: 150px;\n}\n.w-ns-edit label input {\n  width: 345px;\n}\n.w-ns-edit .modal-dialog {\n  width: 500px;\n}\n.w-ns-columns legend .btn-primary {\n  width: auto;\n  margin-right: 10px;\n  padding: 0 8px;\n  color: var(--c-active);\n  font-weight: normal;\n}\n.w-certs-title {\n  margin-bottom: 10px;\n  font-size: 16px;\n  display: inline-flex;\n  align-items: center;\n}\n\n.w-query-select {\n  font-size: 12px;\n  width: 64px;\n  text-align: center;\n  height: 20px;\n  color: var(--c-thin);\n  margin-left: 5px;\n  border: 1px solid var(--c-border);\n  border-radius: 3px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/online.css",
    "content": ".w-online-dialog .modal-dialog {\n  width: 320px;\n}\n.w-online-info,\n.w-online-dns {\n  display: none;\n}\n.w-online-ctn h5 {\n  padding: 5px 0;\n  margin: 0;\n  text-overflow: ellipsis;\n  max-width: 100%;\n  white-space: nowrap;\n  overflow: hidden;\n}\n.w-online-ctn p {\n  margin: -23px 0 5px 36px;\n}\n.w-confirm-reload-dialog {\n  z-index: var(--z-max);\n}\n.w-confirm-reload-global {\n  z-index: calc(var(--z-max) + 1);\n}\n.w-confirm-reload-dialog .modal-dialog {\n  width: 310px;\n}\n.w-confirm-reload-dialog .w-confirm-reload {\n  font-weight: bold;\n  line-height: 1.8;\n  padding: 10px 20px;\n}\n\n.w-online-menu > span {\n  margin-left: 5px;\n  font-size: 13px;\n}\n\n.w-dns-servers-dialog {\n  z-index: var(--z-modal);\n}\n\n.w-dns-servers-dialog pre {\n  font-size: 14px;\n  font-weight: bold;\n}\n\n.w-dns-servers-dialog .modal-dialog {\n  width: 420px;\n}\n\n.w-tips-dialog .modal-dialog {\n  width: 560px !important;\n}\n\n#appearanceMode {\n  display: inline-block;\n  width: 150px;\n  margin-left: 10px;\n  height: 32px;\n  font-size: 13px;\n  font-weight: normal;\n}\n\n.w-online-option {\n  font-weight: normal!important;\n  display: block;\n}\n\n.w-online-option input, .w-online-option span {\n  vertical-align: middle;\n}\n\n.w-theme-option, .w-dns-order-option {\n  display: flex;\n  align-items: center;\n}\n\n.w-theme-option strong, .w-dns-order-option strong {\n  width: 85px;\n}\n\n.w-theme-option select, .w-dns-order-option select {\n  margin-left: 5px;\n  width: 160px;\n}\n\n.w-login-btn span {\n  margin-right: 6px;\n}\n\n.w-whistle-id-option {\n  font-size: 16px;\n}\n\n\n.w-shortcuts {\n   z-index: var(--z-modal);\n}\n\n.w-shortcuts .modal-dialog {\n  width: 660px;\n}\n\n.w-shortcuts h5 {\n  font-size: 16px;\n}\n\n.w-shortcuts .modal-body > div {\n  margin-top: 25px;\n  margin-left: 10px;\n}\n\n.w-shortcuts .modal-body > div:first-child {\n  margin-top: 10px;\n}\n\n\n.w-shortcuts label {\n  display: flex;\n  margin: 15px 0 15px 20px;\n  align-items: center;\n  font-weight: normal;\n}\n\n.w-shortcuts label:hover {\n  color: var(--c-default);\n}\n\n.w-shortcuts label input,\n.w-shortcuts label strong {\n  margin-right: 5px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/override.css",
    "content": ".table-hover>tbody>tr:hover {\n  background-color: var(--b-hover);\n}\n\n.nav-tabs {\n  border-bottom: 1px solid var(--c-border);\n}\n\n.nav-tabs>li>a:hover {\n  background-color: var(--b-heavy);\n  border-color: var(--c-border);\n}\n\n.nav-tabs>li.active>a, .nav-tabs>li.active>a:focus, .nav-tabs>li.active>a:hover {\n  color: var(--c-thin);\n  background: var(--b-default);\n  border: 1px solid var(--c-border);\n  border-bottom-color: transparent;\n}\n\n.modal-header {\n  border-bottom: 1px solid var(--c-border);\n}\n\n.modal-content {\n  position: relative;\n  background-color: var(--b-default);\n  border: 1px solid var(--c-border);\n}\n\n.modal-footer {\n  border-top: 1px solid var(--c-border);\n}\n\n.form-control {\n  background-color: var(--b-default);\n  border: 1px solid var(--c-border);\n  color: var(--c-thin)\n}\n\n.alert-danger {\n  color: var(--c-danger);\n  background-color: var(--b-error);\n  border: none;\n}\n\n.alert-warning {\n  color: var(--c-tree-warning);\n  background-color: var(--b-warn);\n  border: none;\n}\n\n.alert-info {\n  color: var(--c-tree-info);\n  background-color: var(--b-alert-info);\n  border: none;\n}\n\n.alert-success {\n  color: var(--c-success);\n  background-color: var(--b-success);\n  border: none;\n}\n\n.table>tbody>tr>td, .table>tbody>tr>th, .table>thead>tr>td, .table>thead>tr>th {\n    border-top: 1px solid var(--c-border);\n}\n\n.table>thead>tr>th {\n  border-bottom: 1px solid var(--c-border);\n}\n\nlegend, pre {\n  color: var(--c-heavy)\n}\n\na {\n  color: var(--c-link);\n}\n\na:focus, a:hover {\n  color: var(--c-link-hover);\n}\n\n.close {\n  color: var(--c-gray);\n  text-shadow: none;\n  filter: alpha(opacity=100);\n  opacity: 1;\n}\n\n.close:focus, .close:hover {\n  color: var(--c-default);\n  filter: alpha(opacity=100);\n  opacity: 1;\n}\n\n.btn-default {\n  color: var(--c-heavy);\n  background-color: var(--b-default);\n  border-color: var(--c-border);\n}\n\n.dropdown-menu {\n  background-color: var(--c-default);\n  border: 1px solid var(--c-border);\n  box-shadow: 0 6px 9pt var(--s-rgba15);\n}\n\n.btn-default.active, .btn-default:hover, .btn-default:focus, .btn-default:active,\n.w-tabs-sm .btn-default.active:focus, .w-tabs-sm .btn-default.active:hover,\n.w-tabs-sm .btn-default:active:focus, .w-tabs-sm .btn-default:active:hover {\n  color: var(--c-heavy);\n  background-color: var(--b-btn-hover);\n  border-color: var(--c-border);\n}\n\n.btn-default.active:focus, .btn-default.active:hover,\n.btn-default:active:focus, .btn-default:active:hover,\n.open>.dropdown-toggle.btn-default,\n.open>.dropdown-toggle.btn-default:focus,\n.open>.dropdown-toggle.btn-default:hover {\n  color: var(--c-heavy);\n  background-color: var(--b-btn-active);\n  border-color: var(--c-border);\n}\n\n.btn-default[disabled], .btn-default[disabled].active, .btn-default[disabled].focus,\n.btn-default[disabled]:active, .btn-default[disabled]:focus,\n.btn-default[disabled]:hover {\n  background-color: var(--b-default);\n  border-color: var(--c-border);\n}\n\n.btn-primary {\n  color: var(--c-btn);\n  background-color: var(--b-primary);\n  border-color: var(--c-primary);\n}\n\n.btn-primary.active, .btn-primary:hover, .btn-primary:focus, .btn-primary:active {\n  color: var(--c-btn);\n  background-color: var(--b-primary-hover);\n  border-color: var(--c-primary);\n}\n\n.btn-primary.active:focus, .btn-primary.active:hover,\n.btn-primary:active:focus, .btn-primary:active:hover {\n  color: var(--c-btn);\n  background-color: var(--b-primary-active);\n  border-color: var(--c-primary);\n}\n\n.btn-danger {\n  color: var(--c-btn);\n  background-color: var(--b-danger);\n  border-color: var(--c-danger);\n}\n\n.btn-danger.active, .btn-danger:hover, .btn-danger:focus, .btn-danger:active {\n  color: var(--c-btn);\n  background-color: var(--b-danger-hover);\n  border-color: var(--c-danger-hover);\n}\n\n.btn-danger.active:focus, .btn-danger.active:hover,\n.btn-danger:active:focus, .btn-danger:active:hover {\n  color: var(--c-btn);\n  background-color: var(--b-danger-active);\n  border-color: var(--c-danger-active);\n}\n\n.btn-warning {\n  color: var(--c-btn);\n  background-color: var(--b-warning);\n  border-color: var(--c-warning);\n}\n\n.btn-warning.active, .btn-warning:hover, .btn-warning:focus, .btn-warning:active {\n  color: var(--c-btn);\n  background-color: var(--b-warning-hover);\n  border-color: var(--c-warning-hover);\n}\n\n.btn-warning.active:focus, .btn-warning.active:hover,\n.btn-warning:active:focus, .btn-warning:active:hover {\n  color: var(--c-btn);\n  background-color: var(--b-warning-active);\n  border-color: var(--c-warning-active);\n}\n\n.btn-info {\n  color: var(--c-btn);\n  background-color: var(--b-info);\n  border-color: var(--c-info);\n}\n\n.btn-info.active, .btn-info:hover, .btn-info:focus, .btn-info:active {\n  color: var(--c-btn);\n  background-color: var(--b-info-hover);\n  border-color: var(--c-info-hover);\n}\n\n.btn-info.active:focus, .btn-info.active:hover,\n.btn-info:active:focus, .btn-info:active:hover {\n  color: var(--c-btn);\n  background-color: var(--b-info-active);\n  border-color: var(--c-info-active);\n}\n\n.table>tbody>tr.success>td, .table>tbody>tr.success>th {\n  background-color: var(--b-success);\n}\n\n.table>tbody>tr.active>td, .table>tbody>tr.active>th {\n  background-color: var(--b-hover);\n}\n\n.table>tbody>tr.danger>td, .table>tbody>tr.danger>th {\n  background-color: var(--b-error);\n}\n.table>tbody>tr.warning>td, .table>tbody>tr.warning>th {\n  background-color: var(--b-warn);\n}\n.table>tbody>tr.info>td, .table>tbody>tr.info>th  {\n  background: var(--b-alert-info);\n}\n\n.dropdown-menu {\n  background-color: var(--b-bar);\n}\n\n.dropdown-menu .divider {\n  background: var(--c-border);\n}\n\n.dropdown-menu>li>a {\n    color: var(--c-heavy);\n}\n\n.dropdown-menu>li>a:focus, .dropdown-menu>li>a:hover {\n  color: var(--c-heavy);\n  text-decoration: none;\n  background-color: var(--b-hover);\n}\n\n.modal-content {\n  border: 1px solid var(--c-border);\n  box-shadow: 0 5px 15px var(--s-rgba50);\n}\n\n@media (min-width: 768px) {\n  .modal-content {\n    -webkit-box-shadow: 0 5px 15px var(--s-rgba50);\n    box-shadow: 0 5px 15px var(--s-rgba50);\n  }\n}\n\n.modal-backdrop.in {\n  filter: alpha(opacity=100);\n  opacity: 1;\n  background: var(--b-modal);\n}\n\nselect.form-control[disabled] {\n  background: var(--b-disabled);\n}\n\n.form-control {\n  background: field;\n}\n\n.w-json-tree label {\n  font-weight: normal;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/overview.css",
    "content": ".w-detail-overview > .w-props-wrap:last-child {\n  border-top: 1px solid var(--c-border);\n}\n.w-detail-overview-title {\n  margin: 0;\n  padding: 6px 5px 0;\n}\n.w-detail-overview-title:after {\n  content: '';\n  display: block;\n  clear: both;\n}\n.w-detail-overview-title label {\n  float: right;\n  font-weight: normal;\n  display: flex;\n  align-items: center;\n}\n.w-detail-overview-title label input {\n  margin-right: 5px;\n  vertical-align: text-bottom;\n}\n\n.w-overview-timeline {\n  position: relative;\n}\n\n.w-overview-timeline > pre {\n  z-index: 1;\n  position: relative;\n}\n\n.w-overview-timeline::before {\n  position: absolute;\n  content: '';\n  display: block;\n  background: var(--overview-bg, none);\n  width: var(--overview-width, 0);\n  left: var(--overview-left, 0);\n  top: 0;\n  height: 100%;\n}\n\n.w-props-wrap td pre[data-rule-source]:hover {\n  cursor: pointer;\n  color: var(--c-link);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/plugins-mgr.css",
    "content": ".w-plugins-mgr-dialog {\n  z-index: var(--z-modal-max);\n}\n\n.w-plugins-mgr-dialog .modal-content {\n  width: 380px;\n}\n\n.w-plugins-mgr-dialog .plugin-mgr-btn {\n  display: block;\n  line-height: 1.5;\n  width: 320px;\n  margin: 20px auto;\n  padding: 15px 10px;\n  text-align: center;\n  cursor: pointer;\n  font-size: 15px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  font-weight: 500;\n}\n\n.w-plugins-mgr-dialog .plugin-mgr-btn span {\n  opacity: 0.66;\n  font-weight: 400;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/plugins.css",
    "content": ".w-nav-tabs {\n  font-size: 13px;\n}\n.w-nav-tabs > .nav-tabs {\n  padding-left: 2px;\n  padding-top: 5px;\n  background: var(--b-bar);\n}\n.w-nav-tabs > .nav > li > a {\n  padding: 5px 20px 5px 10px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  max-width: 160px;\n  min-width: 60px;\n  min-height: 30px;\n  position: relative;\n  display: flex;\n  align-items: center;\n}\n.w-nav-tabs > .nav > li > a > .w-close-icon {\n  display: block;\n  position: absolute;\n  top: 5px;\n  right: 5px;\n  color: var(--c-gray);\n  cursor: pointer;\n}\n.w-nav-tabs > .nav > li > a > .w-close-icon:hover {\n  color: var(--c-default);\n}\n.w-plugin-tab-disabled {\n  color: var(--c-disabled) !important;\n}\n.w-plugin-tab-disabled .glyphicon-ban-circle {\n  vertical-align: text-top;\n  margin-right: 3px;\n  color: var(--c-disabled) !important;\n}\n\n.w-custom-tab-btn {\n  display: inline-flex;\n  align-items: center;\n}\n\n.w-plugins-tab img, .w-custom-tab-btn img {\n  width: 13px;\n  max-height: 13px;\n  margin: 0 3px 0 0;\n}\n\n.w-nav-tabs iframe {\n  border: none;\n  display: block;\n  background-color: var(--b-default);\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n}\n.w-nav-tabs .w-nav-tab-panel,\n.w-nav-tabs .w-nav-tab-panel > div {\n  overflow: hidden;\n}\n.w-nav-tabs > .nav > .w-nav-normal-tab > a {\n  padding-right: 10px !important;\n}\n\n.w-nav-tabs > .nav > .w-nav-normal-tab > a span {\n  margin-right: 5px;\n  top: 0;\n}\n\n.w-nav-tabs > .nav > .w-nav-normal-tab > a .glyphicon-cloud {\n  top: 2px;\n}\n\n.w-plugins th,\n.w-plugins td {\n  font-size: 13px;\n  font-weight: normal;\n}\n.w-plugins-headers {\n  border-bottom: 1px solid var(--c-border);\n  overflow: hidden;\n}\n.w-plugins-headers .table th {\n  border: none !important;\n  padding: 6px;\n  color: var(--c-default);\n  background: var(--b-bar);\n  font-weight: bold;\n}\n.w-plugins-list {\n  overflow-y: auto;\n  overflow-x: hidden;\n  outline: none;\n}\n.w-plugins-list th,\n.w-plugins-list td {\n  border-top: none !important;\n  border-bottom: 1px solid var(--c-border);\n  padding: 8px 6px !important;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n.w-plugins-disable,\n.w-plugins-disable a,\n.w-plugins-disable td {\n  color: var(--c-disabled);\n}\n\n.w-plugins-order {\n  width: 50px;\n}\n.w-plugins-active {\n  width: 60px;\n}\n.w-plugins-list td.w-plugins-active input {\n  margin-left: 12px;\n  vertical-align: text-bottom;\n}\n.w-plugins-name {\n  width: 160px;\n}\n.w-plugins-version {\n  width: 150px;\n}\n.w-has-new-version .w-plugins-version .w-new-version,\n.w-has-new-version .w-plugin-update-btn,\n.w-plugins-menu.w-plugin-update-btn,\n.w-about-dialog .w-new-version {\n  color: var(--c-risk) !important;\n}\n\n.w-about-dialog .w-new-version {\n  margin-left: 5px;\n}\n\n.w-has-new-version .w-plugins-version .w-new-version:hover,\n.w-has-new-version .w-plugin-update-btn:hover,\n.w-plugins-menu.w-plugin-update-btn:hover,\n.w-about-dialog .w-new-version:hover {\n  color: var(--c-risk) !important;\n}\n.w-plugins-date {\n  width: 190px;\n}\n.w-plugins-operation {\n  width: 360px;\n}\n.w-plugins-operation a,\n.w-plugins-operation span.disabled {\n  margin-right: 20px;\n}\n.w-plugins-operation a:last-child,\n.w-plugins-operation span.disabled:last-child {\n  margin-right: 0;\n}\n.w-plugins-operation span.disabled {\n  color: var(--c-disabled);\n}\n\n.w-plugin-rules-dialog .modal-dialog {\n  width: 680px;\n}\n.w-plugin-rules-dialog .modal-dialog pre {\n  max-height: 230px;\n  overflow: auto;\n  padding: 10px;\n}\n.w-plugin-rules-dialog fieldset {\n  border: 1px solid var(--c-border);\n}\n.w-plugin-rules-dialog fieldset legend {\n  font-size: 13px;\n  padding: 0 10px;\n  border: none;\n  width: auto;\n  margin: 0 10px;\n  font-weight: bold;\n}\n.w-plugin-rules-dialog fieldset:nth-child(2) {\n  margin-top: 15px;\n}\n.w-plugin-rules-dialog .modal-header h4 {\n  margin: 0;\n}\n.w-plugin-rules-dialog .modal-header .close {\n  position: absolute;\n  right: 10px;\n  top: 16px;\n}\n.w-plugin-update-dialog {\n  z-index: var(--z-modal-max);\n}\n.w-plugin-update-dialog .modal-dialog {\n  width: 600px;\n}\n.w-plugin-update-cmd {\n  background: var(--b-editor);\n  color: var(--c-editor);\n  padding: 10px;\n  margin-top: 10px;\n  font-size: 14px;\n  display: block;\n  width: 578px;\n  height: 220px;\n  border-radius: 3px;\n}\n.w-plugin-install {\n  padding: 5px;\n}\n.w-plugin-btn {\n  color: var(--c-link) !important;\n}\n.w-plugin-btn:hover {\n  color: var(--c-link-hover) !important;\n}\n.w-plugin-update-dialog .modal-footer {\n  white-space: nowrap;\n}\n.w-registry-list {\n  margin-right: 15px;\n}\n.w-registry-list select {\n  border-radius: 0;\n  display: inline-block;\n  font-size: 12px;\n  line-height: 28px;\n  height: 30px;\n  padding: 0 5px;\n  width: 350px;\n}\n\n.w-plugin-copy-cmd {\n  display: inline-block;\n  position: absolute;\n  z-index: 1;\n  text-decoration: none!important;\n  color: var(--c-heavy);\n  padding: 0 6px;\n  background: var(--b-heavy);\n  font-size: 12px;\n  top: 28px;\n  right: 10px;\n  border-top-left-radius: 3px;\n  border-top-right-radius: 3px;\n}\n\n.w-copied-text {\n  color: var(--c-disabled) !important;\n  cursor: not-allowed;\n}\n\n.w-plugin-copy-cmd:hover {\n  color: var(--c-default);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/properties.css",
    "content": ".w-props-wrap {\n  position: relative;\n}\n.w-props-source {\n  padding: 5px 10px;\n}\n.w-props-view-source .w-props-parsed {\n  display: none;\n}\n.w-props-view-parsed .w-props-source {\n  display: none;\n}\n.w-props-view-source .w-textarea-bar,\n.w-props-view-parsed .w-textarea-bar {\n  right: 5px !important;\n}\n.w-props th,\n.w-props td {\n  border: none !important;\n  font-size: 12px;\n  padding-top: 3px !important;\n  padding-bottom: 3px !important;\n}\n.w-props tr {\n  border-bottom: 1px solid var(--c-border);\n}\n.w-props th {\n  width: 124px;\n  text-align: right;\n  font-weight: normal;\n  background: var(--b-prop);\n  padding-right: 3px;\n  word-break: break-word;\n}\n.w-enabled-rules {\n  overflow-y: auto;\n}\n.w-enabled-rules .w-props th {\n  width: 36px;\n  padding-left: 5px;\n  vertical-align: middle;\n}\n.w-props td {\n  padding-left: 5px;\n  vertical-align: top;\n  overflow: hidden;\n  word-break: break-word;\n}\n\n.w-props th .glyphicon-question-sign {\n  display: none;\n}\n\n.w-props th:hover .w-help-icon,\n.w-props th .w-enable-https-help {\n  display: inline-block;\n  vertical-align: top;\n}\n\n.w-props-raw-data {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.w-user-select-none {\n  user-select: none;\n}\n\n.w-user-select-none > pre, .w-user-select-none > span  {\n  user-select: text;\n  display: block;\n}\n\n.w-props .w-props-json {\n  padding-top: 0!important;\n  padding-bottom: 5px!important;\n}\n\n.w-props .w-props-json>ul {\n  background: var(--b-default)!important;\n  margin: 0!important;\n}\n\n.w-props-icon {\n  cursor: pointer!important;\n  text-decoration: none!important;\n  color: var(--c-default);;\n}\n\n.w-src-info {\n  margin-left: 30px;\n}\n\nspan.w-src-info {\n  color: var(--c-gray);\n}\n\n.w-com-dialog .form-control {\n  padding: 6px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/props-editor.css",
    "content": ".w-props-editor {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  overflow-y: auto;\n  overflow-x: hidden;\n}\n\n.w-props-editor th,\n.w-props-editor td {\n  border: none;\n  border-bottom: 1px solid var(--c-border);\n  border-top: none !important;\n  padding: 0 !important;\n  font-weight: normal;\n  font-size: 12px;\n  word-break: break-word;\n}\n.w-props-editor th {\n  width: 125px;\n  line-height: 22px !important;\n  background: var(--b-prop);\n  text-align: right;\n  padding: 0 8px !important;\n}\n.w-props-editor .w-props-ops {\n  width: 75px;\n  padding: 4px 0 0 8px !important;\n  padding-left: 0 !important;\n  background: var(--b-bar);\n}\n.w-props-ops span {\n  margin-left: 15px !important;\n  text-decoration: none !important;\n  cursor: pointer;\n}\n.w-edit-btn {\n  color: var(--c-heavy);\n}\n.w-edit-btn:hover {\n  color: var(--c-link);\n}\n.w-del-btn {\n  color: var(--c-risk);\n}\n.w-del-btn:hover {\n  color: var(--c-error);\n}\n\n.w-props-editor pre {\n  overflow: auto;\n  line-height: 22px !important;\n  max-height: 350px;\n  padding: 0 8px !important;\n}\n\n.w-add-field {\n  font-size: 14px !important;\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  margin: -17px 0 0 -38px;\n}\n\n.w-add-field.w-add-header {\n  margin-left: -48px;\n}\n.w-com-dialog .modal-content {\n  width: 600px;\n}\n.w-com-dialog label {\n  display: block;\n  padding: 10px 0;\n}\n.w-com-dialog textarea {\n  height: 120px;\n  white-space: normal;\n}\n.w-props-editor-upload {\n  white-space: nowrap;\n}\n.w-props-editor-upload .btn-primary {\n  line-height: 108px;\n  vertical-align: middle;\n}\n.w-props-editor-upload textarea {\n  display: inline-block;\n  width: 478px;\n  margin-right: 8px;\n  vertical-align: middle;\n}\n.w-props-editor-form button {\n  display: none;\n}\n.w-com-dialog label input,\n.w-com-dialog label textarea {\n  font-weight: normal;\n}\n\n.w-props-editor-file {\n  position: relative;\n  display: inline-block;\n  width: 577px;\n  height: 120px;\n  line-height: 120px;\n  vertical-align: middle;\n  cursor: pointer;\n  border: 1px solid var(--c-border);\n  border-radius: 4px;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  text-align: center;\n}\n\n.w-props-editor-file .close {\n  width: 20px;\n  height: 20px;\n  position: absolute;\n  line-height: 20px;\n  top: 0;\n  right: 3px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/record-btn.css",
    "content": ".w-switch-btn .glyphicon-stop {\n  color: var(--c-risk);\n}\n\n.w-switch-btn .w-disabled .glyphicon-stop,\n.w-switch-btn .w-pause .glyphicon-stop,\n.w-switch-btn .w-pause.glyphicon-stop,\n.w-switch-btn .w-menu-item .glyphicon-stop,\n.w-switch-btn .w-disabled .glyphicon-minus-sign,\n.w-switch-btn .w-pause .glyphicon-minus-sign,\n.w-switch-btn .w-menu-item .glyphicon-minus-sign {\n  color: var(--c-disabled) !important;\n}\n\n\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/req-data.css",
    "content": ".w-req-data-con {\n  overflow-x: auto;\n}\n.w-req-data-ctn {\n  overflow-x: auto;\n  overflow-y: hidden;\n  position: relative;\n}\n.w-req-data-ctn .order {\n  width: 50px;\n  position: relative;\n}\n.w-req-data-ctn .order .glyphicon {\n  font-size: 6px;\n  position: absolute;\n  top: 2px;\n  left: 1px;\n  font-weight: normal !important;\n}\n.w-req-data-ctn .date {\n  width: 150px;\n}\n.w-req-data-ctn .result {\n  width: 65px;\n}\n.w-req-data-ctn .protocol {\n  width: 92px;\n}\n.w-req-data-ctn .method {\n  width: 75px;\n}\n.w-req-data-ctn .time,\n.w-req-data-ctn .dns {\n  width: 70px;\n}\n.w-req-data-ctn .serverPort,\n.w-req-data-ctn .clientPort {\n  width: 90px;\n}\n.w-req-data-ctn .type {\n  width: 125px;\n}\n.w-req-data-ctn .clientIp,\n.w-req-data-ctn .hostIp {\n  width: 110px;\n}\n.w-req-data-ctn .hostname {\n  width: 150px;\n}\n.w-req-data-ctn .request,\n.w-req-data-ctn .response,\n.w-req-data-ctn .download,\n.w-req-data-ctn .contentEncoding,\n.w-req-data-ctn .body {\n  width: 90px;\n}\n\n.w-req-data-headers {\n  height: 29px;\n  border-bottom: 1px solid var(--c-border);\n  overflow: hidden;\n}\n.w-req-data-headers .table {\n  height: 30px;\n}\n.w-req-data-headers .table th {\n  border: none !important;\n  padding: 2px 0 6px 6px;\n  -webkit-user-select: none;\n  user-select: none;\n  font-weight: normal;\n  color: var(--c-default);\n  position: relative;\n  cursor: pointer;\n  background-color: var(--b-bar);\n}\n.w-req-data-headers .table th:last-child {\n  box-sizing: content-box;\n}\n.w-req-data-headers .table th:hover {\n  background-color: var(--b-btn-hover) !important;\n}\n.w-req-data-list {\n  overflow: hidden;\n  outline: none;\n}\n.w-req-data-list th,\n.w-req-data-list td {\n  border-top: none !important;\n  border-bottom: 1px solid var(--c-border);\n  font-weight: normal;\n  font-size: 12px;\n  padding: 3px 0 3px 6px !important;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  line-height: 21px !important;\n}\n.w-req-data-con .w-filter-con,\n.w-req-data-ctn {\n  min-width: 820px;\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  user-select: none;\n}\n.w-menu,\n.w-left-menu {\n  -moz-user-select: none;\n  -webkit-user-select: none;\n  user-select: none;\n}\n.w-req-data-list .w-tunnel th,\n.w-req-data-list .w-tunnel td {\n  color: var(--c-gray);\n}\n.w-req-data-list .w-has-rules th,\n.w-req-data-list .w-has-rules td,\n.w-req-data-list .tree-leaf.w-has-rules {\n  font-weight: 500;\n  color: var(--c-has);\n}\n\n.w-req-data-list .w-has-local th,\n.w-req-data-list .w-has-local td,\n.w-req-data-list .tree-leaf.w-has-local {\n  font-weight: 500;\n  color: var(--c-default);\n}\n\n.w-req-data-list .w-error-status th,\n.w-req-data-list .w-error-status td {\n  color: var(--c-danger);\n}\n.w-req-data-list .w-forbidden th,\n.w-req-data-list .w-forbidden td {\n  color: var(--c-forbidden);\n}\n.w-req-data-list tr {\n  cursor: default;\n  background: var(--b-default);\n}\n.w-req-data-list .w-hover-body tr.w-selected th,\n.w-req-data-list .w-hover-body tr.w-selected td {\n  background: var(--c-link) !important;\n  color: var(--c-active) !important;\n}\n\n.w-req-data-list .tree-leaf .glyphicon {\n  color: var(--c-thin) !important;\n  font-size: 12px;\n  display: inline-block;\n  margin-right: 5px;\n  overflow: hidden;\n  font-weight: normal !important;\n}\n.w-req-data-list .tree-leaf .w-type-icon {\n  transform: scale(0.8);\n  display: inline-block;\n  padding-right: 3px;\n  font-weight: normal !important;\n  color: var(--c-thin) !important;\n  margin-left: -4px;\n}\n.w-req-data-list .tree-leaf.success span {\n  color: var(--c-tree-success) !important;\n}\n.w-req-data-list .tree-leaf.info span {\n  color: var(--c-tree-info) !important;\n}\n.w-req-data-list .tree-leaf.warning span {\n  color: var(--c-tree-warning) !important;\n}\n.w-req-data-list .tree-leaf.active span {\n  color: var(--c-link) !important;\n}\n.w-req-data-list .tree-leaf.w-error-status,\n.w-req-data-list .tree-leaf.w-error-status span {\n  color: var(--c-danger) !important;\n}\n.w-req-data-list .tree-leaf.w-forbidden span {\n  color: var(--c-tree-forbidden) !important;\n}\n.w-req-data-list .tree-leaf.w-mark {\n  color: var(--c-tree-mark) !important;\n}\n.w-req-data-list .tree-leaf.danger span {\n  color: var(--c-tree-danger) !important;\n}\n\n.w-qrcode-dialog .modal-dialog {\n  width: 350px;\n}\n\n.w-qrcode-dialog .close {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  height: 21px;\n}\n\n.w-qrcode-dialog h4 {\n  margin: 5px 0 0;\n  font-size: 15px;\n  font-weight: normal;\n}\n\n.w-qrcode-dialog canvas {\n  width: 320px;\n  height: 320px;\n}\n\n.w-qrcode-url-wrap {\n  display: flex;\n  align-items: center;\n  width: 320px;\n}\n\n.w-qrcode-url-wrap input {\n  flex: 1;\n}\n\n.w-qrcode-url-wrap span, .w-root-ca-url-wrap span {\n  font-size: 18px;\n  cursor: pointer;\n  margin-left: 10px;\n  color: var(--c-thin);\n}\n\n.w-qrcode-dialog input {\n  border: 1px solid var(--c-border);\n  padding: 3px;\n  display: block;\n  width: 320px;\n  overflow: hidde;\n  margin: 15px 0 10px;\n}\n\n.w-qrcode-dialog .modal-dialog .modal-body,\n.w-qrcode-dialog .modal-dialog .modal-footer {\n  padding: 10px 15px;\n}\n\n.w-ctx-menu li[data-menu-action='Plugins'] ul {\n  overflow-x: hidden;\n  overflow-y: auto;\n  max-height: 310px;\n}\n\n.w-ctx-menu li[data-menu-action='Plugins'] li {\n  max-width: 260px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.w-ctx-menu li[data-menu-action='Modify'] ul {\n  top: -61px !important;\n}\n\n.w-spinner {\n  position: absolute;\n  top: -9px;\n  right: -2px;\n  font-size: 12px;\n  -webkit-transform: scale(0.625, 0.625);\n  transform: scale(0.625, 0.625);\n  overflow: hidden;\n  padding: 11px 4px 10px 0;\n}\n.w-spinner span {\n  display: block;\n}\n.w-spinner .glyphicon-triangle-top.spinner-desc {\n  visibility: hidden;\n}\n.w-spinner .glyphicon-triangle-bottom.spinner-asc {\n  visibility: hidden;\n}\n\n.w-header-drag-block {\n  position: absolute;\n  top: 0;\n  right: -2px;\n  height: 28px;\n  width: 12px;\n  cursor: col-resize;\n  z-index: 1;\n}\n\n.tree-node {\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n  line-height: 24px;\n  padding: 0 10px;\n  font-size: 12px;\n  outline: none;\n}\n\n.tree-node:hover {\n  background: var(--b-btn-hover);\n}\n\n.tree-node.w-selected {\n  background: var(--b-tree-active);\n  color: var(--c-active);\n}\n\n.highlight, .w-highlight td, .w-highlight th {\n  animation-name: blink;\n  animation-iteration-count: 1;\n  animation-duration: 1s;\n  animation-timing-function: linear;\n}\n\n@keyframes blink {\n  70% {\n    background-color: var(--b-blink);\n  }\n}\n\n.icon-fold {\n  font-size: 10px;\n  opacity: 0.5;\n  padding: 10px 10px;\n  margin: -10px;\n  margin-right: -6px;\n  transform: scale(0.8);\n}\n\n.w-tree-view-list > div > .ReactVirtualized__Grid {\n  overflow: auto !important;\n}\n.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer {\n  overflow: auto !important;\n  width: 100% !important;\n  overflow-y: hidden !important;\n}\n\n.w-tree-view-list .ReactVirtualized__Grid__innerScrollContainer tr {\n  width: auto !important;\n  white-space: nowrap;\n}\n\n.w-tree-view-list\n  .ReactVirtualized__Grid__innerScrollContainer::-webkit-scrollbar {\n  width: 10px;\n  height: 0px;\n}\n\n.w-tree-view-list\n  .ReactVirtualized__Grid__innerScrollContainer::-webkit-scrollbar-thumb {\n  border-radius: 8px;\n}\n\n.w-record-status {\n  line-height: 28px;\n  font-size: 14px;\n  text-align: center;\n  background-color: var(--b-warn);\n  border-bottom: 1px solid var(--c-border);\n  color: var(--c-error);\n}\n\n.w-record-status .btn {\n  font-size: 12px;\n  line-height: 20px;\n  height: 22px;\n  margin-left: 20px;\n  padding: 0 8px;\n  margin-top: -2px;\n}\n\n.w-pr td, .w-pr th {\n  font-style: italic;\n  font-weight: normal!important;\n}\n\n.w-back-to-the-bottom {\n  position: absolute;\n  bottom: 5px;\n  right: 13px;\n  font-size: 11px;\n  color: var(--c-link);\n  padding: 5px 8px;\n  background-color: var(--b-default);\n  border: 1px solid var(--c-border);\n  border-radius: 20px;\n  opacity: .85;\n  display: none;\n}\n.w-back-to-the-bottom span {\n  margin-right: 3px;\n}\n.w-back-to-the-bottom:hover {\n  opacity: 1;\n}\n\n.w-save-sessions-label .form-control {\n  width: 350px!important;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/req-detail.css",
    "content": ".w-detail-request textarea {\n  padding: 5px;\n  border: none;\n}\n\n.w-detail-request-headers,\n.w-detail-request-cookies,\n.w-detail-request-query,\n.w-detail-request-form,\n.w-detail-request-form {\n  overflow: auto;\n}\n\n.w-detail-inspectors-title,\n.w-detail-webforms-title {\n  line-height: 18px;\n  background: var(--b-title);\n  font-size: 12px;\n  padding: 0 10px;\n  border-bottom: 1px solid var(--c-border);\n}\n.w-detail-inspectors-title {\n  padding: 0 5px !important;\n}\n.w-detail-inspectors-title {\n  font-weight: bold;\n}\n.w-detail-webforms-title {\n  line-height: 16px !important;\n}\n.w-detail-inspectors-url.w-props td pre {\n  overflow-x: hidden;\n  overflow-y: auto;\n  max-height: 35px;\n  padding-right: 8px;\n}\n\n.w-detail-inspectors-url.w-props th {\n  width: 60px;\n  white-space: nowrap;\n}\n\n.w-detail-inspectors-url.w-props td {\n  padding-right: 0;\n}\n\n.w-custom-tabs::-webkit-scrollbar {\n  width: 1px;\n  height: 1px;\n}\n.w-custom-tabs::-webkit-scrollbar-button {\n  width: 1px;\n  height: 1px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/res-detail.css",
    "content": ".w-detail-res textarea {\n  padding: 5px;\n  border: none;\n}\n.w-detail-res-headers,\n.w-detail-res-cookies {\n  overflow: auto;\n}\n.w-detail-res-cookies table {\n  table-layout: auto;\n}\n\n.w-detail-res-cookies tbody > tr > td:nth-child(7),\n.w-detail-res-cookies tbody > tr > td:nth-child(8),\n  .w-detail-res-cookies tbody > tr > td:nth-child(10) {\n  text-align: center;\n  vertical-align: middle;\n}\n.w-show-history .w-btn-group-sm {\n  height: 22px;\n  overflow: hidden;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/service.css",
    "content": ".w-service-btn {\n  font-weight: bold;\n}\n\n.w-service-btn .w-disabled {\n  cursor: pointer!important;\n}\n\n.w-large-dialog.w-login-dialog .modal-dialog {\n  width: 420px;\n  height: 520px;\n  min-width: 420px;\n  min-height: 520px;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/sync-dialog.css",
    "content": ".w-sync-dialog .modal-dialog {\n  width: 420px;\n}\n\n.w-sync-dialog .modal-body .btn {\n  display: block;\n  width: 398px;\n  line-height: 40px;\n  font-size: 18px;\n  margin: 10px 0;\n}\n\n.w-sync-dialog .modal-body .btn span {\n  margin-right: 10px;\n}\n\n.w-history-record-list {\n  margin: 20px 0 15px 10px;\n  width: 635px;\n  display: inline-block;\n  font-weight: normal;\n  white-space: nowrap;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/table.css",
    "content": ".w-table > thead > tr > th {\n  white-space: nowrap;\n  background: var(--b-bar);\n  border-bottom: 1px;\n  font-weight: 500;\n  color: var(--c-default);\n}\n\n.w-table th,\n.w-table td {\n  font-size: 12px;\n  padding: 3px 5px !important;\n  border-top: none;\n  border-bottom: 1px solid var(--c-border);\n  word-break: break-word;\n}\n\n.w-order-table table  tr > th:first-child {\n  width: 50px;\n}\n\n.w-order-table-head {\n  background: var(--b-bar);\n}\n\n.w-order-table .table td, .w-order-table .table th {\n  border-top: none;\n  border-bottom: 1px solid var(--c-border);\n  vertical-align: middle;\n}\n\n.w-order-table .w-order-table-head th {\n  padding: 4px 8px !important;\n}\n\n.w-order-table-body {\n  max-height: calc(100% - 28px);\n  overflow-y: auto;\n}\n\n.w-order-table-body td, .w-order-table-body th {\n  font-size: 13px;\n  font-weight: 400;\n}\n\n.w-table-cell-middle {\n  vertical-align: middle!important;\n}\n\n.w-order-table-operation {\n  white-space: nowrap;\n}\n\n.w-order-table-operation a {\n  margin-right: 20px;\n}\n.w-order-table-operation a:last-child {\n  margin-right: 0;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/textarea.css",
    "content": ".w-textarea {\n  position: relative;\n}\n.w-textarea-bar {\n  display: none;\n  position: absolute;\n  z-index: 1;\n  border-radius: 2px;\n  top: 2px;\n  right: 12px;\n  font-size: 12px;\n  white-space: nowrap;\n  line-height: 1.5;\n  background: var(--b-heavy);\n}\n.w-textarea:hover .w-textarea-bar {\n  display: block;\n}\n.w-textarea-bar a {\n  display: inline-block;\n  text-decoration: none;\n  color: var(--c-thin);\n  padding: 0 5px;\n}\n.w-textarea-bar a:hover {\n  color: var(--c-default);\n}\n\n.w-textarea-input {\n  display: block;\n  position: absolute;\n  background: var(--b-default);\n  border: 1px solid var(--c-border);\n  border-radius: 2px;\n  z-index: 101;\n  top: 22px;\n  right: 0;\n  display: none;\n  white-space: nowrap;\n}\n.w-textarea-input input {\n  width: 246px;\n  height: 32px;\n  border: 1px solid var(--c-border);\n  border-radius: 2px;\n  padding: 0 5px;\n  vertical-align: middle;\n}\n.w-textarea-input .btn {\n  height: 32px;\n  padding: 0 12px;\n  vertical-align: middle;\n  border-radius: 0;\n  border-top-right-radius: 2px;\n  border-bottom-right-radius: 2px;\n  margin-left: 1px;\n}\n\n.w-textview-tips {\n  color: var(--c-disabled);\n  text-align: center;\n  width: 520px;\n  height: 70px;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  margin: -50px 0 0 -260px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  flex-direction: column;\n}\n.w-textview-tips p {\n  font-size: 14px;\n  line-height: 36px;\n  margin: 0;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow: hidden;\n}\n.w-textview-tips a {\n  font-size: 12px;\n  color: var(--c-disabled);\n  text-decoration: none;\n}\na.w-textview-tips {\n  color: var(--c-link);\n}\n.w-textview-tips a:hover,\na.w-textview-tips:hover {\n  text-decoration: underline;\n}\n\n.w-textview-tips a.glyphicon-question-sign {\n  text-decoration: none!important;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/theme.css",
    "content": "\n[data-theme=\"dark\"]:root {\n  color-scheme: dark;\n  /* 颜色变量 */\n  --c-default: #f0f0f0;\n  --c-btn: #eee;\n  --c-active: #1a1a1a;\n  --c-heavy: #ddd;\n  --c-editor: #ccc;\n  --c-forbidden: #c0c060;\n  --c-disabled: #666666;\n  --c-link: #4a9fe3;\n  --c-link-hover: #7bb8f0;\n  --c-border: #444;\n  --c-error: #ff6b6b;\n  --c-risk: #ff8a8a;\n  --c-danger: #ff6da9;\n  --c-gray: #aaa;\n  --c-thin: #ccc;\n  --c-ok: #70d485;\n  --c-has: #7a7aff;\n  --c-warn: #ffcc44;\n  --c-warn-active: #ffe066;\n  --c-post: #6da9df;\n  --c-del: #e57c6d;\n  --c-head: #6dc8b2;\n  --c-options: #ffc266;\n  --c-trace: #6cd3ec;\n  --c-patch: #b080d0;\n  --c-put: #f0b55a;\n  --c-tag: #6cd3ec;\n  --c-tagx: #f0c066;\n  --c-rule-filter: #a580e8;\n  --c-rule-props: #ffb366;\n  --c-req: #44cc44;\n  --c-res: #ff8c42;\n\n  /* 树状结构颜色 */\n  --c-tree-success: #6dc291;\n  --c-tree-info: #6db0d0;\n  --c-tree-warning: #d4b06d;\n  --c-tree-danger: #e57c6d;\n  --c-tree-forbidden: #e5a844;\n  --c-tree-mark: #5a9cf5;\n\n  /* 代码高亮颜色 */\n  --c-j0: #0a0f14;\n  --c-j1: #101a20;\n  --c-j2: #2d3b42;\n  --c-j3: #3a4a52;\n  --c-j4: #5a6a70;\n  --c-j5: #7a8a8a;\n  --c-j6: #e8dbb5;\n  --c-j7: #f6e9c3;\n  --c-j8: #ff6b6b;\n  --c-j9: #ff8c42;\n  --c-ja: #ffcc44;\n  --c-jb: #99cc66;\n  --c-jc: #44cccc;\n  --c-jd: #5a9cf5;\n  --c-je: #9d8ef0;\n  --c-jf: #f06da3;\n\n  /* 文字颜色 */\n  --c-success: #5cb85c;\n  --c-primary: #6da8ff;\n  --c-primary-hover: #8bbdff;\n  --c-primary-active: #a9d1ff;\n  --c-danger-hover: #ff8a8a;\n  --c-danger-active: #ff4d4d;\n  --c-warning: #ffcc44;\n  --c-warning-hover: #ffd966;\n  --c-warning-active: #ffe599;\n  --c-info: #6cd3ec;\n  --c-info-hover: #8bddf2;\n  --c-info-active: #a9e7f7;\n  --c-cobalt: #e6e6e6;\n\n  /* 背景颜色 */\n  --b-success: #1e3c21;\n  --b-success-hover: #3a4a38;\n  --b-warning: #5c3b00;\n  --b-warning-hover: #7a4d00;\n  --b-warning-active: #996000;\n  --b-info: #0d2b3d;\n  --b-info-hover: #124a8a;\n  --b-info-active: #1659a8;\n  --b-primary: #0d3a6b;\n  --b-primary-hover: #124a8a;\n  --b-primary-active: #1659a8;\n  --b-danger: #5c1a1a;\n  --b-danger-hover: #7a2222;\n  --b-danger-active: #4d0a0a;\n\n  --b-alert-info: #0d2b3d;\n  /* 背景颜色 */\n  --b-default: #1a1a1a;\n  --b-error: #3a2222;\n  --b-error-hover: #4a2a2a;\n  --b-active: #3f3f3f;\n  --b-gray: #333;\n  --b-btn-hover: #333;\n  --b-btn-active: #1e2c4a;\n  --b-hover: #333;\n  --b-heavy: #333;\n  --b-heavy-active: #2d2d2d;\n  --b-disabled: #333;\n  --b-bar: #212121;\n  --b-mark: #1a3a7a;\n  --b-title: #222427;\n  --b-img: #1a1a1a;\n  --b-frames: #cc6600;\n  --b-prop: #2a2d30;\n  --b-ok: #2a4a2a;\n  --b-editor: #001a33;\n  --b-filtered: #333322;\n  --b-warn: #332e22;\n  --b-req: #223322;\n  --b-req-hover: #2d402d;\n  --b-blink: #665c00;\n  --b-tree-active: #4a6a4a;\n\n  /* 时间线背景 */\n  --b-tl-dns: #2a4a45;\n  --b-tl-req: #333322;\n  --b-tl-res: #332a22;\n  --b-tl-load: #2a3a4a;\n\n  /* 列表背景 */\n  --b-list-hover: #444;\n  --b-list-warn: #40392a;\n  --b-list-info: #22333a;\n  --b-list-mark: #1a3a7a;\n  --b-list-danger-hover: #6a2323;\n  --b-modal: rgba(0, 0, 0, 0.7);\n\n  --b-cobalt: #001122;\n\n  --s-rgba5: rgba(255, 255, 255, 0.08);\n  --s-rgba8: rgba(255, 255, 255, 0.12);\n  --s-rgba12: rgba(255, 255, 255, 0.18);\n  --s-rgba15: rgba(255, 255, 255, 0.24);\n  --s-rgba20: rgba(255, 255, 255, 0.32);\n  --s-fc: rgba(77, 140, 190, 0.65);\n\n  --b-rgba50: rgba(255, 255, 255, 0.12);\n  --b-rgba70: rgba(255, 255, 255, 0.15);\n  --b-rgba15: rgba(255, 255, 255, 0.06);\n  --b-rgba30: rgba(255, 255, 255, 0.08);\n}\n\nhtml[data-theme=\"dark\"] .w-filter-input {\n  background: field;\n  color: var(--c-thin);\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/timeline.css",
    "content": ".w-timeline ul,\n.w-timeline li {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  white-space: nowrap;\n  font-size: 0;\n  width: 100%;\n  display: block;\n}\n.w-timeline li {\n  padding: 2px 0 2px;\n  position: relative;\n  white-space: nowrap;\n  margin-bottom: 12px;\n}\n.w-timeline span {\n  display: inline-block;\n  font-size: 12px;\n  height: 20px;\n  line-height: 20px;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  cursor: default;\n}\n.w-timeline-url {\n  padding-right: 5px;\n  width: 18%;\n  max-width: 200px;\n  text-align: right;\n}\n.w-timeline-stalled {\n  background: var(--c-disabled);\n}\n.w-timeline-ttfb {\n  background: var(--b-active);\n}\n.w-timeline-dns {\n  background: var(--b-tl-dns);\n}\n.w-timeline-request {\n  background: var(--b-tl-req);\n}\n.w-timeline-response {\n  background: var(--b-tl-res);\n}\n.w-timeline-load {\n  background: var(--b-tl-load);\n}\n.w-timeline-one > ul > li:hover, .w-timeline-multi:hover {\n  background: var(--b-hover);\n}\n.w-timeline-time {\n  position: absolute;\n  top: 2px;\n  right: 0;\n  padding-right: 5px;\n}\n.w-timeline-stream {\n  padding-bottom: 5px!important;\n}\n.w-timeline-one .w-timeline-url {\n  text-align: right;\n  padding-right: 10px;\n  font-weight: bold;\n}\n.w-timeline-one .w-timeline-time {\n  left: 18% !important;\n}\n.w-timeline-full-url {\n  width: 82%;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/css/tools.css",
    "content": ".w-tools {\n  position: relative;\n  min-width: 560px;\n}\n.w-tools ul,\n.w-tools li {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n  display: block;\n  width: 100%;\n  font-size: 12px;\n}\n.w-tools li {\n  width: 100%;\n  padding: 1px 0;\n  position: relative;\n}\n.w-tools pre {\n  background: none;\n  border: none;\n  padding: 10px;\n  margin: 0;\n  font-size: 12px;\n}\n\n.w-log li.w-fatal {\n  background: var(--b-gray);\n}\n.w-log li.w-error {\n  background: var(--b-error);\n}\n.w-log li.w-warn {\n  background: var(--b-warn);\n}\n.w-log li.w-info {\n  background: var(--b-default);\n}\n.w-log li.w-debug {\n  background: var(--b-disabled);\n}\n.w-log li.w-error pre {\n  color: var(--c-error);\n}\n.w-log li.w-warn pre {\n  color: var(--c-warn);\n}\n\n.w-log li > pre > ul {\n  display: inline-block;\n  background: transparent !important;\n  margin: 0 0 0 10px !important;\n  vertical-align: top;\n}\n.w-log li > pre > span {\n  vertical-align: top;\n  line-height: 24px;\n}\n.w-log li > pre ul,\n.w-log li > pre li {\n  width: auto !important;\n}\n.w-log > .w-log-ctn > ul > li {\n  border-bottom: 1px solid var(--c-border);\n}\n.w-log li > pre > span {\n  margin-left: 10px;\n}\n.w-log li > pre > span:first-child,\n.w-log li > pre > ul:first-child {\n  margin-left: 0 !important;\n}\n\n.w-server-log li.w-fatal pre {\n  color: var(--c-risk);\n}\n.w-server-log li.w-error pre {\n  color: var(--c-error);\n}\n.w-server-log li.w-warn pre {\n  color: var(--c-warn-active);\n}\n.w-server-log li.w-info pre {\n  color: var(--c-ok);\n}\n.w-server-log li.w-debug pre {\n  color: var(--c-editor);\n}\n.w-server-log li.w-trace pre {\n  color: var(--c-disabled);\n}\n.w-log, .w-server-log {\n  overflow: auto;\n}\n.w-server-log .w-log-ctn {\n  background: var(--b-editor);\n}\n.w-log-ctn {\n  overflow: auto;\n}\n.w-log-ctn strong {\n  margin-right: 5px;\n}\n.w-log-action-bar {\n  background: var(--b-title);\n  line-height: 20px;\n  height: 21px;\n  border-bottom: 1px solid var(--c-border);\n  position: relative;\n  padding-left: 10px;\n}\n.w-log-action-bar .w-textarea-bar {\n  right: 5px !important;\n  display: block !important;\n  background: transparent;\n  position: absolute !important;\n  top: 1px !important;\n}\n.w-log-action-bar .w-textarea-bar.hide {\n  display: none !important;\n}\n.w-log-action-bar .w-textarea-bar a {\n  color: var(--c-default);\n}\n.w-log-action-bar .w-textarea-bar a:hover {\n  color: var(--c-link);\n}\n.w-log-action-bar > .w-dropdown {\n  margin-right: 18px;\n  width: 70px;\n}\n.w-textarea-bar .w-disabled, .w-com-enable-body.w-disabled {\n  cursor: default;\n  color: var(--c-disabled) !important;\n}\n.w-textarea-bar .w-switch-btn {\n  display: inline-block;\n}\n\n.w-tools .w-textarea-bar .w-menu-item {\n  top: 21px;\n}\n\n.w-tools .w-textarea-bar .w-switch-btn {\n  padding-bottom: 2px;\n}\n\n.w-tools .w-textarea-bar a {\n  padding-bottom: 2px;\n}\n\n.w-log-expand-root {\n  font-weight: normal;\n  font-size: 12px;\n  line-height: 20px;\n  display: inline-block;\n  height: 20px;\n  vertical-align: top;\n  margin-left: 15px;\n}\n\n.w-log-expand-root input {\n  margin-right: 5px;\n  vertical-align: text-bottom;\n}\n\n.w-tool-box-ctn {\n  display: block;\n  width: 100%;\n  height: 160px;\n  border: 1px solid var(--c-border);\n  padding: 5px;\n}\n.w-tool-box {\n  overflow-x: hidden;\n  overflow-y: auto;\n  padding-bottom: 20px;\n}\n.w-tool-box .w-detail-inspectors-title {\n  border: none;\n  border-right: 1px solid var(--c-border);\n  line-height: 20px;\n}\n\n.w-tool-box .w-detail-inspectors-title button {\n  height: 18px;\n  line-height: 16px;\n  font-size: 12px;\n  float: right;\n  padding: 0 10px;\n  margin-top: 1px;\n}\n\n.w-tool-box-base64 {\n  height: 100px;\n  line-height: 92px;\n  text-align: center;\n  border: 1px solid var(--c-border);\n  color: var(--c-disabled);\n  font-size: 16px;\n  background: var(--b-default);\n  font-weight: 400;\n}\n\n.w-text-dialog .modal-dialog {\n  width: 900px;\n}\n\n.w-json-dialog .modal-dialog {\n  width: 905px;\n}\n\n.w-text-dialog textarea {\n  width: 879px;\n  border: 1px solid var(--c-border);\n  border-radius: 3px;\n  padding: 5px;\n}\n\n.w-text-dialog .w-textarea {\n  width: 880px;\n}\n\n.w-text-dialog button.close {\n  margin-top: -3px;\n}\n\n.w-mock-file button.close {\n  top: 3px;\n  right: 5px;\n  height: 22px;\n  line-height: 1;\n  padding: 0;\n  position: absolute;\n  width: 22px;\n}\n\n.w-mock-dialog .modal-dialog {\n  width: 1000px;\n}\n\n.w-rules-dialog {\n  z-index: var(--z-modal);\n}\n.w-create-rules-dialog {\n  z-index: calc(var(--z-modal) + 1);\n}\n.w-rules-dialog .modal-dialog {\n  width: 1020px;\n}\n\n.w-generate-cert input {\n  line-height: 34px;\n  height: 34px;\n  padding: 5px;\n  border-radius: 0;\n  border: 1px solid var(--c-border);\n  display: block;\n}\n\n.w-generate-cert button {\n  border-radius: 0;\n}\n\n.w-tools .w-custom-tabs {\n  height: 22px;\n  background-color: var(--b-title);\n}\n\n.w-tools .w-custom-tabs button.w-spec-active {\n  background-color: var(--b-btn-hover);\n}\n\n.w-tools .w-custom-tabs button {\n  border: none;\n  border-right: 1px solid var(--c-border);\n  height: 22px;\n  line-height: 18px;\n  font-size: 12px;\n  padding: 0 12px;\n  border-radius: 0;\n}\n\n.w-mock-preview {\n  line-height: 16px;\n  min-height: 36px;\n  max-height: 120px;\n  padding: 10px;\n  overflow: auto;\n  background-color: var(--b-disabled);\n  display: inline-block;\n  width: 820px;\n  vertical-align: top;\n  white-space: pre;\n  position: relative;\n  font-size: 13px;\n}\n\n.w-mock-dialog .form-control {\n  height: 32px;\n  border-radius: 2px;\n  box-shadow: none;\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.w-mock-dialog  .glyphicon-question-sign {\n  margin-right: 5px;\n}\n\n.w-mock-row {\n  white-space: nowrap;\n  font-size: 12px;\n  font-weight: normal;\n  margin-top: 15px;\n  position: relative;\n}\n\n.w-mock-row span {\n  display: inline-block;\n  width: 120px;\n  padding-right: 10px;\n  font-weight: 600;\n  font-size: 14px;\n  text-align: right;\n}\n\n.w-mock-row a, .w-big-editor-dialog .w-mock-action a {\n  color: var(--c-default);\n  text-decoration: none;\n}\n\n.w-mock-row a:hover, .w-big-editor-dialog .w-mock-action a:hover {\n  color: var(--c-link-hover);\n}\n\n.w-url-pattern {\n  width: 778px;\n  border-top-right-radius: 0!important;\n  border-bottom-right-radius: 0!important;\n}\n\n.w-mock-row .w-com-params {\n  line-height: 30px;\n  border-left: none;\n}\n\n.w-mock-protocol {\n  width: 160px;\n  padding-right: 0;\n}\n\n.w-mock-value-options {\n  width: 105px;\n  padding-right: 0;\n  margin: 0 10px;\n}\n\n.w-mock-key-name {\n  width: 375px;\n  margin: 0 10px 0 0;\n  margin-left: 10px;\n  font-weight: bold;\n}\n\n.w-mock-value-type {\n  width: 170px;\n  padding-right: 10px;\n}\n\n.w-mock-comment {\n  display: none;\n}\n\n.w-mock-file .w-mock-comment {\n  margin-left: 20px;\n  display: inline-block;\n  color: var(--c-thin);\n}\n\n.w-mock-file .w-mock-comment input {\n  margin-left: 5px;\n  width: 291px;\n  font-weight: normal;\n  font-size: 12px;\n }\n\n.w-mock-dialog .modal-footer select {\n  width: 220px;\n  vertical-align: middle;\n  margin-right: 20px;\n}\n\n.w-mock-inline {\n  display: inline-block;\n  vertical-align: top;\n  width: 555px;\n  height: 200px;\n  border: 1px solid var(--c-border);\n  border-radius: 2px;\n  padding: 5px;\n}\n\n.w-mock-row .w-mock-rules-action {\n  display: none;\n  position: absolute;\n  top: 0;\n  right: 40px;\n  z-index: 1;\n}\n\n.w-mock-row .w-mock-rules-action a,\n.w-mock-row .w-mock-action a,\n.w-big-editor-dialog .modal-body .w-mock-action a {\n  padding: 0 6px;\n  border-radius: 2px;\n  background: var(--b-title);\n  display: inline-block;\n  color: var(--c-thin);\n}\n\n.w-mock-row .w-mock-rules-action a:hover,\n.w-mock-row .w-mock-action a:hover,\n.w-big-editor-dialog .modal-body .w-mock-action a:hover {\n  color: var(--c-default);\n}\n\n.w-mock-row:hover .w-mock-rules-action {\n  display: inline-block;\n}\n\n.w-rules-dialog select {\n  margin: 0 10px;\n  width: 260px;\n  display: inline-block;\n  vertical-align: middle;\n}\n\n.w-rules-dialog .w-list-content {\n  height: 500px;\n}\n\n.w-rules-dialog .CodeMirror {\n  border-radius: 3px;\n}\n\n.w-rules-dialog .modal-title {\n  padding: 10px 0;\n  font-weight: bold;\n}\n\n.w-mock-action {\n  top: 2px;\n  right: 20px;\n  position: absolute;\n  display: none;\n}\n\n.w-big-editor-dialog .modal-body .w-mock-action {\n  top: 12px;\n  right: 11px;\n  font-size: 12px;\n}\n\n.w-mock-row:hover .w-mock-action, .w-big-editor-dialog .modal-body:hover .w-mock-action {\n  display: block;\n  background: var(--b-title);\n  border-radius: 3px;\n  z-index: 1;\n}\n\n.w-mock-dialog, .w-mock-dialog .form-control {\n  font-size: 13px;\n}\n\n.w-mock-dialog .w-mock-row .glyphicon-trash {\n  position: absolute;\n  display: inline-block;\n  width: 16px;\n  top: 12px;\n  right: 15px;\n  color: var(--c-thin);\n  cursor: pointer;\n}\n\n.w-mock-dialog .w-mock-row .glyphicon-trash:hover {\n  color: var(--c-default);\n}\n\n.w-mock-dialog .w-com-params-editor {\n  top: 60px;\n  right: 30px;\n  width: 840px;\n}\n\n.w-com-params-editor .w-params-clear-btn .glyphicon-trash {\n  width: 16px;\n  margin-right: 0;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/about.js",
    "content": "require('../css/about.css');\nvar React = require('react');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar storage = require('./storage');\nvar util = require('./util');\nvar events = require('./events');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar isElectron = util.isElectron;\nvar clientName = isElectron ? 'electron' : 'nodejs';\n\nvar About = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  hasNewVersion: function (data) {\n    var clientVersion = this.props.clientVersion;\n    var state = this.state;\n    var flag = util.compareVersion(data.latestVersion, data.version);\n    state._hasNewWhistle = flag;\n    state._hasNewClient = 0;\n    flag = flag && util.compareVersion(data.latestVersion, storage.get('latestVersion'));\n    if (!flag && clientVersion && data.latestClientVersion) {\n      state._hasNewClient = util.compareVersion(data.latestClientVersion, clientVersion);\n      return state._hasNewClient &&\n      util.compareVersion(data.latestClientVersion, storage.get('latestClientVersion'));\n    }\n    return flag;\n  },\n  componentDidMount: function () {\n    var self = this;\n    var updateVersion = function(data) {\n      self.setState({\n        version: data.version,\n        latestVersion: data.latestVersion,\n        latestClientVersion: data.latestClientVersion,\n        hasUpdate: self.checkUpdate(self.hasNewVersion(data))\n      });\n    };\n    dataCenter.getInitialData(updateVersion);\n    events.on('updateVersion', function(_, data) {\n      updateVersion(data);\n    });\n  },\n  checkUpdate: function (hasNew) {\n    if (this.props.onCheckUpdate) {\n      if ((!this._hasUpdate && !hasNew) || hasNew !== this._hasUpdate) {\n        this._hasUpdate = hasNew;\n        this.props.onCheckUpdate(hasNew);\n      }\n    }\n    return hasNew;\n  },\n  checkUpdateClient: function (e) {\n    if (this.state._hasNewClient && dataCenter.showLatestClientVersion()) {\n      e.preventDefault();\n    }\n  },\n  showAboutInfo: function () {\n    var self = this;\n    self.showDialog();\n    var onClick = self.props.onClick;\n    if (typeof onClick === 'function') {\n      onClick();\n    }\n\n    dataCenter.checkUpdate(function (data) {\n      if (data && data.ec === 0) {\n        if (data.latestVersion) {\n          storage.set('latestVersion', data.latestVersion);\n        }\n        if (data.latestClientVersion) {\n          storage.set('latestClientVersion', data.latestClientVersion);\n        }\n        self.setState({\n          version: data.version,\n          latestVersion: data.latestVersion,\n          latestClientVersion: data.latestClientVersion,\n          hasUpdate: self.checkUpdate(self.hasNewVersion(data))\n        });\n      }\n    });\n  },\n  showDialog: function () {\n    this.refs.aboutDialog.show();\n  },\n  hideDialog: function () {\n    this.refs.aboutDialog.hide();\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var version = state.version;\n    var latest = state.latestVersion;\n    var latestClient = state.latestClientVersion;\n    var clientVersion = self.props.clientVersion;\n    var hasNewWhistle = state._hasNewWhistle;\n    var hasNewClient = state._hasNewClient;\n\n    return (\n      <a\n        draggable=\"false\"\n        onClick={self.showAboutInfo}\n        className=\"w-about-menu\"\n      >\n        {state.hasUpdate ? <i className=\"w-new-version-icon\" /> : null}\n        <Icon name=\"info-sign\" />About\n        <Dialog ref=\"aboutDialog\" wstyle=\"w-about-dialog\">\n          <div className=\"modal-body w-about-has-plugins\">\n            <CloseBtn />\n            <img alt=\"logo\" src=\"img/whistle.png?v=2016\" />\n            <span className=\"w-about-dialog-ctn\">\n              <span className=\"w-about-dialog-title\">\n                Whistle for Web Developers\n              </span>\n              {clientVersion ? 'Client Version: ' : null}\n              {clientVersion ? <a\n                className=\"w-about-version\"\n                href=\"https://github.com/avwo/whistle-client/blob/main/CHANGELOG.md\"\n                target=\"_blank\"\n              >\n                {clientVersion}\n              </a> : null}\n              {hasNewClient ? <a\n                className=\"w-new-version\"\n                title=\"Update Whistle Client\"\n                onClick={self.checkUpdateClient}\n                href={util.UPDATE_URL}\n                target=\"_blank\"\n              >\n                (NEW: {latestClient})\n              </a> : null}\n              {clientVersion ? <br /> : null}\n              {clientVersion ? 'Whistle Version: ' : 'Version: '}\n              <a\n                className=\"w-about-version\"\n                href=\"https://github.com/avwo/whistle/blob/master/CHANGELOG.md\"\n                target=\"_blank\"\n              >\n                {version}\n              </a>\n              {hasNewWhistle ? <a\n                className=\"w-new-version\"\n                title={clientVersion ? 'Update Whistle Client' : 'Update Whistle'}\n                onClick={self.checkUpdateClient}\n                href={util.UPDATE_URL}\n                target=\"_blank\"\n              >\n                (NEW: {latest})\n              </a> : null}\n              <br />\n              Visit{' '}\n              <a\n                className=\"w-about-url\"\n                href={util.getDocUrl() + '?type=' + clientName + '&version=' + version}\n                target=\"_blank\"\n              >\n                https://wproxy.org\n              </a>\n            </span>\n          </div>\n          <div className=\"modal-footer\">\n            {hasNewWhistle || hasNewClient ? (\n              <a\n                className=\"btn btn-primary\"\n                title={clientVersion ? 'Update Whistle Client' : 'Update Whistle'}\n                onClick={self.checkUpdateClient}\n                href={util.UPDATE_URL}\n                target=\"_blank\"\n              >\n                Update Now\n              </a>\n            ) : null}\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Close\n            </button>\n          </div>\n        </Dialog>\n      </a>\n    );\n  }\n});\n\nmodule.exports = About;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/base-css.js",
    "content": "require('bootstrap/dist/css/bootstrap.css');\nrequire('../css/base.css');\nrequire('../css/override.css');\nwindow.jQuery = require('jquery'); //for bootstrap\nrequire('bootstrap/dist/js/bootstrap.js');\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/bridge.js",
    "content": "var qrCode = require('qrcode');\nvar $ = require('jquery');\nvar message = require('./message');\nvar createCgi = require('./cgi').createCgi;\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar modal = require('./modal');\nvar events = require('./events');\nvar mockWin = require('./win');\nvar parseRules = require('./parse-rules');\n\nvar dataModal = dataCenter.networkModal;\n\nfunction compatAjax(options) {\n  if (typeof options !== 'string') {\n    options.type = options.type || options.method;\n  }\n  return options;\n}\n\nfunction getPlugin(win) {\n  if (!win) {\n    return;\n  }\n  try {\n    var pathname = win.location.pathname.split('/');\n    for (var i = pathname.length - 1; i >= 0; i--) {\n      var name = pathname[i];\n      if (/^plugin\\.([a-z\\d_\\-]+)$/.test(name)) {\n        var plugin = dataCenter.getPlugin(RegExp.$1  + ':');\n        if (plugin) {\n          return plugin;\n        }\n      }\n    }\n  } catch (e) {}\n}\n\nfunction getBridge(win, api) {\n  var plugin = getPlugin(win);\n  var result = {\n    updateUI: function() {\n      events.trigger('updateUIThrottle');\n    },\n    pageId: dataCenter.getPageId(),\n    getWhistleId: function() {\n      return dataCenter.whistleId;\n    },\n    hasWhistleToken: function() {\n      return dataCenter.hasWhistleToken;\n    },\n    escapeHtml: util.escape,\n    compose: dataCenter.compose,\n    createComposeInterrupt: dataCenter.createComposeInterrupt,\n    importSessions: dataCenter.importAnySessions,\n    exportSessions: dataCenter.exportSessions,\n    msgBox: message,\n    qrCode: qrCode,\n    qrcode: qrCode,\n    decodeBase64: util.decodeBase64,\n    joinBase64: util.joinBase64,\n    getReqId: dataCenter.getReqId,\n    onComposeData: dataCenter.onComposeData,\n    offComposeData: dataCenter.offComposeData,\n    alert: mockWin.alert,\n    confirm: mockWin.confirm,\n    showNetwork: function () {\n      events.trigger('showNetwork');\n    },\n    showRules: function (name) {\n      events.trigger('showRules', name);\n    },\n    showValues: function () {\n      events.trigger('showValues');\n    },\n    showPlugins: function () {\n      events.trigger('showPlugins');\n    },\n    getActiveSession: function () {\n      return dataModal.getActive();\n    },\n    getSelectedSessionList: function () {\n      return dataModal.getSelectedList();\n    },\n    importMockData: function(data) {\n      return util.handleImportData(data);\n    },\n    download: function(data) {\n      events.trigger('download', [data]);\n    },\n    showOption: function() {\n      events.trigger('showPluginOption', plugin);\n    },\n    hideOption: function() {\n      events.trigger('hidePluginOption', plugin);\n    },\n    setNetworkSettings: function(data) {\n      events.trigger('setNetworkSettings', data);\n    },\n    setRulesSettings: function(data) {\n      events.trigger('setRulesSettings', data);\n    },\n    setValuesSettings: function(data) {\n      events.trigger('setValuesSettings', data);\n    },\n    setComposerData: function(data) {\n      events.trigger('setComposerData', data);\n    },\n    readFileAsText: util.readFileAsText,\n    readFileAsBase64: util.readFileAsBase64,\n    showHttpsSettings: function() {\n      events.trigger('showHttpsSettingsDialog');\n    },\n    showCustomCerts: function() {\n      events.trigger('showCustomCerts');\n    },\n    uploadCustomCerts: function(data, cb) {\n      return dataCenter.uploadCerts(data, cb);\n    },\n    showService: util.showService,\n    hideService: util.hideService,\n    getInstalledPlugins: function() {\n      return dataCenter.getInstalledPlugins();\n    },\n    showInstallPlugins: function(list, registry) {\n      events.trigger('showInstallPlugins', [list, registry]);\n    },\n    showUpdatePlugins: function(list, registry) {\n      events.trigger('showUpdatePlugins', [list, registry]);\n    },\n    getVersion: function() {\n      return dataCenter.version;\n    },\n    copyText: util.copyText,\n    syncData: function(cb) {\n      plugin && dataCenter.syncData(plugin, cb);\n    },\n    syncRules: function() {\n      plugin && dataCenter.syncRules(plugin);\n    },\n    syncValues: function() {\n      plugin && dataCenter.syncValues(plugin);\n    },\n    request: function (options, cb) {\n      var request = createCgi(compatAjax(options));\n      return request(options.data, cb);\n    },\n    createRequest: function (options) {\n      return createCgi(compatAjax(options));\n    },\n    parseRules: parseRules,\n    showModal: modal.show,\n    getServerInfo: function () {\n      var serverInfo = dataCenter.getServerInfo();\n      return serverInfo && $.extend(true, {}, serverInfo);\n    },\n    importRules: function (data) {\n      events.trigger('handleImportRules', data);\n    },\n    importValues: function (data) {\n      events.trigger('handleImportValues', data);\n    }\n  };\n  if (api) {\n    Object.keys(api).forEach(function (key) {\n      result[key] = api[key];\n    });\n  }\n  return result;\n}\n\nmodule.exports = getBridge;\n\ngetBridge.getServiceBridge = function(closeDialog) {\n  var bridgeApi = getBridge(null, {\n    login: function(data, cb) {\n      if (typeof data !== 'string') {\n        data = JSON.stringify(data);\n      }\n      dataCenter.login(data, cb);\n    },\n    logout: function(cb) {\n      dataCenter.logout(cb);\n    }\n  });\n  bridgeApi.closeDialog = closeDialog;\n  bridgeApi.installPlugins = dataCenter.installPluginsFromService;\n  return bridgeApi;\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/btn-group.js",
    "content": "require('../css/btn-group.css');\nvar React = require('react');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar BtnGroup = React.createClass({\n  handleClick: function (btn) {\n    if (btn.active || btn.disabled) {\n      return;\n    }\n    this.clearSelection();\n    btn.active = true;\n    if (!this.props.onClick || this.props.onClick(btn)) {\n      this.setState({\n        curBtn: btn\n      });\n    }\n  },\n  clearSelection: function() {\n    var list = this.props.tabs || this.props.btns;\n    list.forEach(function (btn) {\n      btn.active = false;\n    });\n  },\n  onDoubleClick: function (e) {\n    if (this.props.onDoubleClick) {\n      this.props.onDoubleClick(e);\n    }\n    e.stopPropagation();\n  },\n  render: function () {\n    var self = this;\n    var tabs = self.props.tabs;\n    var isSmall = self.props.type === 's';\n    var list = tabs || self.props.btns;\n    var disabled = util.getBool(self.props.disabled);\n\n    return (\n      <div\n        onDoubleClick={self.props.onDoubleClickBar}\n        className={\n          'btn-group btn-group-sm ' +\n          (tabs ? 'w-tabs-sm' : 'w-btn-group-sm') +\n          (isSmall ? ' small' : '')\n        }\n      >\n        {list.map(function (btn) {\n          btn.disabled = disabled;\n          var icon = btn.icon ? <Icon name={btn.icon} /> : '';\n          var clazz = btn.className ? ' ' + btn.className : '';\n          btn.key = btn.key || util.getKey();\n\n          return (\n            <button\n              onClick={function () {\n                self.handleClick(btn);\n              }}\n              onDoubleClick={self.onDoubleClick}\n              key={btn.key}\n              type=\"button\"\n              data-name={btn.name}\n              style={{ display: btn.hide ? 'none' : undefined }}\n              title={btn.title}\n              className={\n                'btn btn-default' +\n                (btn.active && !disabled ? ' active' : '') +\n                clazz\n              }\n            >\n              {icon}\n              {btn.display || btn.name}\n            </button>\n          );\n        })}\n        {self.props.appendTabs || self.props.appendBtns}\n        {self.props.dockBtn}\n      </div>\n    );\n  }\n});\n\nmodule.exports = BtnGroup;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/certs-info-dialog.js",
    "content": "require('../css/certs.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar util = require('./util');\nvar Dialog = require('./dialog');\nvar TipsDialog = require('./tips-dialog');\nvar win = require('./win');\nvar dataCenter = require('./data-center');\nvar message = require('./message');\nvar Icon = require('./icon');\nvar HelpIcon = require('./help-icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_CERT_SIZE = 128 * 1024;\n\nfunction getCertName(cert, filename) {\n  filename = filename || cert.filename;\n  return filename + '.' + (cert.type || 'crt');\n}\n\nfunction readFile(file, callback) {\n  var reader = new FileReader();\n  reader.readAsText(file);\n  reader.onload = function () {\n    callback(reader.result);\n  };\n}\n\nvar CertsInfoDialog = React.createClass({\n  getInitialState: function () {\n    return { list: [] };\n  },\n  show: function (data, dir) {\n    var list = [];\n    var rootCA;\n    this._certsDir = this._certsDir || dir;\n    Object.keys(data).forEach(function (filename) {\n      var cert = data[filename];\n      var startDate = new Date(cert.notBefore);\n      var endDate = new Date(cert.notAfter);\n      var status = '';\n      var now = Date.now();\n      var isInvalid;\n      if (startDate.getTime() > now) {\n        isInvalid = true;\n        status = 'Invalid';\n      } else if (endDate.getTime() < now) {\n        isInvalid = true;\n        status = 'Expired';\n      }\n      var item = {\n        dir: cert.dir,\n        filename: filename,\n        domain: cert.dnsName,\n        disabled: cert.disabled,\n        mtime: cert.mtime,\n        type: cert.type,\n        validity: startDate.toLocaleString() + ' ~ ' + endDate.toLocaleString(),\n        status: status || <Icon name=\"ok\" />,\n        isInvalid: isInvalid\n      };\n      if (filename === 'root') {\n        item.displayName = 'root (Root CA)';\n        rootCA = item;\n        item.readOnly = true;\n        item.isRoot = true;\n      } else {\n        if (filename[0] === 'z' && filename[1] === '/') {\n          filename = filename.substring(2);\n          item.readOnly = true;\n        }\n        item.displayName = getCertName(item, filename);\n        list.push(item);\n      }\n    });\n    list.sort(function (a, b) {\n      if (a.readOnly) {\n        return b.readOnly ? 0 : -1;\n      }\n      if (b.readOnly) {\n        return 1;\n      }\n      return util.compare(b.mtime, a.mtime);\n    });\n    if (rootCA) {\n      list.unshift(rootCA);\n    }\n    this.refs.certsInfoDialog.show();\n    this._hideDialog = false;\n    this.setState({ list: list });\n  },\n  hide: function () {\n    this.refs.certsInfoDialog.hide();\n    this._hideDialog = true;\n  },\n  showRemoveTips: function (item) {\n    var dir = (item.dir || '').replace(/\\\\/g, '/');\n    dir = dir + (/\\/$/.test(dir) ? '' : '/');\n    var crt = dir + getCertName(item);\n    var key = dir + item.filename + '.key';\n    this.refs.tipsDialog.show({\n      title: 'Delete the following files and restart whistle:',\n      tips: key + '\\n' + crt,\n      dir: item.dir\n    });\n  },\n  handleCgi: function (data, xhr) {\n    if (!data) {\n      return util.showSystemError(xhr);\n    }\n    this.show(data);\n  },\n  removeCert: function (item) {\n    var self = this;\n    win.confirm(\n      'Do you confirm the deletion of \\'' + getCertName(item) + '\\'?',\n      function (sure) {\n        if (!sure) {\n          return;\n        }\n        dataCenter.certs.remove({ filename: item.filename, type: item.type }, self.handleCgi);\n      }\n    );\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  formatFiles: function (fileList) {\n    var certs;\n    for (var i = 0, len = fileList.length; i < len; i++) {\n      var cert = fileList[i];\n      if (cert.size > MAX_CERT_SIZE || !(cert.size > 0)) {\n        message.error('Maximum file size: 128KB');\n        return;\n      }\n      var { name } = cert;\n      if (!/\\.(crt|cer|pem|key)/.test(name)) {\n        message.error('Supported file formats: .key, .crt, .cer, .pem');\n        return;\n      }\n      var suffix = RegExp.$1;\n      name = name.slice(0, -4);\n      if (!name || name.length > 128) {\n        message.error('Filename must be between 1-128 characters');\n        return;\n      }\n      certs = certs || {};\n      var pair = certs[name] || {};\n      pair[suffix == 'key' ? 'key' : 'cert'] = cert;\n      if (suffix !== 'key') {\n        pair.type = suffix;\n      }\n      certs[name] = pair;\n    }\n    if (!certs) {\n      return;\n    }\n    var result;\n    var missKeys = [];\n    var missCerts = [];\n    Object.keys(certs).forEach(function (key) {\n      var cert = certs[key];\n      if (cert.key && cert.cert) {\n        result = result || {};\n        result[key] = cert;\n      } else if (cert.key) {\n        missCerts.push(key + '.[crt/cer/pem]');\n      } else {\n        missKeys.push(key + '.key');\n      }\n    });\n    if (missKeys.length || missCerts.length) {\n      var msg = '';\n      if (missKeys.length) {\n        msg += 'Missing key files: ' + missKeys.join(', ');\n      }\n      if (missCerts.length) {\n        msg += (msg ? '\\n' : '') + 'Missing cert files: ' + missCerts.join(', ');\n      }\n      win.alert(msg);\n    }\n    return result;\n  },\n  handleChange: function (e) {\n    var self = this;\n    var input = findDOMNode(self.refs.uploadCerts);\n    var files = input.files && self.formatFiles(input.files);\n    input.value = '';\n    if (!files) {\n      return;\n    }\n    if (files.root) {\n      var dir = self._certsDir || '~/.WhistleAppData/custom_certs';\n      win.alert('Root CA must be manually copied to the following directory and Whistle restarted:\\n' + dir);\n      delete files.root;\n    }\n    var handleCallback = function () {\n      dataCenter.uploadCerts(files, self.handleCgi);\n    };\n    var keys = Object.keys(files);\n    var len = keys.length * 2;\n    keys.map(function (name) {\n      var file = files[name];\n      readFile(file.key, function (text) {\n        file.key = text;\n        if (--len === 0) {\n          handleCallback();\n        }\n      });\n      readFile(file.cert, function (text) {\n        file.cert = text;\n        if (--len === 0) {\n          handleCallback();\n        }\n      });\n    });\n  },\n  handleActive: function (e) {\n    var target = e.target;\n    var checked = target.checked;\n    var filename = target.getAttribute('data-filename');\n    var data = JSON.stringify({ filename: filename, disabled: !checked });\n    dataCenter.certs.active(data, this.handleCgi);\n  },\n  showUpload: function () {\n    findDOMNode(this.refs.uploadCerts).click();\n  },\n  showService: function () {\n    util.showService('certs/history');\n  },\n  render: function () {\n    var self = this;\n    var list = self.state.list || [];\n    return (\n      <Dialog ref=\"certsInfoDialog\" wstyle=\"w-certs-dialog\">\n        <div className=\"modal-body\">\n          <CloseBtn onClick={self.hide} />\n          <h4 className=\"w-certs-title\">\n            <HelpIcon docsUrl=\"gui/https.html#custom-certs\" />\n            Custom Certs Settings\n          </h4>\n          <table className=\"table w-hover-body\">\n            <thead>\n              <th className=\"w-certs-order\">#</th>\n              <th className=\"w-certs-active\">Active</th>\n              <th className=\"w-certs-filename\">Filename</th>\n              <th className=\"w-certs-domain\">DNS Name</th>\n              <th className=\"w-certs-validity\">Validity</th>\n              <th className=\"w-certs-status\">Status</th>\n            </thead>\n            <tbody>\n              {list.length ? (\n                list.map(function (item, i) {\n                  return (\n                    <tr\n                      className={(item.isInvalid ? 'w-cert-invalid' : '') + (item.disabled ? ' w-certs-disabled' : '')}\n                    >\n                      <th className=\"w-certs-order\">{i + 1}</th>\n                      <td className=\"w-certs-active\">\n                        {item.isRoot ? null :  <input type=\"checkbox\" data-filename={item.filename}\n                        onChange={self.handleActive} checked={!item.disabled} />}\n                      </td>\n                      <td\n                        className=\"w-certs-filename\"\n                        title={item.filename}\n                      >\n                        {item.displayName || item.filename}\n                        <br />\n                        <a\n                          className={item.readOnly ? null : 'w-delete'}\n                          onClick={function () {\n                            item.readOnly\n                              ? self.showRemoveTips(item)\n                              : self.removeCert(item);\n                          }}\n                        >\n                          {item.readOnly ? 'View path' : 'Delete'}\n                        </a>\n                      </td>\n                      <td className=\"w-certs-domain\" title={item.domain}>\n                        {item.isRoot ? null : item.domain}\n                      </td>\n                      <td\n                        className=\"w-certs-validity\"\n                        title={item.validity}\n                      >\n                        {item.validity}\n                      </td>\n                      <td className=\"w-certs-status\">{item.status}</td>\n                    </tr>\n                  );\n                })\n              ) : (\n                <tr>\n                  <td colSpan=\"5\" className=\"w-empty\">\n                    Empty\n                  </td>\n                </tr>\n              )}\n            </tbody>\n          </table>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          {dataCenter.whistleId ? <button\n            type=\"button\"\n            className=\"btn btn-warning\"\n            data-dismiss=\"modal\"\n            onClick={this.showService}\n          >\n            <Icon name=\"cloud\" />\n            Import From Service\n          </button> : null}\n          <input\n            ref=\"uploadCerts\"\n            style={{ display: 'none' }}\n            type=\"file\"\n            accept=\".crt,.cer,.pem,.key\"\n            multiple=\"multiple\"\n            onChange={self.handleChange}\n          />\n          <button\n            type=\"button\"\n            style={{\n              display: dataCenter.isDiableCustomCerts() ? 'none' : undefined,\n              marginLeft: 5\n            }}\n            className=\"btn btn-primary\"\n            onClick={self.showUpload}\n          >\n            <Icon name=\"folder-open\" />\n            Upload\n          </button>\n        </div>\n        <TipsDialog ref=\"tipsDialog\" />\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = CertsInfoDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/cgi.js",
    "content": "var $ = require('jquery');\nvar util = require('./util');\n\nvar auth = util.getQuery().authorization;\n\nfunction createCgi(url, settings) {\n  var self = this;\n  if (typeof url == 'string') {\n    url = { url: url };\n  }\n  settings = $.extend({ dataType: 'json' }, settings, url);\n  url = url.url;\n  var queue = [];\n  var jqXhr;\n\n  function cgiFn(data, callback, options) {\n    var opts = { url: typeof url == 'function' ? url() : url };\n    if (typeof data == 'function') {\n      options = callback;\n      callback = data;\n      data = null;\n    } else {\n      opts.data = data;\n    }\n\n    options = $.extend(true, {}, settings, options, opts);\n    if (jqXhr) {\n      var mode = options.mode;\n      if (mode == 'ignore') {\n        return;\n      }\n      if (mode == 'cancel') {\n        jqXhr.abort();\n      } else if (mode == 'chain') {\n        return queue.push([data, callback, options]);\n      }\n    }\n\n    var execCallback = function (data, xhr, em) {\n      jqXhr = null;\n      callback && callback.call(this, data, xhr, em);\n      var args = queue.shift();\n      args && cgiFn.apply(self, args);\n    };\n    options.success = function (data, statusText, xhr) {\n      execCallback.call(this, data, xhr);\n    };\n    options.error = function (xhr, em) {\n      if (xhr && em) {\n        xhr.errMsg = em;\n      }\n      execCallback.call(this, false, xhr, em);\n    };\n    opts = options;\n    if (auth && typeof opts.url === 'string') {\n      opts = $.extend({}, opts);\n      opts.url += (opts.url.indexOf('?') === -1 ? '?' : '&') + 'authorization=' + auth;\n    }\n    return (jqXhr = $.ajax(opts));\n  }\n\n  return cgiFn;\n}\n\nfunction create(obj, settings) {\n  var cgi = {};\n  Object.keys(obj).forEach(function (name) {\n    cgi[name] = createCgi(obj[name], settings);\n  });\n  return cgi;\n}\n\nmodule.exports = create;\nmodule.exports.createCgi = createCgi;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/close-btn.js",
    "content": "var React = require('react');\n\nvar CloseBtn = React.createClass({\n  shouldComponentUpdate: function (nextProps) {\n    var props = this.props;\n    return props.onClick !== nextProps.onClick || props.className !== nextProps.className;\n  },\n  render: function() {\n    var onClick = this.props.onClick;\n    return <button\n            type=\"button\"\n            className={'close ' + (this.props.className || '')}\n            data-dismiss={onClick ? null : 'modal'}\n            onClick={onClick}>&times;</button>;\n  }\n});\n\nmodule.exports = CloseBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/columns.js",
    "content": "var $ = require('jquery');\nvar React = require('react');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar storage = require('./storage');\nvar util = require('./util');\n\nvar settings = dataCenter.getNetworkColumns();\nvar sortedCols = Array.isArray(settings.columns) ? settings.columns : [];\nvar pluginColList = dataCenter.getPluginColumns();\nvar pluginColsMap = {};\n\nfunction getDefaultColumns() {\n  return [\n    {\n      title: 'APP',\n      name: 'app',\n      className: 'app',\n      selected: true,\n      width: 50,\n      getIcon: function getIcon(item) {\n        var appName = item.appName || 'browser';\n        var img;\n        if (/\\//.test(appName)) {\n          img = appName;\n        } else if (appName === 'whistle') {\n          img = 'img/whistle.png';\n        } else {\n          img = 'img/app/' + (appName === 'ipad' ? 'iphone' : appName) + '.png';\n        }\n        return <img src={img} title={appName} className=\"w-cell-img\" />;\n      }\n    },\n    {\n      title: 'Date',\n      name: 'date',\n      className: 'date',\n      showTitle: true,\n      width: 160\n    },\n    {\n      title: 'Result',\n      name: 'result',\n      className: 'result',\n      selected: true,\n      width: 75\n    },\n    {\n      title: 'Method',\n      name: 'method',\n      className: 'method',\n      showTitle: true,\n      selected: true,\n      width: 75\n    },\n    {\n      title: 'Protocol',\n      name: 'protocol',\n      className: 'protocol',\n      selected: true,\n      showTitle: true,\n      width: 95\n    },\n    {\n      title: 'ClientIP',\n      name: 'clientIp',\n      className: 'clientIp',\n      showTitle: true,\n      width: 110\n    },\n    {\n      title: 'ServerIP',\n      name: 'hostIp',\n      className: 'hostIp',\n      selected: true,\n      showTitle: true,\n      width: 110\n    },\n    {\n      title: 'ClientPort',\n      name: 'clientPort',\n      className: 'clientPort',\n      width: 90\n    },\n    {\n      title: 'ServerPort',\n      name: 'serverPort',\n      className: 'serverPort',\n      width: 90\n    },\n    {\n      title: 'Host',\n      name: 'hostname',\n      className: 'hostname',\n      selected: true,\n      showTitle: true,\n      width: 150\n    },\n    {\n      title: 'URL',\n      name: 'path',\n      className: 'path',\n      selected: true,\n      showTitle: true,\n      locked: true,\n      minWidth: 60\n    },\n    {\n      title: 'Type',\n      name: 'type',\n      className: 'type',\n      selected: true,\n      showTitle: true,\n      width: 125\n    },\n    {\n      title: 'Body',\n      showTitle: true,\n      name: 'body',\n      className: 'body',\n      width: 90\n    },\n    {\n      title: 'Encoding',\n      name: 'contentEncoding',\n      className: 'contentEncoding',\n      width: 90\n    },\n    {\n      title: 'DNS',\n      name: 'dns',\n      className: 'dns',\n      width: 70\n    },\n    {\n      title: 'Request',\n      name: 'request',\n      className: 'request',\n      width: 90\n    },\n    {\n      title: 'Response',\n      name: 'response',\n      className: 'response',\n      width: 90\n    },\n    {\n      title: 'Download',\n      name: 'download',\n      className: 'download',\n      width: 90\n    },\n    {\n      title: 'Time',\n      name: 'time',\n      className: 'time',\n      selected: true,\n      width: 70\n    },\n    {\n      title: 'Custom1',\n      name: 'custom1',\n      className: 'custom1',\n      showTitle: true,\n      width: 120\n    },\n    {\n      title: 'Custom2',\n      name: 'custom2',\n      className: 'custom2',\n      showTitle: true,\n      width: 160\n    }\n  ];\n}\n\nvar columnsMap;\nvar curColumns;\nvar buildInCols;\nvar colWidthData;\n\nfunction updateColumns(reseted, init) {\n  buildInCols = buildInCols || getDefaultColumns();\n  curColumns = buildInCols.concat(pluginColList);\n  curColumns.forEach(function (col) {\n    var menus = [];\n    var curWidth = colWidthData[col.name];\n    var hasSelected;\n    var width = col.minWidth || col.width;\n    var icon = col.minWidth ? '>= ' : '';\n    var round = width % 2 ? 5 : 0;\n    for (var i = 0; i < 11; i++) {\n      var w = width + i * 60 + round;\n      var selected = curWidth === w;\n      hasSelected = hasSelected || selected;\n      if (i) {\n        menus.push({ name: icon + w + 'px', action: w, selected: selected });\n      } else {\n        menus.push({ name: icon + width + 'px (Default)', action: w, selected: selected });\n      }\n    }\n    columnsMap[col.name] = col;\n    col.menus = menus;\n    if (!hasSelected) {\n      menus[0].selected = true;\n      colWidthData[col.name] = menus[0].action;\n    }\n  });\n  if (reseted) {\n    sortedCols = curColumns;\n  }\n  sortColumns(init);\n}\n\nfunction reset(init) {\n  columnsMap = {};\n  if (init) {\n    try {\n      colWidthData = JSON.parse(storage.get('networkColumnsWidth'));\n    } catch (e) {}\n    colWidthData = colWidthData || {};\n  } else {\n    buildInCols = null;\n    colWidthData = {};\n  }\n  updateColumns(!init, init);\n  if (!init) {\n    save();\n    storage.set('networkColumnsWidth');\n  }\n}\n\nfunction sortColumns(init) {\n  var columns = [];\n  var preColumns = [];\n  sortedCols.forEach(function(col) {\n    var name = col && col.name;\n    var curCol = name && columnsMap[name];\n    if (init && curCol) {\n      curCol.selected = !!col.selected;\n    }\n    if (curCol && curColumns.indexOf(curCol) !== -1 && columns.indexOf(curCol) === -1) {\n      columns.push(curCol);\n    }\n  });\n  curColumns.forEach(function(col) {\n    if (columns.indexOf(col) === -1) {\n      if (pluginColList.indexOf(col) === -1) {\n        preColumns.push(col);\n      } else {\n        columns.push(col);\n      }\n    }\n  });\n  curColumns = preColumns.concat(columns);\n  settings = { columns: curColumns };\n}\n\nreset(true);\n\nfunction save() {\n  settings.columns = curColumns;\n  dataCenter.setNetworkColumns(settings);\n}\n\nexports.getColumn = function (name) {\n  return columnsMap[name];\n};\n\nfunction moveTo(name, targetName) {\n  if (name === targetName) {\n    return;\n  }\n  var col = columnsMap[name];\n  var target = columnsMap[targetName];\n  if (!col || !target) {\n    return;\n  }\n  var fromIndex = curColumns.indexOf(col);\n  var toIndex = curColumns.indexOf(target);\n  curColumns.splice(fromIndex, 1);\n  curColumns.splice(toIndex, 0, col);\n  save();\n}\n\nexports.getAllColumns = function () {\n  return curColumns;\n};\nexports.reset = reset;\nexports.setSelected = function (name, selected) {\n  var col = columnsMap[name];\n  if (col) {\n    col.selected = selected !== false;\n    save();\n  }\n};\nexports.getSelectedColumns = function () {\n  var width = 50;\n  var list = curColumns.filter(function (col) {\n    if (col.selected || col.locked || col.isPlugin) {\n      width += colWidthData[col.name] || col.width || col.minWidth;\n      return true;\n    }\n  });\n  return {\n    width: width,\n    style: { minWidth: width },\n    list: list\n  };\n};\n\nvar COLUMN_TYPE_PREFIX = 'networkcolumn$';\nvar curTarget;\n\nfunction getTarget(e) {\n  var target = e.target;\n  var nodeName = target.nodeName;\n  if (nodeName === 'TH' || nodeName === 'LABEL') {\n    return target;\n  }\n  target = target.parentNode;\n  if (target) {\n    nodeName = target.nodeName;\n    if (nodeName === 'TH' || nodeName === 'LABEL') {\n      return target;\n    }\n  }\n}\n\nfunction getDragInfo(e) {\n  var target = getTarget(e);\n  var name = target && target.getAttribute('data-name');\n  if (!name) {\n    return;\n  }\n  var fromName = getNameFromTypes(e);\n  if (fromName && name.toLowerCase() !== fromName) {\n    return {\n      target: target,\n      toName: name\n    };\n  }\n}\n\nfunction getNameFromTypes(e) {\n  var type = util.findArray(e.dataTransfer.types, function (type) {\n    if (type.indexOf(COLUMN_TYPE_PREFIX) === 0) {\n      return true;\n    }\n  });\n  return type && type.substring(COLUMN_TYPE_PREFIX.length);\n}\n\n$(document).on('drop', function () {\n  if (curTarget) {\n    curTarget.style.background = '';\n  }\n  curTarget = null;\n});\n\nexports.getDragger = function () {\n  return {\n    onDragStart: function (e) {\n      var target = getTarget(e);\n      var name = target && target.getAttribute('data-name');\n      e.dataTransfer.setData(COLUMN_TYPE_PREFIX + name, 1);\n      e.dataTransfer.setData('-' + COLUMN_TYPE_PREFIX, name);\n    },\n    onDragEnter: function (e) {\n      var info = getDragInfo(e);\n      if (info) {\n        curTarget = info.target;\n        curTarget.style.background = 'var(--b-active)';\n      }\n    },\n    onDragLeave: function (e) {\n      var info = getDragInfo(e);\n      if (info) {\n        info.target.style.background = '';\n      }\n    },\n    onDrop: function (e) {\n      var info = getDragInfo(e);\n      if (info) {\n        var fromName = e.dataTransfer.getData('-' + COLUMN_TYPE_PREFIX);\n        moveTo(fromName, info.toName);\n        info.target.style.background = '';\n        if (typeof this.onColumnsResort === 'function') {\n          this.onColumnsResort();\n        }\n      }\n    }\n  };\n};\n\nexports.setWidth = function(name, width) {\n  colWidthData[name] = parseInt(width);\n  storage.set('networkColumnsWidth', JSON.stringify(colWidthData));\n};\n\nexports.getWidth = function(col) {\n  return colWidthData[col.name] || col.width || col.minWidth;\n};\n\nevents.on('pluginColumnsChange', function() {\n  var map = {};\n  pluginColList = dataCenter.getPluginColumns().map(function(col) {\n    map[col.name] = col;\n    var oldCol = pluginColsMap[col.name];\n    if (oldCol) {\n      oldCol.title = col.title;\n      oldCol.key = col.key;\n      oldCol.iconKey = col.iconKey;\n      oldCol.width = col.width;\n      return oldCol;\n    }\n    return col;\n  });\n  pluginColsMap = map;\n  updateColumns();\n  events.trigger('onColumnsChanged');\n});\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/json/index.js",
    "content": "exports.parse = require('./parse');\nexports.stringify = require('./stringify');\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/json/parse.js",
    "content": "/*\n    json_parse.js\n    2016-05-02\n\n    Public Domain.\n\n    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n\n    This file creates a json_parse function.\n\n        json_parse(text, reviver)\n            This method parses a JSON text to produce an object or array.\n            It can throw a SyntaxError exception.\n\n            The optional reviver parameter is a function that can filter and\n            transform the results. It receives each of the keys and values,\n            and its return value is used instead of the original value.\n            If it returns what it received, then the structure is not modified.\n            If it returns undefined then the member is deleted.\n\n            Example:\n\n            // Parse the text. Values that look like ISO date strings will\n            // be converted to Date objects.\n\n            myData = json_parse(text, function (key, value) {\n                var a;\n                if (typeof value === \"string\") {\n                    a =\n/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*)?)Z$/.exec(value);\n                    if (a) {\n                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],\n                            +a[5], +a[6]));\n                    }\n                }\n                return value;\n            });\n\n    This is a reference implementation. You are free to copy, modify, or\n    redistribute.\n\n    This code should be minified before deployment.\n    See http://javascript.crockford.com/jsmin.html\n\n    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO\n    NOT CONTROL.\n*/\n\n/*jslint for */\n\n/*property\n    at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name,\n    prototype, push, r, t, text\n*/\n\nmodule.exports = (function () {\n  // This is a function that can parse a JSON text, producing a JavaScript\n  // data structure. It is a simple, recursive descent parser. It does not use\n  // eval or regular expressions, so it can be used as a model for implementing\n  // a JSON parser in other languages.\n\n  // We are defining the function inside of another function to avoid creating\n  // global variables.\n\n  var at; // The index of the current character\n  var ch; // The current character\n  var escapee = {\n    '\"': '\"',\n    '\\\\': '\\\\',\n    '/': '/',\n    b: '\\b',\n    f: '\\f',\n    n: '\\n',\n    r: '\\r',\n    t: '\\t'\n  };\n  var text;\n\n  var error = function (m) {\n    // Call error when something is wrong.\n\n    throw {\n      name: 'SyntaxError',\n      message: m,\n      at: at,\n      text: text\n    };\n  };\n\n  var next = function (c) {\n    // If a c parameter is provided, verify that it matches the current character.\n\n    if (c && c !== ch) {\n      error(\"Expected '\" + c + \"' instead of '\" + ch + \"'\");\n    }\n\n    // Get the next character. When there are no more characters,\n    // return the empty string.\n\n    ch = text.charAt(at);\n    at += 1;\n    return ch;\n  };\n\n  var number = function () {\n    // Parse a number value.\n\n    var value;\n    var string = '';\n\n    if (ch === '-') {\n      string = '-';\n      next('-');\n    }\n    while (ch >= '0' && ch <= '9') {\n      string += ch;\n      next();\n    }\n    if (ch === '.') {\n      string += '.';\n      while (next() && ch >= '0' && ch <= '9') {\n        string += ch;\n      }\n    }\n    if (ch === 'e' || ch === 'E') {\n      string += ch;\n      next();\n      if (ch === '-' || ch === '+') {\n        string += ch;\n        next();\n      }\n      while (ch >= '0' && ch <= '9') {\n        string += ch;\n        next();\n      }\n    }\n    if (string.length > 15) {\n      value = new String(string);\n      value._$isNumber = true;\n      return value;\n    }\n    value = +string;\n    if (!isFinite(value)) {\n      error('Bad number');\n    } else {\n      return value;\n    }\n  };\n\n  var string = function () {\n    // Parse a string value.\n\n    var hex;\n    var i;\n    var value = '';\n    var uffff;\n\n    // When parsing for string values, we must look for \" and \\ characters.\n\n    if (ch === '\"') {\n      while (next()) {\n        if (ch === '\"') {\n          next();\n          return value;\n        }\n        if (ch === '\\\\') {\n          next();\n          if (ch === 'u') {\n            uffff = 0;\n            for (i = 0; i < 4; i += 1) {\n              hex = parseInt(next(), 16);\n              if (!isFinite(hex)) {\n                break;\n              }\n              uffff = uffff * 16 + hex;\n            }\n            value += String.fromCharCode(uffff);\n          } else if (typeof escapee[ch] === 'string') {\n            value += escapee[ch];\n          } else {\n            break;\n          }\n        } else {\n          value += ch;\n        }\n      }\n    }\n    error('Bad string');\n  };\n\n  var white = function () {\n    // Skip whitespace.\n\n    while (ch && ch <= ' ') {\n      next();\n    }\n  };\n\n  var word = function () {\n    // true, false, or null.\n\n    switch (ch) {\n      case 't':\n        next('t');\n        next('r');\n        next('u');\n        next('e');\n        return true;\n      case 'f':\n        next('f');\n        next('a');\n        next('l');\n        next('s');\n        next('e');\n        return false;\n      case 'n':\n        next('n');\n        next('u');\n        next('l');\n        next('l');\n        return null;\n    }\n    error(\"Unexpected '\" + ch + \"'\");\n  };\n\n  var value; // Place holder for the value function.\n\n  var array = function () {\n    // Parse an array value.\n\n    var arr = [];\n\n    if (ch === '[') {\n      next('[');\n      white();\n      if (ch === ']') {\n        next(']');\n        return arr; // empty array\n      }\n      while (ch) {\n        arr.push(value());\n        white();\n        if (ch === ']') {\n          next(']');\n          return arr;\n        }\n        next(',');\n        white();\n      }\n    }\n    error('Bad array');\n  };\n\n  var object = function () {\n    // Parse an object value.\n\n    var key;\n    var obj = {};\n\n    if (ch === '{') {\n      next('{');\n      white();\n      if (ch === '}') {\n        next('}');\n        return obj; // empty object\n      }\n      while (ch) {\n        key = string();\n        white();\n        next(':');\n        if (Object.hasOwnProperty.call(obj, key)) {\n          error(\"Duplicate key '\" + key + \"'\");\n        }\n        obj[key] = value();\n        white();\n        if (ch === '}') {\n          next('}');\n          return obj;\n        }\n        next(',');\n        white();\n      }\n    }\n    error('Bad object');\n  };\n\n  value = function () {\n    // Parse a JSON value. It could be an object, an array, a string, a number,\n    // or a word.\n\n    white();\n    switch (ch) {\n      case '{':\n        return object();\n      case '[':\n        return array();\n      case '\"':\n        return string();\n      case '-':\n        return number();\n      default:\n        return ch >= '0' && ch <= '9' ? number() : word();\n    }\n  };\n\n  // Return the json_parse function. It will have access to all of the above\n  // functions and variables.\n\n  return function (source, reviver) {\n    var result;\n\n    text = source;\n    at = 0;\n    ch = ' ';\n    result = value();\n    white();\n    if (ch) {\n      error('Syntax error');\n    }\n\n    // If there is a reviver function, we recursively walk the new structure,\n    // passing each name/value pair to the reviver function for possible\n    // transformation, starting with a temporary root object that holds the result\n    // in an empty key. If there is not a reviver function, we simply return the\n    // result.\n\n    return typeof reviver === 'function'\n      ? (function walk(holder, key) {\n          var k;\n          var v;\n          var val = holder[key];\n          if (val && typeof val === 'object') {\n            for (k in val) {\n              if (Object.prototype.hasOwnProperty.call(val, k)) {\n                v = walk(val, k);\n                if (v !== undefined) {\n                  val[k] = v;\n                } else {\n                  delete val[k];\n                }\n              }\n            }\n          }\n          return reviver.call(holder, key, val);\n        })({ '': result }, '')\n      : result;\n  };\n})();\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/json/stringify.js",
    "content": "//  json2.js\n//  2017-06-12\n//  Public Domain.\n//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n\n//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO\n//  NOT CONTROL.\n\n//  This file creates a global JSON object containing two methods: stringify\n//  and parse. This file provides the ES5 JSON capability to ES3 systems.\n//  If a project might run on IE8 or earlier, then this file should be included.\n//  This file does nothing on ES5 systems.\n\n//      JSON.stringify(value, replacer, space)\n//          value       any JavaScript value, usually an object or array.\n//          replacer    an optional parameter that determines how object\n//                      values are stringified for objects. It can be a\n//                      function or an array of strings.\n//          space       an optional parameter that specifies the indentation\n//                      of nested structures. If it is omitted, the text will\n//                      be packed without extra whitespace. If it is a number,\n//                      it will specify the number of spaces to indent at each\n//                      level. If it is a string (such as \"\\t\" or \"&nbsp;\"),\n//                      it contains the characters used to indent at each level.\n//          This method produces a JSON text from a JavaScript value.\n//          When an object value is found, if the object contains a toJSON\n//          method, its toJSON method will be called and the result will be\n//          stringified. A toJSON method does not serialize: it returns the\n//          value represented by the name/value pair that should be serialized,\n//          or undefined if nothing should be serialized. The toJSON method\n//          will be passed the key associated with the value, and this will be\n//          bound to the value.\n\n//          For example, this would serialize Dates as ISO strings.\n\n//              Date.prototype.toJSON = function (key) {\n//                  function f(n) {\n//                      // Format integers to have at least two digits.\n//                      return (n < 10)\n//                          ? \"0\" + n\n//                          : n;\n//                  }\n//                  return this.getUTCFullYear()   + \"-\" +\n//                       f(this.getUTCMonth() + 1) + \"-\" +\n//                       f(this.getUTCDate())      + \"T\" +\n//                       f(this.getUTCHours())     + \":\" +\n//                       f(this.getUTCMinutes())   + \":\" +\n//                       f(this.getUTCSeconds())   + \"Z\";\n//              };\n\n//          You can provide an optional replacer method. It will be passed the\n//          key and value of each member, with this bound to the containing\n//          object. The value that is returned from your method will be\n//          serialized. If your method returns undefined, then the member will\n//          be excluded from the serialization.\n\n//          If the replacer parameter is an array of strings, then it will be\n//          used to select the members to be serialized. It filters the results\n//          such that only members with keys listed in the replacer array are\n//          stringified.\n\n//          Values that do not have JSON representations, such as undefined or\n//          functions, will not be serialized. Such values in objects will be\n//          dropped; in arrays they will be replaced with null. You can use\n//          a replacer function to replace those with JSON values.\n\n//          JSON.stringify(undefined) returns undefined.\n\n//          The optional space parameter produces a stringification of the\n//          value that is filled with line breaks and indentation to make it\n//          easier to read.\n\n//          If the space parameter is a non-empty string, then that string will\n//          be used for indentation. If the space parameter is a number, then\n//          the indentation will be that many spaces.\n\n//          Example:\n\n//          text = JSON.stringify([\"e\", {pluribus: \"unum\"}]);\n//          // text is '[\"e\",{\"pluribus\":\"unum\"}]'\n\n//          text = JSON.stringify([\"e\", {pluribus: \"unum\"}], null, \"\\t\");\n//          // text is '[\\n\\t\"e\",\\n\\t{\\n\\t\\t\"pluribus\": \"unum\"\\n\\t}\\n]'\n\n//          text = JSON.stringify([new Date()], function (key, value) {\n//              return this[key] instanceof Date\n//                  ? \"Date(\" + this[key] + \")\"\n//                  : value;\n//          });\n//          // text is '[\"Date(---current time---)\"]'\n\n//      JSON.parse(text, reviver)\n//          This method parses a JSON text to produce an object or array.\n//          It can throw a SyntaxError exception.\n\n//          The optional reviver parameter is a function that can filter and\n//          transform the results. It receives each of the keys and values,\n//          and its return value is used instead of the original value.\n//          If it returns what it received, then the structure is not modified.\n//          If it returns undefined then the member is deleted.\n\n//          Example:\n\n//          // Parse the text. Values that look like ISO date strings will\n//          // be converted to Date objects.\n\n//          myData = JSON.parse(text, function (key, value) {\n//              var a;\n//              if (typeof value === \"string\") {\n//                  a =\n//   /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*)?)Z$/.exec(value);\n//                  if (a) {\n//                      return new Date(Date.UTC(\n//                         +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]\n//                      ));\n//                  }\n//                  return value;\n//              }\n//          });\n\n//          myData = JSON.parse(\n//              \"[\\\"Date(09/09/2001)\\\"]\",\n//              function (key, value) {\n//                  var d;\n//                  if (\n//                      typeof value === \"string\"\n//                      && value.slice(0, 5) === \"Date(\"\n//                      && value.slice(-1) === \")\"\n//                  ) {\n//                      d = new Date(value.slice(5, -1));\n//                      if (d) {\n//                          return d;\n//                      }\n//                  }\n//                  return value;\n//              }\n//          );\n\n//  This is a reference implementation. You are free to copy, modify, or\n//  redistribute.\n\n/*jslint\n    eval, for, this\n*/\n\n/*property\n    JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,\n    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,\n    lastIndex, length, parse, prototype, push, replace, slice, stringify,\n    test, toJSON, toString, valueOf\n*/\n\n// Create a JSON object only if one does not already exist. We create the\n// methods in a closure to avoid creating global variables.\n\nvar rx_escapable =\n  /[\\\\\"\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\n\nfunction f(n) {\n  // Format integers to have at least two digits.\n  return n < 10 ? '0' + n : n;\n}\n\nfunction this_value() {\n  return this.valueOf();\n}\n\nif (typeof Date.prototype.toJSON !== 'function') {\n  Date.prototype.toJSON = function () {\n    return isFinite(this.valueOf())\n      ? this.getUTCFullYear() +\n          '-' +\n          f(this.getUTCMonth() + 1) +\n          '-' +\n          f(this.getUTCDate()) +\n          'T' +\n          f(this.getUTCHours()) +\n          ':' +\n          f(this.getUTCMinutes()) +\n          ':' +\n          f(this.getUTCSeconds()) +\n          'Z'\n      : null;\n  };\n\n  Boolean.prototype.toJSON = this_value;\n  Number.prototype.toJSON = this_value;\n  String.prototype.toJSON = this_value;\n}\n\nvar gap;\nvar indent;\nvar meta;\nvar rep;\n\nfunction quote(string) {\n  // If the string contains no control characters, no quote characters, and no\n  // backslash characters, then we can safely slap some quotes around it.\n  // Otherwise we must also replace the offending characters with safe escape\n  // sequences.\n\n  rx_escapable.lastIndex = 0;\n  return rx_escapable.test(string)\n    ? '\"' +\n        string.replace(rx_escapable, function (a) {\n          var c = meta[a];\n          return typeof c === 'string'\n            ? c\n            : '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n        }) +\n        '\"'\n    : '\"' + string + '\"';\n}\n\nfunction str(key, holder) {\n  // Produce a string from holder[key].\n\n  var i; // The loop counter.\n  var k; // The member key.\n  var v; // The member value.\n  var length;\n  var mind = gap;\n  var partial;\n  var value = holder[key];\n\n  // If the value has a toJSON method, call it to obtain a replacement value.\n\n  if (\n    value &&\n    typeof value === 'object' &&\n    typeof value.toJSON === 'function'\n  ) {\n    value = value.toJSON(key);\n  }\n\n  // If we were called with a replacer function, then call the replacer to\n  // obtain a replacement value.\n\n  if (typeof rep === 'function') {\n    value = rep.call(holder, key, value);\n  }\n\n  // What happens next depends on the value's type.\n\n  switch (typeof value) {\n    case 'string':\n      return quote(value);\n\n    case 'number':\n      // JSON numbers must be finite. Encode non-finite numbers as null.\n\n      return isFinite(value) ? String(value) : 'null';\n\n    case 'boolean':\n    case 'null':\n      // If the value is a boolean or null, convert it to a string. Note:\n      // typeof null does not produce \"null\". The case is included here in\n      // the remote chance that this gets fixed someday.\n\n      return String(value);\n\n    // If the type is \"object\", we might be dealing with an object or an array or\n    // null.\n\n    case 'object':\n      // Due to a specification blunder in ECMAScript, typeof null is \"object\",\n      // so watch out for that case.\n\n      if (!value) {\n        return 'null';\n      }\n\n      // Make an array to hold the partial results of stringifying this object value.\n\n      gap += indent;\n      partial = [];\n\n      // Is the value an array?\n\n      if (Object.prototype.toString.apply(value) === '[object Array]') {\n        // The value is an array. Stringify every element. Use null as a placeholder\n        // for non-JSON values.\n\n        length = value.length;\n        for (i = 0; i < length; i += 1) {\n          partial[i] = str(i, value) || 'null';\n        }\n\n        // Join all of the elements together, separated with commas, and wrap them in\n        // brackets.\n\n        v =\n          partial.length === 0\n            ? '[]'\n            : gap\n            ? '[\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + ']'\n            : '[' + partial.join(',') + ']';\n        gap = mind;\n        return v;\n      }\n\n      if (value._$isNumber) {\n        return value.toString();\n      }\n\n      // If the replacer is an array, use it to select the members to be stringified.\n\n      if (rep && typeof rep === 'object') {\n        length = rep.length;\n        for (i = 0; i < length; i += 1) {\n          if (typeof rep[i] === 'string') {\n            k = rep[i];\n            v = str(k, value);\n            if (v) {\n              partial.push(quote(k) + (gap ? ': ' : ':') + v);\n            }\n          }\n        }\n      } else {\n        // Otherwise, iterate through all of the keys in the object.\n\n        for (k in value) {\n          if (Object.prototype.hasOwnProperty.call(value, k)) {\n            v = str(k, value);\n            if (v) {\n              partial.push(quote(k) + (gap ? ': ' : ':') + v);\n            }\n          }\n        }\n      }\n\n      // Join all of the member texts together, separated with commas,\n      // and wrap them in braces.\n\n      v =\n        partial.length === 0\n          ? '{}'\n          : gap\n          ? '{\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + '}'\n          : '{' + partial.join(',') + '}';\n      gap = mind;\n      return v;\n  }\n}\n\n// If the JSON object does not yet have a stringify method, give it one.\n\nmeta = {\n  // table of character substitutions\n  '\\b': '\\\\b',\n  '\\t': '\\\\t',\n  '\\n': '\\\\n',\n  '\\f': '\\\\f',\n  '\\r': '\\\\r',\n  '\"': '\\\\\"',\n  '\\\\': '\\\\\\\\'\n};\n\nmodule.exports = function (value, replacer, space) {\n  // The stringify method takes a value and an optional replacer, and an optional\n  // space parameter, and returns a JSON text. The replacer can be a function\n  // that can replace values, or an array of strings that will select the keys.\n  // A default replacer method can be provided. Use of the space parameter can\n  // produce text that is more easily readable.\n\n  var i;\n  gap = '';\n  indent = '';\n\n  // If the space parameter is a number, make an indent string containing that\n  // many spaces.\n\n  if (typeof space === 'number') {\n    for (i = 0; i < space; i += 1) {\n      indent += ' ';\n    }\n\n    // If the space parameter is a string, it will be used as the indent string.\n  } else if (typeof space === 'string') {\n    indent = space;\n  }\n\n  // If there is a replacer, it must be a function or an array.\n  // Otherwise, throw an error.\n\n  rep = replacer;\n  if (\n    replacer &&\n    typeof replacer !== 'function' &&\n    (typeof replacer !== 'object' || typeof replacer.length !== 'number')\n  ) {\n    throw new Error('JSON.stringify');\n  }\n\n  // Make a fake root object containing our value under the key of \"\".\n  // Return the result of stringifying the value.\n\n  return str('', { '': value });\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/ItemRange.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');\n\nvar _classCallCheck3 = _interopRequireDefault(_classCallCheck2);\n\nvar _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');\n\nvar _possibleConstructorReturn3 = _interopRequireDefault(\n  _possibleConstructorReturn2\n);\n\nvar _inherits2 = require('babel-runtime/helpers/inherits');\n\nvar _inherits3 = _interopRequireDefault(_inherits2);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONArrow = require('./JSONArrow');\n\nvar _JSONArrow2 = _interopRequireDefault(_JSONArrow);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nvar ItemRange = (function (_React$Component) {\n  (0, _inherits3['default'])(ItemRange, _React$Component);\n\n  function ItemRange(props) {\n    (0, _classCallCheck3['default'])(this, ItemRange);\n\n    var _this = (0, _possibleConstructorReturn3['default'])(\n      this,\n      _React$Component.call(this, props)\n    );\n\n    _this.state = { expanded: false };\n\n    _this.handleClick = _this.handleClick.bind(_this);\n    return _this;\n  }\n\n  ItemRange.prototype.render = function render() {\n    var _props = this.props,\n      styling = _props.styling,\n      from = _props.from,\n      to = _props.to,\n      renderChildNodes = _props.renderChildNodes,\n      nodeType = _props.nodeType;\n\n    return this.state.expanded\n      ? _react2['default'].createElement(\n          'div',\n          styling('itemRange', this.state.expanded),\n          renderChildNodes(this.props, from, to)\n        )\n      : _react2['default'].createElement(\n          'div',\n          (0, _extends3['default'])(\n            {},\n            styling('itemRange', this.state.expanded),\n            {\n              onClick: this.handleClick\n            }\n          ),\n          _react2['default'].createElement(_JSONArrow2['default'], {\n            nodeType: nodeType,\n            styling: styling,\n            expanded: false,\n            onClick: this.handleClick,\n            arrowStyle: 'double'\n          }),\n          from + ' ... ' + to\n        );\n  };\n\n  ItemRange.prototype.handleClick = function handleClick() {\n    this.setState({ expanded: !this.state.expanded });\n  };\n\n  return ItemRange;\n})(_react2['default'].Component);\n\nexports['default'] = ItemRange;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONArrayNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');\n\nvar _objectWithoutProperties3 = _interopRequireDefault(\n  _objectWithoutProperties2\n);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONNestedNode = require('./JSONNestedNode');\n\nvar _JSONNestedNode2 = _interopRequireDefault(_JSONNestedNode);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n// Returns the \"n Items\" string for this node,\n// generating and caching it if it hasn't been created yet.\nfunction createItemString(data) {\n  return data.length + ' ' + (data.length !== 1 ? 'items' : 'item');\n}\n\n// Configures <JSONNestedNode> to render an Array\nvar JSONArrayNode = function JSONArrayNode(_ref) {\n  var data = _ref.data,\n    props = (0, _objectWithoutProperties3['default'])(_ref, ['data']);\n  return _react2['default'].createElement(\n    _JSONNestedNode2['default'],\n    (0, _extends3['default'])({}, props, {\n      data: data,\n      nodeType: 'Array',\n      nodeTypeIndicator: '[]',\n      createItemString: createItemString,\n      expandable: data.length > 0\n    })\n  );\n};\n\nexports['default'] = JSONArrayNode;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONArrow.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nvar JSONArrow = function JSONArrow(_ref) {\n  var styling = _ref.styling,\n    arrowStyle = _ref.arrowStyle,\n    expanded = _ref.expanded,\n    nodeType = _ref.nodeType,\n    onClick = _ref.onClick;\n  return _react2['default'].createElement(\n    'div',\n    (0, _extends3['default'])({}, styling('arrowContainer', arrowStyle), {\n      onClick: onClick\n    }),\n    _react2['default'].createElement(\n      'div',\n      styling(['arrow', 'arrowSign'], nodeType, expanded, arrowStyle),\n      '\\u25B6',\n      arrowStyle === 'double' &&\n        _react2['default'].createElement(\n          'div',\n          styling(['arrowSign', 'arrowSignInner']),\n          '\\u25B6'\n        )\n    )\n  );\n};\n\nJSONArrow.defaultProps = {\n  arrowStyle: 'single'\n};\n\nexports['default'] = JSONArrow;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONIterableNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');\n\nvar _objectWithoutProperties3 = _interopRequireDefault(\n  _objectWithoutProperties2\n);\n\nvar _getIterator2 = require('babel-runtime/core-js/get-iterator');\n\nvar _getIterator3 = _interopRequireDefault(_getIterator2);\n\nvar _isSafeInteger = require('babel-runtime/core-js/number/is-safe-integer');\n\nvar _isSafeInteger2 = _interopRequireDefault(_isSafeInteger);\n\nexports['default'] = JSONIterableNode;\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONNestedNode = require('./JSONNestedNode');\n\nvar _JSONNestedNode2 = _interopRequireDefault(_JSONNestedNode);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n// Returns the \"n Items\" string for this node,\n// generating and caching it if it hasn't been created yet.\nfunction createItemString(data, limit) {\n  var count = 0;\n  var hasMore = false;\n  if ((0, _isSafeInteger2['default'])(data.size)) {\n    count = data.size;\n  } else {\n    // eslint-disable-next-line no-unused-vars\n    for (\n      var _iterator = data,\n        _isArray = Array.isArray(_iterator),\n        _i = 0,\n        _iterator = _isArray\n          ? _iterator\n          : (0, _getIterator3['default'])(_iterator);\n      ;\n\n    ) {\n      var _ref;\n\n      if (_isArray) {\n        if (_i >= _iterator.length) break;\n        _ref = _iterator[_i++];\n      } else {\n        _i = _iterator.next();\n        if (_i.done) break;\n        _ref = _i.value;\n      }\n\n      var entry = _ref;\n\n      if (limit && count + 1 > limit) {\n        hasMore = true;\n        break;\n      }\n      count += 1;\n    }\n  }\n  return (\n    '' +\n    (hasMore ? '>' : '') +\n    count +\n    ' ' +\n    (count !== 1 ? 'entries' : 'entry')\n  );\n}\n\n// Configures <JSONNestedNode> to render an iterable\nfunction JSONIterableNode(_ref2) {\n  var props = (0, _objectWithoutProperties3['default'])(_ref2, []);\n\n  return _react2['default'].createElement(\n    _JSONNestedNode2['default'],\n    (0, _extends3['default'])({}, props, {\n      nodeType: 'Iterable',\n      nodeTypeIndicator: '()',\n      createItemString: createItemString\n    })\n  );\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONNestedNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _stringify = require('babel-runtime/core-js/json/stringify');\n\nvar _stringify2 = _interopRequireDefault(_stringify);\n\nvar _keys = require('babel-runtime/core-js/object/keys');\n\nvar _keys2 = _interopRequireDefault(_keys);\n\nvar _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');\n\nvar _classCallCheck3 = _interopRequireDefault(_classCallCheck2);\n\nvar _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');\n\nvar _possibleConstructorReturn3 = _interopRequireDefault(\n  _possibleConstructorReturn2\n);\n\nvar _inherits2 = require('babel-runtime/helpers/inherits');\n\nvar _inherits3 = _interopRequireDefault(_inherits2);\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONArrow = require('./JSONArrow');\n\nvar _JSONArrow2 = _interopRequireDefault(_JSONArrow);\n\nvar _getCollectionEntries = require('./getCollectionEntries');\n\nvar _getCollectionEntries2 = _interopRequireDefault(_getCollectionEntries);\n\nvar _JSONNode = require('./JSONNode');\n\nvar _JSONNode2 = _interopRequireDefault(_JSONNode);\n\nvar _ItemRange = require('./ItemRange');\n\nvar _ItemRange2 = _interopRequireDefault(_ItemRange);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n/**\n * Renders nested values (eg. objects, arrays, lists, etc.)\n */\n\nfunction renderChildNodes(props, from, to) {\n  var nodeType = props.nodeType,\n    data = props.data,\n    collectionLimit = props.collectionLimit,\n    circularCache = props.circularCache,\n    keyPath = props.keyPath,\n    postprocessValue = props.postprocessValue,\n    sortObjectKeys = props.sortObjectKeys;\n\n  var childNodes = [];\n\n  (0, _getCollectionEntries2['default'])(\n    nodeType,\n    data,\n    sortObjectKeys,\n    collectionLimit,\n    from,\n    to\n  ).forEach(function (entry) {\n    if (entry.to) {\n      childNodes.push(\n        _react2['default'].createElement(\n          _ItemRange2['default'],\n          (0, _extends3['default'])({}, props, {\n            key: 'ItemRange--' + entry.from + '-' + entry.to,\n            from: entry.from,\n            to: entry.to,\n            renderChildNodes: renderChildNodes\n          })\n        )\n      );\n    } else {\n      var key = entry.key,\n        value = entry.value;\n\n      var isCircular = circularCache.indexOf(value) !== -1;\n\n      var node = _react2['default'].createElement(\n        _JSONNode2['default'],\n        (0, _extends3['default'])(\n          {},\n          props,\n          {\n            postprocessValue: postprocessValue,\n            collectionLimit: collectionLimit\n          },\n          {\n            key: 'Node--' + key,\n            keyPath: [key].concat(keyPath),\n            value: postprocessValue(value),\n            circularCache: [].concat(circularCache, [value]),\n            isCircular: isCircular,\n            hideRoot: false\n          }\n        )\n      );\n\n      if (node !== false) {\n        childNodes.push(node);\n      }\n    }\n  });\n\n  return childNodes;\n}\n\nfunction getStateFromProps(props) {\n  // calculate individual node expansion if necessary\n  var expanded =\n    props.shouldExpandNode && !props.isCircular\n      ? props.shouldExpandNode(props.keyPath, props.data, props.level)\n      : false;\n  return {\n    expanded: expanded\n  };\n}\n\nvar JSONNestedNode = (function (_React$Component) {\n  (0, _inherits3['default'])(JSONNestedNode, _React$Component);\n\n  function JSONNestedNode(props) {\n    (0, _classCallCheck3['default'])(this, JSONNestedNode);\n\n    var _this = (0, _possibleConstructorReturn3['default'])(\n      this,\n      _React$Component.call(this, props)\n    );\n\n    _this.handleClick = function () {\n      if (_this.props.expandable) {\n        _this.setState({ expanded: !_this.state.expanded });\n      }\n    };\n\n    _this.state = getStateFromProps(props);\n    return _this;\n  }\n\n  JSONNestedNode.prototype.componentWillReceiveProps =\n    function componentWillReceiveProps(nextProps) {\n      var nextState = getStateFromProps(nextProps);\n      if (getStateFromProps(this.props).expanded !== nextState.expanded) {\n        this.setState(nextState);\n      }\n    };\n\n  JSONNestedNode.prototype.shouldComponentUpdate =\n    function shouldComponentUpdate(nextProps, nextState) {\n      var _this2 = this;\n\n      return (\n        !!(0, _keys2['default'])(nextProps).find(function (key) {\n          return (\n            key !== 'circularCache' &&\n            (key === 'keyPath'\n              ? nextProps[key].join('/') !== _this2.props[key].join('/')\n              : nextProps[key] !== _this2.props[key])\n          );\n        }) || nextState.expanded !== this.state.expanded\n      );\n    };\n\n  JSONNestedNode.prototype.render = function render() {\n    var _props = this.props,\n      getItemString = _props.getItemString,\n      nodeTypeIndicator = _props.nodeTypeIndicator,\n      nodeType = _props.nodeType,\n      data = _props.data,\n      hideRoot = _props.hideRoot,\n      createItemString = _props.createItemString,\n      styling = _props.styling,\n      collectionLimit = _props.collectionLimit,\n      keyPath = _props.keyPath,\n      labelRenderer = _props.labelRenderer,\n      expandable = _props.expandable;\n    var expanded = this.state.expanded;\n\n    var renderedChildren =\n      expanded || (hideRoot && this.props.level === 0)\n        ? renderChildNodes(\n            (0, _extends3['default'])({}, this.props, {\n              level: this.props.level + 1\n            })\n          )\n        : null;\n\n    var itemType = _react2['default'].createElement(\n      'span',\n      styling('nestedNodeItemType', expanded),\n      nodeTypeIndicator\n    );\n    var renderedItemString = getItemString(\n      nodeType,\n      data,\n      itemType,\n      createItemString(data, collectionLimit)\n    );\n    var stylingArgs = [keyPath, nodeType, expanded, expandable];\n\n    return hideRoot\n      ? _react2['default'].createElement(\n          'li',\n          styling.apply(undefined, ['rootNode'].concat(stylingArgs)),\n          _react2['default'].createElement(\n            'ul',\n            styling.apply(undefined, ['rootNodeChildren'].concat(stylingArgs)),\n            renderedChildren\n          )\n        )\n      : _react2['default'].createElement(\n          'li',\n          styling.apply(undefined, ['nestedNode'].concat(stylingArgs)),\n          expandable &&\n            _react2['default'].createElement(_JSONArrow2['default'], {\n              styling: styling,\n              nodeType: nodeType,\n              expanded: expanded,\n              onClick: this.handleClick\n            }),\n          _react2['default'].createElement(\n            'label',\n            (0, _extends3['default'])(\n              {},\n              styling.apply(\n                undefined,\n                [['label', 'nestedNodeLabel']].concat(stylingArgs)\n              ),\n              {\n                onClick: this.handleClick,\n                'data-key-path': (0, _stringify2['default'])(keyPath)\n              }\n            ),\n            labelRenderer.apply(undefined, stylingArgs)\n          ),\n          _react2['default'].createElement(\n            'span',\n            (0, _extends3['default'])(\n              {},\n              styling.apply(\n                undefined,\n                ['nestedNodeItemString'].concat(stylingArgs)\n              ),\n              {\n                onClick: this.handleClick\n              }\n            ),\n            renderedItemString\n          ),\n          _react2['default'].createElement(\n            'ul',\n            styling.apply(\n              undefined,\n              ['nestedNodeChildren'].concat(stylingArgs)\n            ),\n            renderedChildren\n          )\n        );\n  };\n\n  return JSONNestedNode;\n})(_react2['default'].Component);\n\nJSONNestedNode.defaultProps = {\n  data: [],\n  circularCache: [],\n  level: 0,\n  expandable: true\n};\nexports['default'] = JSONNestedNode;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');\n\nvar _objectWithoutProperties3 = _interopRequireDefault(\n  _objectWithoutProperties2\n);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _objType = require('./objType');\n\nvar _objType2 = _interopRequireDefault(_objType);\n\nvar _JSONObjectNode = require('./JSONObjectNode');\n\nvar _JSONObjectNode2 = _interopRequireDefault(_JSONObjectNode);\n\nvar _JSONArrayNode = require('./JSONArrayNode');\n\nvar _JSONArrayNode2 = _interopRequireDefault(_JSONArrayNode);\n\nvar _JSONIterableNode = require('./JSONIterableNode');\n\nvar _JSONIterableNode2 = _interopRequireDefault(_JSONIterableNode);\n\nvar _JSONValueNode = require('./JSONValueNode');\n\nvar _JSONValueNode2 = _interopRequireDefault(_JSONValueNode);\n\nvar _expandCollapse = require('../../expand-collapse');\n\nvar _expandCollapse2 = _interopRequireDefault(_expandCollapse);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nvar JSONNode = function JSONNode(_ref) {\n  var getItemString = _ref.getItemString,\n    keyPath = _ref.keyPath,\n    labelRenderer = _ref.labelRenderer,\n    styling = _ref.styling,\n    value = _ref.value,\n    valueRenderer = _ref.valueRenderer,\n    isCustomNode = _ref.isCustomNode,\n    rest = (0, _objectWithoutProperties3['default'])(_ref, [\n      'getItemString',\n      'keyPath',\n      'labelRenderer',\n      'styling',\n      'value',\n      'valueRenderer',\n      'isCustomNode'\n    ]);\n\n  var nodeType = isCustomNode(value)\n    ? 'Custom'\n    : (0, _objType2['default'])(value);\n\n  if (nodeType === 'BigNumber') {\n    nodeType = 'Number';\n    value = value.toString();\n  }\n\n  var simpleNodeProps = {\n    getItemString: getItemString,\n    key: keyPath[0],\n    keyPath: keyPath,\n    labelRenderer: labelRenderer,\n    nodeType: nodeType,\n    styling: styling,\n    value: value,\n    valueRenderer: valueRenderer\n  };\n\n  var nestedNodeProps = (0, _extends3['default'])({}, rest, simpleNodeProps, {\n    data: value,\n    isCustomNode: isCustomNode\n  });\n\n  switch (nodeType) {\n    case 'Object':\n    case 'Error':\n    case 'WeakMap':\n    case 'WeakSet':\n      return _react2['default'].createElement(\n        _JSONObjectNode2['default'],\n        nestedNodeProps\n      );\n    case 'Array':\n      return _react2['default'].createElement(\n        _JSONArrayNode2['default'],\n        nestedNodeProps\n      );\n    case 'Iterable':\n    case 'Map':\n    case 'Set':\n      return _react2['default'].createElement(\n        _JSONIterableNode2['default'],\n        nestedNodeProps\n      );\n    case 'String':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter(raw) {\n            raw = '\"' + raw + '\"';\n            return raw.length > 1024\n              ? _react2['default'].createElement(_expandCollapse2['default'], {\n                  text: raw\n                })\n              : raw;\n          }\n        })\n      );\n    case 'Number':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        simpleNodeProps\n      );\n    case 'Boolean':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter(raw) {\n            return raw ? 'true' : 'false';\n          }\n        })\n      );\n    case 'Date':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter(raw) {\n            return raw.toISOString();\n          }\n        })\n      );\n    case 'Null':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter() {\n            return 'null';\n          }\n        })\n      );\n    case 'Undefined':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter() {\n            return 'undefined';\n          }\n        })\n      );\n    case 'Function':\n    case 'Symbol':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter(raw) {\n            return raw.toString();\n          }\n        })\n      );\n    case 'Custom':\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        simpleNodeProps\n      );\n    default:\n      return _react2['default'].createElement(\n        _JSONValueNode2['default'],\n        (0, _extends3['default'])({}, simpleNodeProps, {\n          valueGetter: function valueGetter(raw) {\n            return '<' + nodeType + '>';\n          }\n        })\n      );\n  }\n};\n\nexports['default'] = JSONNode;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONObjectNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');\n\nvar _objectWithoutProperties3 = _interopRequireDefault(\n  _objectWithoutProperties2\n);\n\nvar _getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names');\n\nvar _getOwnPropertyNames2 = _interopRequireDefault(_getOwnPropertyNames);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONNestedNode = require('./JSONNestedNode');\n\nvar _JSONNestedNode2 = _interopRequireDefault(_JSONNestedNode);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n// Returns the \"n Items\" string for this node,\n// generating and caching it if it hasn't been created yet.\nfunction createItemString(data) {\n  var len = (0, _getOwnPropertyNames2['default'])(data).length;\n  return len + ' ' + (len !== 1 ? 'keys' : 'key');\n}\n\n// Configures <JSONNestedNode> to render an Object\nvar JSONObjectNode = function JSONObjectNode(_ref) {\n  var data = _ref.data,\n    props = (0, _objectWithoutProperties3['default'])(_ref, ['data']);\n  return _react2['default'].createElement(\n    _JSONNestedNode2['default'],\n    (0, _extends3['default'])({}, props, {\n      data: data,\n      nodeType: 'Object',\n      nodeTypeIndicator: props.nodeType === 'Error' ? 'Error()' : '{}',\n      createItemString: createItemString,\n      expandable: (0, _getOwnPropertyNames2['default'])(data).length > 0\n    })\n  );\n};\n\nexports['default'] = JSONObjectNode;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/JSONValueNode.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _stringify = require('babel-runtime/core-js/json/stringify');\n\nvar _stringify2 = _interopRequireDefault(_stringify);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n/**\n * Renders simple values (eg. strings, numbers, booleans, etc)\n */\n\nvar JSONValueNode = function JSONValueNode(_ref) {\n  var nodeType = _ref.nodeType,\n    styling = _ref.styling,\n    labelRenderer = _ref.labelRenderer,\n    keyPath = _ref.keyPath,\n    valueRenderer = _ref.valueRenderer,\n    value = _ref.value,\n    valueGetter = _ref.valueGetter;\n  return _react2['default'].createElement(\n    'li',\n    styling('value', nodeType, keyPath),\n    _react2['default'].createElement(\n      'label',\n      (0, _extends3['default'])(\n        {},\n        styling(['label', 'valueLabel'], nodeType, keyPath),\n        {\n          'data-key-path': (0, _stringify2['default'])(keyPath)\n        }\n      ),\n      labelRenderer(keyPath, nodeType, false, false)\n    ),\n    _react2['default'].createElement(\n      'span',\n      styling('valueText', nodeType, keyPath),\n      valueRenderer.apply(\n        undefined,\n        [valueGetter(value), value].concat(keyPath)\n      )\n    )\n  );\n};\n\nJSONValueNode.defaultProps = {\n  valueGetter: function valueGetter(value) {\n    return value;\n  }\n};\n\nexports['default'] = JSONValueNode;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/createStylingFromTheme.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _reactBase16Styling = require('react-base16-styling');\n\nvar _solarized = require('./themes/solarized');\n\nvar _solarized2 = _interopRequireDefault(_solarized);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nvar colorMap = function colorMap(theme) {\n  return {\n    BACKGROUND_COLOR: theme.base00,\n    TEXT_COLOR: theme.base07,\n    STRING_COLOR: theme.base0B,\n    DATE_COLOR: theme.base0B,\n    NUMBER_COLOR: theme.base09,\n    BOOLEAN_COLOR: theme.base09,\n    NULL_COLOR: theme.base08,\n    UNDEFINED_COLOR: theme.base08,\n    FUNCTION_COLOR: theme.base08,\n    SYMBOL_COLOR: theme.base08,\n    LABEL_COLOR: theme.base0D,\n    ARROW_COLOR: theme.base0D,\n    ITEM_STRING_COLOR: theme.base0B,\n    ITEM_STRING_EXPANDED_COLOR: theme.base03\n  };\n};\n\nvar valueColorMap = function valueColorMap(colors) {\n  return {\n    String: colors.STRING_COLOR,\n    Date: colors.DATE_COLOR,\n    Number: colors.NUMBER_COLOR,\n    Boolean: colors.BOOLEAN_COLOR,\n    Null: colors.NULL_COLOR,\n    Undefined: colors.UNDEFINED_COLOR,\n    Function: colors.FUNCTION_COLOR,\n    Symbol: colors.SYMBOL_COLOR\n  };\n};\n\nvar getDefaultThemeStyling = function getDefaultThemeStyling(theme) {\n  var colors = colorMap(theme);\n\n  return {\n    tree: {\n      border: 0,\n      padding: 0,\n      marginTop: '0.5em',\n      marginBottom: '0.5em',\n      marginLeft: '0.125em',\n      marginRight: 0,\n      listStyle: 'none',\n      MozUserSelect: 'none',\n      WebkitUserSelect: 'none',\n      backgroundColor: colors.BACKGROUND_COLOR\n    },\n\n    value: function value(_ref, nodeType, keyPath) {\n      var style = _ref.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          paddingTop: '0.25em',\n          paddingRight: 0,\n          marginLeft: '0.875em',\n          WebkitUserSelect: 'text',\n          MozUserSelect: 'text',\n          wordWrap: 'break-word',\n          paddingLeft: keyPath.length > 1 ? '2.125em' : '1.25em',\n          textIndent: '-0.5em',\n          wordBreak: 'break-all'\n        })\n      };\n    },\n\n    label: {\n      display: 'inline-block',\n      color: colors.LABEL_COLOR\n    },\n\n    valueLabel: {\n      margin: '0 0.5em 0 0'\n    },\n\n    valueText: function valueText(_ref2, nodeType) {\n      var style = _ref2.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          color: valueColorMap(colors)[nodeType]\n        })\n      };\n    },\n\n    itemRange: function itemRange(styling, expanded) {\n      return {\n        style: {\n          paddingTop: expanded ? 0 : '0.25em',\n          cursor: 'pointer',\n          color: colors.LABEL_COLOR\n        }\n      };\n    },\n\n    arrow: function arrow(_ref3, nodeType, expanded) {\n      var style = _ref3.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          marginLeft: 0,\n          transition: '150ms',\n          WebkitTransition: '150ms',\n          MozTransition: '150ms',\n          WebkitTransform: expanded ? 'rotateZ(90deg)' : 'rotateZ(0deg)',\n          MozTransform: expanded ? 'rotateZ(90deg)' : 'rotateZ(0deg)',\n          transform: expanded ? 'rotateZ(90deg)' : 'rotateZ(0deg)',\n          transformOrigin: '45% 50%',\n          WebkitTransformOrigin: '45% 50%',\n          MozTransformOrigin: '45% 50%',\n          position: 'relative',\n          lineHeight: '1.1em',\n          fontSize: '0.75em'\n        })\n      };\n    },\n\n    arrowContainer: function arrowContainer(_ref4, arrowStyle) {\n      var style = _ref4.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          display: 'inline-block',\n          paddingRight: '0.5em',\n          paddingLeft: arrowStyle === 'double' ? '1em' : 0,\n          cursor: 'pointer'\n        })\n      };\n    },\n\n    arrowSign: {\n      color: colors.ARROW_COLOR\n    },\n\n    arrowSignInner: {\n      position: 'absolute',\n      top: 0,\n      left: '-0.4em'\n    },\n\n    nestedNode: function nestedNode(\n      _ref5,\n      keyPath,\n      nodeType,\n      expanded,\n      expandable\n    ) {\n      var style = _ref5.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          position: 'relative',\n          paddingTop: '0.25em',\n          marginLeft: keyPath.length > 1 ? '0.875em' : 0,\n          paddingLeft: !expandable ? '1.125em' : 0\n        })\n      };\n    },\n\n    rootNode: {\n      padding: 0,\n      margin: 0\n    },\n\n    nestedNodeLabel: function nestedNodeLabel(\n      _ref6,\n      keyPath,\n      nodeType,\n      expanded,\n      expandable\n    ) {\n      var style = _ref6.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          margin: 0,\n          padding: 0,\n          WebkitUserSelect: expandable ? 'inherit' : 'text',\n          MozUserSelect: expandable ? 'inherit' : 'text',\n          cursor: expandable ? 'pointer' : 'default'\n        })\n      };\n    },\n\n    nestedNodeItemString: function nestedNodeItemString(\n      _ref7,\n      keyPath,\n      nodeType,\n      expanded\n    ) {\n      var style = _ref7.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          paddingLeft: '0.5em',\n          cursor: 'default',\n          color: expanded\n            ? colors.ITEM_STRING_EXPANDED_COLOR\n            : colors.ITEM_STRING_COLOR\n        })\n      };\n    },\n\n    nestedNodeItemType: {\n      marginLeft: '0.3em',\n      marginRight: '0.3em'\n    },\n\n    nestedNodeChildren: function nestedNodeChildren(_ref8, nodeType, expanded) {\n      var style = _ref8.style;\n      return {\n        style: (0, _extends3['default'])({}, style, {\n          padding: 0,\n          margin: 0,\n          listStyle: 'none',\n          display: expanded ? 'block' : 'none'\n        })\n      };\n    },\n\n    rootNodeChildren: {\n      padding: 0,\n      margin: 0,\n      listStyle: 'none'\n    }\n  };\n};\n\nexports['default'] = (0, _reactBase16Styling.createStyling)(\n  getDefaultThemeStyling,\n  {\n    defaultBase16: _solarized2['default']\n  }\n);\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/getCollectionEntries.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _getIterator2 = require('babel-runtime/core-js/get-iterator');\n\nvar _getIterator3 = _interopRequireDefault(_getIterator2);\n\nvar _getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names');\n\nvar _getOwnPropertyNames2 = _interopRequireDefault(_getOwnPropertyNames);\n\nvar _keys = require('babel-runtime/core-js/object/keys');\n\nvar _keys2 = _interopRequireDefault(_keys);\n\nexports['default'] = getCollectionEntries;\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nfunction getLength(type, collection) {\n  if (type === 'Object') {\n    return (0, _keys2['default'])(collection).length;\n  } else if (type === 'Array') {\n    return collection.length;\n  }\n\n  return Infinity;\n}\n\nfunction isIterableMap(collection) {\n  return typeof collection.set === 'function';\n}\n\nfunction getEntries(type, collection, sortObjectKeys) {\n  var from =\n    arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;\n  var to =\n    arguments.length > 4 && arguments[4] !== undefined\n      ? arguments[4]\n      : Infinity;\n\n  var res = void 0;\n\n  if (type === 'Object') {\n    var keys = (0, _getOwnPropertyNames2['default'])(collection);\n\n    if (sortObjectKeys) {\n      keys.sort(sortObjectKeys === true ? undefined : sortObjectKeys);\n    }\n\n    keys = keys.slice(from, to + 1);\n\n    res = {\n      entries: keys.map(function (key) {\n        return { key: key, value: collection[key] };\n      })\n    };\n  } else if (type === 'Array') {\n    var oidx = collection._idx;\n    res = {\n      entries: collection.slice(from, to + 1).map(function (val, idx) {\n        idx += from;\n        var i = oidx && oidx[idx];\n        return { key: i >= 0 ? i : idx, value: val };\n      })\n    };\n  } else {\n    var idx = 0;\n    var entries = [];\n    var done = true;\n\n    var isMap = isIterableMap(collection);\n\n    for (\n      var _iterator = collection,\n        _isArray = Array.isArray(_iterator),\n        _i = 0,\n        _iterator = _isArray\n          ? _iterator\n          : (0, _getIterator3['default'])(_iterator);\n      ;\n\n    ) {\n      var _ref;\n\n      if (_isArray) {\n        if (_i >= _iterator.length) break;\n        _ref = _iterator[_i++];\n      } else {\n        _i = _iterator.next();\n        if (_i.done) break;\n        _ref = _i.value;\n      }\n\n      var item = _ref;\n\n      if (idx > to) {\n        done = false;\n        break;\n      }\n      if (from <= idx) {\n        if (isMap && Array.isArray(item)) {\n          if (typeof item[0] === 'string' || typeof item[0] === 'number') {\n            entries.push({ key: item[0], value: item[1] });\n          } else {\n            entries.push({\n              key: '[entry ' + idx + ']',\n              value: {\n                '[key]': item[0],\n                '[value]': item[1]\n              }\n            });\n          }\n        } else {\n          entries.push({ key: idx, value: item });\n        }\n      }\n      idx++;\n    }\n\n    res = {\n      hasMore: !done,\n      entries: entries\n    };\n  }\n\n  return res;\n}\n\nfunction getRanges(from, to, limit) {\n  var ranges = [];\n  while (to - from > limit * limit) {\n    limit = limit * limit;\n  }\n  for (var i = from; i <= to; i += limit) {\n    ranges.push({ from: i, to: Math.min(to, i + limit - 1) });\n  }\n\n  return ranges;\n}\n\nfunction getCollectionEntries(type, collection, sortObjectKeys, limit) {\n  var from =\n    arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;\n  var to =\n    arguments.length > 5 && arguments[5] !== undefined\n      ? arguments[5]\n      : Infinity;\n\n  var getEntriesBound = getEntries.bind(null, type, collection, sortObjectKeys);\n\n  if (!limit) {\n    return getEntriesBound().entries;\n  }\n\n  var isSubset = to < Infinity;\n  var length = Math.min(to - from, getLength(type, collection));\n\n  if (type !== 'Iterable') {\n    if (length <= limit || limit < 7) {\n      return getEntriesBound(from, to).entries;\n    }\n  } else {\n    if (length <= limit && !isSubset) {\n      return getEntriesBound(from, to).entries;\n    }\n  }\n\n  var limitedEntries = void 0;\n  if (type === 'Iterable') {\n    var _getEntriesBound = getEntriesBound(from, from + limit - 1),\n      hasMore = _getEntriesBound.hasMore,\n      entries = _getEntriesBound.entries;\n\n    limitedEntries = hasMore\n      ? [].concat(entries, getRanges(from + limit, from + 2 * limit - 1, limit))\n      : entries;\n  } else {\n    limitedEntries = isSubset\n      ? getRanges(from, to, limit)\n      : [].concat(\n          getEntriesBound(0, limit - 5).entries,\n          getRanges(limit - 4, length - 5, limit),\n          getEntriesBound(length - 4, length - 1).entries\n        );\n  }\n\n  return limitedEntries;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/index.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');\n\nvar _objectWithoutProperties3 = _interopRequireDefault(\n  _objectWithoutProperties2\n);\n\nvar _stringify = require('babel-runtime/core-js/json/stringify');\n\nvar _stringify2 = _interopRequireDefault(_stringify);\n\nvar _typeof2 = require('babel-runtime/helpers/typeof');\n\nvar _typeof3 = _interopRequireDefault(_typeof2);\n\nvar _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');\n\nvar _classCallCheck3 = _interopRequireDefault(_classCallCheck2);\n\nvar _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');\n\nvar _possibleConstructorReturn3 = _interopRequireDefault(\n  _possibleConstructorReturn2\n);\n\nvar _inherits2 = require('babel-runtime/helpers/inherits');\n\nvar _inherits3 = _interopRequireDefault(_inherits2);\n\nvar _extends2 = require('babel-runtime/helpers/extends');\n\nvar _extends3 = _interopRequireDefault(_extends2);\n\nvar _keys = require('babel-runtime/core-js/object/keys');\n\nvar _keys2 = _interopRequireDefault(_keys);\n\nvar _react = require('react');\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _JSONNode = require('./JSONNode');\n\nvar _JSONNode2 = _interopRequireDefault(_JSONNode);\n\nvar _createStylingFromTheme = require('./createStylingFromTheme');\n\nvar _createStylingFromTheme2 = _interopRequireDefault(_createStylingFromTheme);\n\nvar _reactBase16Styling = require('react-base16-styling');\n\nvar _contextMenu = require('../../context-menu');\n\nvar _contextMenu2 = _interopRequireDefault(_contextMenu);\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\n/* eslint-disable react/no-string-refs */\n// ES6 + inline style port of JSONViewer https://bitbucket.org/davevedder/react-json-viewer/\n// all credits and original code to the author\n// Dave Vedder <veddermatic@gmail.com> http://www.eskimospy.com/\n// port by Daniele Zannotti http://www.github.com/dzannotti <dzannotti@me.com>\n\nvar identity = function identity(value) {\n  return value;\n};\nvar expandRootNode = function expandRootNode(keyName, data, level) {\n  return level === 0;\n};\nvar defaultItemString = function defaultItemString(\n  type,\n  data,\n  itemType,\n  itemString\n) {\n  return _react2['default'].createElement(\n    'span',\n    null,\n    itemType,\n    ' ',\n    itemString\n  );\n};\nvar defaultLabelRenderer = function defaultLabelRenderer(_ref) {\n  var label = _ref[0];\n  return _react2['default'].createElement('span', null, label, ':');\n};\nvar noCustomNode = function noCustomNode() {\n  return false;\n};\n\nfunction checkLegacyTheming(theme, props) {\n  var deprecatedStylingMethodsMap = {\n    getArrowStyle: 'arrow',\n    getListStyle: 'nestedNodeChildren',\n    getItemStringStyle: 'nestedNodeItemString',\n    getLabelStyle: 'label',\n    getValueStyle: 'valueText'\n  };\n\n  var deprecatedStylingMethods = (0, _keys2['default'])(\n    deprecatedStylingMethodsMap\n  ).filter(function (name) {\n    return props[name];\n  });\n\n  if (deprecatedStylingMethods.length > 0) {\n    if (typeof theme === 'string') {\n      theme = {\n        extend: theme\n      };\n    } else {\n      theme = (0, _extends3['default'])({}, theme);\n    }\n\n    deprecatedStylingMethods.forEach(function (name) {\n      // eslint-disable-next-line no-console\n      console.error(\n        'Styling method \"' +\n          name +\n          '\" is deprecated, use \"theme\" property instead'\n      );\n\n      theme[deprecatedStylingMethodsMap[name]] = function (_ref2) {\n        for (\n          var _len = arguments.length,\n            args = Array(_len > 1 ? _len - 1 : 0),\n            _key = 1;\n          _key < _len;\n          _key++\n        ) {\n          args[_key - 1] = arguments[_key];\n        }\n\n        var style = _ref2.style;\n        return {\n          style: (0, _extends3['default'])(\n            {},\n            style,\n            props[name].apply(props, args)\n          )\n        };\n      };\n    });\n  }\n\n  return theme;\n}\n\nfunction getStateFromProps(props) {\n  var theme = checkLegacyTheming(props.theme, props);\n  if (props.invertTheme) {\n    if (typeof theme === 'string') {\n      theme = theme + ':inverted';\n    } else if (theme && theme.extend) {\n      if (typeof theme === 'string') {\n        theme = (0, _extends3['default'])({}, theme, {\n          extend: theme.extend + ':inverted'\n        });\n      } else {\n        theme = (0, _extends3['default'])({}, theme, {\n          extend: (0, _reactBase16Styling.invertTheme)(theme.extend)\n        });\n      }\n    } else if (theme) {\n      theme = (0, _reactBase16Styling.invertTheme)(theme);\n    }\n  }\n  return {\n    styling: (0, _createStylingFromTheme2['default'])(theme)\n  };\n}\n\nvar JSONTree = (function (_React$Component) {\n  (0, _inherits3['default'])(JSONTree, _React$Component);\n\n  function JSONTree(props) {\n    (0, _classCallCheck3['default'])(this, JSONTree);\n    var contextMenuList = [\n      { name: 'Copy' },\n      { name: 'Copy Key' },\n      { name: 'Copy Value' },\n      { name: 'Copy Object' },\n      { name: 'Collapse Parent' }\n    ];\n    var expandMenu;\n    var collapseMenu;\n    var searchMenu;\n    if (props.expandAll) {\n      expandMenu = {\n        name: 'Expand All',\n        onClick: function() {\n          props.expandAll();\n        }\n      };\n      contextMenuList.push(expandMenu);\n    }\n    if (props.collapseAll) {\n      collapseMenu = {\n        name: 'Collapse All',\n        onClick: function() {\n          props.collapseAll();\n        }\n      };\n      contextMenuList.push(collapseMenu);\n    }\n    if (props.onSearch) {\n      searchMenu = {\n        name: 'Search Object',\n        onClick: function () {\n          props.onSearch();\n        }\n      };\n      contextMenuList.push(searchMenu);\n    }\n\n    var inspectMenu = { name: 'Inspect Value' };\n    contextMenuList.push(inspectMenu);\n\n    var _this = (0, _possibleConstructorReturn3['default'])(\n      this,\n      _React$Component.call(this, props)\n    );\n\n    _this.onContextMenu = function (e) {\n      var target = (0, _contextMenu.$)(e.target);\n      var label = target.closest('li').find('>label[data-key-path]:first');\n      var keyPath = _contextMenu.util.parseJSON(label.attr('data-key-path'));\n      if (!Array.isArray(keyPath)) {\n        return;\n      }\n      var data = _this.props.data;\n      var keyPathLen = keyPath.length;\n      var showInspect = keyPathLen >= 2 || props.onSearch;\n      contextMenuList[3].copyText = data ? JSON.stringify(data, null, '  ') : '';\n      if (data) {\n        for (var i = keyPathLen - 2; i >= 0; i--) {\n          data = data && data[keyPath[i]];\n        }\n      }\n\n      var height = isRoot ? 90 : 120;\n      if (props.onSearch && !searchMenu) {\n        searchMenu = expandMenu;\n        expandMenu = null;\n      }\n      if (expandMenu) {\n        height += 30;\n      }\n      if (collapseMenu) {\n        height += 30;\n      }\n      if (searchMenu) {\n        height += 30;\n      }\n      var selectedText = _contextMenu.util.getSelectedText(e.clientX, e.clientY);\n      var json = data && showInspect && (typeof data === 'object' ? data : _contextMenu.util.parseJSON(data));\n      inspectMenu.hide = !json;\n      inspectMenu.onClick = json ? function() {\n        _contextMenu.util.showJSONDialog(json, [keyPath[0]]);\n      } : null;\n      height += json ? 30 : 0;\n      height += selectedText ? 30 : 0;\n      var isRoot = keyPathLen === 1;\n      var ctxMenu = _contextMenu.util.getMenuPosition(e, 110, height);\n      ctxMenu.className = 'w-inspectors-ctx-menu';\n      contextMenuList[0].copyText = selectedText;\n      contextMenuList[0].hide = !selectedText;\n      contextMenuList[1].copyText = keyPath[0];\n      if (\n        data &&\n        (typeof data === 'undefined'\n          ? 'undefined'\n          : (0, _typeof3['default'])(data)) === 'object' &&\n        !(data instanceof String)\n      ) {\n        try {\n          data = (0, _stringify2['default'])(data, null, '  ');\n        } catch (e) {} // eslint-disable-line\n      }\n      contextMenuList[2].copyText = data + '';\n      contextMenuList[4].onClick = function () {\n        label.closest('li').parent().closest('li').find('div:first').click();\n      };\n      contextMenuList[4].hide = isRoot;\n      var menus = contextMenuList;\n      if (!target.closest('label').length) {\n        menus = menus.map(_contextMenu.util.noop);\n        menus[1] = contextMenuList[2];\n        menus[2] = contextMenuList[1];\n      }\n      ctxMenu.list = menus;\n      _this.refs.contextMenu.show(ctxMenu); // eslint-disable-line\n      e.preventDefault();\n      e.stopPropagation();\n    };\n\n    _this.state = getStateFromProps(props);\n    return _this;\n  }\n\n  JSONTree.prototype.componentWillReceiveProps =\n    function componentWillReceiveProps(nextProps) {\n      var _this2 = this;\n\n      if (\n        ['theme', 'invertTheme'].find(function (k) {\n          return nextProps[k] !== _this2.props[k];\n        })\n      ) {\n        this.setState(getStateFromProps(nextProps));\n      }\n    };\n\n  JSONTree.prototype.shouldComponentUpdate = function shouldComponentUpdate(\n    nextProps\n  ) {\n    var _this3 = this;\n\n    return !!(0, _keys2['default'])(nextProps).find(function (k) {\n      return k === 'keyPath'\n        ? nextProps[k].join('/') !== _this3.props[k].join('/')\n        : nextProps[k] !== _this3.props[k];\n    });\n  };\n\n  JSONTree.prototype.render = function render() {\n    var _props = this.props,\n      value = _props.data,\n      keyPath = _props.keyPath,\n      postprocessValue = _props.postprocessValue,\n      hideRoot = _props.hideRoot,\n      theme = _props.theme,\n      _ = _props.invertTheme,\n      rest = (0, _objectWithoutProperties3['default'])(_props, [\n        'data',\n        'keyPath',\n        'postprocessValue',\n        'hideRoot',\n        'theme',\n        'invertTheme'\n      ]);\n    var styling = this.state.styling;\n\n    return _react2['default'].createElement(\n      'ul',\n      (0, _extends3['default'])({}, styling('tree'), {\n        onContextMenu: this.onContextMenu,\n        className: 'w-json-tree'\n      }),\n      _react2['default'].createElement(\n        _JSONNode2['default'],\n        (0, _extends3['default'])(\n          {},\n          (0, _extends3['default'])(\n            {\n              postprocessValue: postprocessValue,\n              hideRoot: hideRoot,\n              styling: styling\n            },\n            rest\n          ),\n          {\n            keyPath: hideRoot ? [] : keyPath,\n            value: postprocessValue(value)\n          }\n        )\n      ),\n      _react2['default'].createElement(_contextMenu2['default'], {\n        ref: 'contextMenu'\n      })\n    );\n  };\n\n  return JSONTree;\n})(_react2['default'].Component);\n\nJSONTree.defaultProps = {\n  shouldExpandNode: expandRootNode,\n  hideRoot: false,\n  keyPath: ['root'],\n  getItemString: defaultItemString,\n  labelRenderer: defaultLabelRenderer,\n  valueRenderer: identity,\n  postprocessValue: identity,\n  isCustomNode: noCustomNode,\n  collectionLimit: 50,\n  invertTheme: true\n};\nexports['default'] = JSONTree;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/objType.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nvar _iterator = require('babel-runtime/core-js/symbol/iterator');\n\nvar _iterator2 = _interopRequireDefault(_iterator);\n\nexports['default'] = objType;\n\nfunction _interopRequireDefault(obj) {\n  return obj && obj.__esModule ? obj : { default: obj };\n}\n\nfunction objType(obj) {\n  var type = Object.prototype.toString.call(obj).slice(8, -1);\n  if (type === 'Object' && typeof obj[_iterator2['default']] === 'function') {\n    return 'Iterable';\n  }\n\n  if (type === 'String' && obj._$isNumber) {\n    return 'BigNumber';\n  }\n\n  if (\n    type === 'Custom' &&\n    obj.constructor !== Object &&\n    obj instanceof Object\n  ) {\n    // For projects implementing objects overriding `.prototype[Symbol.toStringTag]`\n    return 'Object';\n  }\n\n  return type;\n}\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/themes/solarized.js",
    "content": "'use strict';\n\nexports.__esModule = true;\nexports['default'] = {\n  scheme: 'solarized',\n  author: 'ethan schoonover (http://ethanschoonover.com/solarized)',\n  base00: 'var(--c-j0)',\n  base01: 'var(--c-j1)',\n  base02: 'var(--c-j2)',\n  base03: 'var(--c-j3)',\n  base04: 'var(--c-j4)',\n  base05: 'var(--c-j5)',\n  base06: 'var(--c-j6)',\n  base07: 'var(--c-j7)',\n  base08: 'var(--c-j8)',\n  base09: 'var(--c-j9)',\n  base0A: 'var(--c-ja)',\n  base0B: 'var(--c-jb)',\n  base0C: 'var(--c-jc)',\n  base0D: 'var(--c-jd)',\n  base0E: 'var(--c-je)',\n  base0F: 'var(--c-jf)'\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/components/react-json-tree/utils/hexToRgb.js",
    "content": "'use strict';\n\nexports.__esModule = true;\n\nexports['default'] = function (hex) {\n  var result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n  return result\n    ? {\n        r: parseInt(result[1], 16),\n        g: parseInt(result[2], 16),\n        b: parseInt(result[3], 16)\n      }\n    : null;\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/composer-list.js",
    "content": "var React = require('react');\nvar Composer = require('./composer');\nvar util = require('./util');\nvar LazyInit = require('./lazy-init');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar TabMgr = require('./tab-mgr');\nvar storage = require('./storage');\nvar Icon = require('./icon');\n\nvar DEFAULT_TAB = ' ';\n\nvar ComposerList = React.createClass({\n  getInitialState: function () {\n    return { activeName: storage.get('activeComposerTab') || DEFAULT_TAB };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  componentDidMount: function () {\n    var self = this;\n    events.on('comTabsChange', function () {\n      self.setState({});\n    });\n    events.on('_setComposerData', function() {\n      self.showTab(DEFAULT_TAB);\n    });\n  },\n  showTab: function (name) {\n    if (this.state.activeName !== name) {\n      storage.set('activeComposerTab', name);\n      this.setState({ activeName: name });\n    }\n  },\n  render: function () {\n    var self = this;\n    var hide = self.props.hide;\n    var modal = self.props.modal;\n    var tabs = dataCenter.getComTabs();\n    var active = self.state.activeName;\n    var activeDefalut = active === DEFAULT_TAB;\n    var hasActive = activeDefalut;\n    var elem = tabs.map(function (tab) {\n      var pluginName = tab.plugin;\n      var activeTab;\n      if (active == tab.plugin) {\n        activeTab = true;\n        hasActive = true;\n      }\n      var icon = util.getTabIcon(tab);\n      return (\n        <button\n          key={pluginName}\n          onClick={function () {\n            self.showTab(pluginName);\n          }}\n          className={'w-custom-tab-btn btn btn-default' + (activeTab ? ' active' : '')}\n          title={pluginName}\n        >\n          {icon ? <img className=\"w-tab-icon\" src={icon} /> : null}\n          {tab.name}\n        </button>\n      );\n    });\n    if (!hasActive) {\n      activeDefalut = true;\n      active = self.state.activeName = DEFAULT_TAB;\n    }\n    return (\n      <div\n        className={\n          'fill v-box w-com-list' + (hide ? ' hide' : '')\n        }\n      >\n        {tabs.length ? (\n          <div className=\"box w-com-tab-list\">\n            <button\n              type=\"button\"\n              onClick={function () {\n                self.showTab(DEFAULT_TAB);\n              }}\n              className={'btn btn-default' + (activeDefalut ? ' active' : '')}\n            >\n              <Icon name=\"send\" />Default\n            </button>\n            <div className=\"fill w-custom-tabs\">{elem}</div>\n          </div>\n        ) : null}\n        <LazyInit inited={!hide}>\n          <Composer\n            modal={modal}\n            disabled={!activeDefalut}\n            hide={hide || !activeDefalut}\n          />\n        </LazyInit>\n        <TabMgr\n          modal={modal}\n          active={active}\n          hide={hide}\n          tabs={tabs}\n          className=\"w-custom-tab-panel\"\n        />\n      </div>\n    );\n  }\n});\n\nmodule.exports = ComposerList;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/composer.js",
    "content": "require('../css/composer.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar events = require('./events');\nvar storage = require('./storage');\nvar Divider = require('./divider');\nvar ResDetail = require('./res-detail');\nvar Properties = require('./properties');\nvar PropsEditor = require('./props-editor');\nvar message = require('./message');\nvar ContextMenu = require('./context-menu');\nvar CookiesDialog = require('./cookies-dialog');\nvar Dialog = require('./dialog');\nvar win = require('./win');\nvar HistoryData = require('./history-data');\nvar LazyInit = require('./lazy-init');\nvar Frames = require('./frames');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\nvar ViewInspector = require('./view-inspector');\n\nvar METHODS = [\n  'GET',\n  'POST',\n  'PUT',\n  'HEAD',\n  'TRACE',\n  'DELETE',\n  'SEARCH',\n  'QUERY',\n  'CONNECT',\n  'UPGRADE',\n  'WEBSOCKET',\n  'PROPFIND',\n  'PROPPATCH',\n  'MKCOL',\n  'COPY',\n  'MOVE',\n  'LOCK',\n  'UNLOCK',\n  'OPTIONS',\n  'PURGE',\n  'ACL',\n  'BIND',\n  'CHECKOUT',\n  'LINK',\n  'M-SEARCH',\n  'MERGE',\n  'MKACTIVITY',\n  'MKCALENDAR',\n  'NOTIFY',\n  'PATCH',\n  'PRI',\n  'REBIND',\n  'REPORT',\n  'SOURCE',\n  'SUBSCRIBE',\n  'UNBIND',\n  'UNLINK',\n  'UNSUBSCRIBE'\n];\nvar SEND_CTX_MENU = [\n  { name: 'Send Body Via File', action: 'file' },\n  { name: 'Replay Times' },\n  { name: 'Show History', action: 'history' },\n  {\n    name: 'Service',\n    list: [\n      { name: 'Create API Test', action: 'createApiTest' },\n      { name: 'Copy As Script', action: 'copyAsScript' },\n      { name: 'Share Via URL', action: 'Export' }\n    ]\n  }\n];\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_FILE_SIZE = 1024 * 1024 * 20;\nvar MAX_RES_SIZE = 1024 * 1024 * 3;\n\nvar TYPES = {\n  form: 'application/x-www-form-urlencoded',\n  upload: 'multipart/form-data',\n  text: 'text/plain',\n  json: 'application/json',\n  custom: ''\n};\nvar TIPS = 'Requests cannot bring rules in strict mode';\nvar TYPE_CONF_RE = /;.+$/;\nvar WS_RE = /^wss?:\\/\\//i;\nvar WS_CONNNECT_RE = /^\\s*connection\\s*:\\s*upgrade\\s*$/im;\nvar WS_UPGRADE_RE = /^\\s*upgrade\\s*:\\s*websocket\\s*$/im;\nvar REV_TYPES = {};\nvar MAX_HEADERS_SIZE = 1024 * 128;\nvar MAX_BODY_SIZE = 1024 * 256;\nvar MAX_UPLOAD_SIZE = MAX_BODY_SIZE * 2;\nvar MAX_COUNT = 64;\nvar MAX_REPEAT_TIMES = 100;\nvar RULES_HEADER = 'x-whistle-rule-value';\nObject.keys(TYPES).forEach(function (name) {\n  REV_TYPES[TYPES[name]] = name;\n});\n\nvar getTabClass = function (active) {\n  return active ? 'w-tab-btn w-active' : 'w-tab-btn';\n};\n\nfunction getString(str, len) {\n  if (typeof str !== 'string') {\n    return '';\n  }\n  len = len || MAX_BODY_SIZE;\n  return str.length > len ? str.substring(0, len) : str;\n}\n\nfunction hasReqBody(method, url, headers) {\n  if (method === 'CONNECT' || util.hasRequestBody(method) || WS_RE.test(url)) {\n    return true;\n  }\n  return headers && WS_CONNNECT_RE.test(headers) && WS_UPGRADE_RE.test(headers);\n}\n\nfunction hexToStr(str) {\n  str = util.getBase64FromHexText(str);\n  return util.base64Decode(str);\n}\n\nfunction strToHex(str) {\n  var base64 = util.toBase64(str);\n  return util.getHexText(util.getHexFromBase64(base64));\n}\n\nfunction getType(headers) {\n  var keys = Object.keys(headers);\n  var type;\n  for (var i = 0, len = keys.length; i < len; i++) {\n    var name = keys[i];\n    if (name.toLowerCase() === 'content-type') {\n      if (type) {\n        return 'custom';\n      }\n      var value = headers[name];\n      if (!value || typeof value !== 'string') {\n        return 'custom';\n      }\n      value = value.split(';')[0].trim().toLowerCase();\n      type = REV_TYPES[value] || 'custom';\n    }\n  }\n  return type || 'custom';\n}\n\nfunction isUpload(headers) {\n  return headers === 'upload' || getType(headers) === 'upload';\n}\n\nfunction escapeRules(rules) {\n  rules = rules.join('\\n');\n  return rules && encodeURIComponent(rules);\n}\n\nfunction getUploadType(type, boundary) {\n  if (type) {\n    type = (type + '').replace(/;?\\s*boundary=.*$/, '');\n  }\n  return (type || 'multipart/form-data') + '; boundary=' + boundary;\n}\n\nfunction getStatus(statusCode) {\n  if (statusCode == 403) {\n    return 'forbidden';\n  }\n  if (statusCode && (!/^\\d+$/.test(statusCode) || statusCode >= 400)) {\n    return 'error';\n  }\n  return '';\n}\n\nfunction parseValue(value) {\n  if (value && typeof value === 'object') {\n    value.data = util.base64ToByteArray(value.base64) || util.EMPTY_BUF;\n    delete value.base64;\n  }\n}\n\nfunction replaceCRLF(body) {\n  return body && body.replace(/\\r\\n|\\r|\\n/g, '\\r\\n');\n}\n\nfunction getComposerTime(composerTime) {\n  if (!composerTime || !composerTime.endTime) {\n    return;\n  }\n  var time = composerTime.endTime - composerTime.startTime;\n  return time >= 1000 ? (time / 1000) + 's' : time + 'ms';\n}\n\nvar Composer = React.createClass({\n  getInitialState: function () {\n    var rules = storage.get('composerRules');\n    var data = util.parseJSON(storage.get('composerData')) || {};\n    var showPretty = storage.get('showPretty') == '1';\n    var useH2 = storage.get('useH2InComposer') == '1';\n    var disableComposerRules = storage.get('disableComposerRules') == '1';\n    var method = data.method;\n    var body = getString(data.body);\n    if (body && body !== data.body) {\n      message.warn('Body content limited to 256KB (excess will be truncated)');\n    }\n    var headers = util.parseHeaders(data.headers);\n    var type = getType(headers);\n    if (data.base64 && isUpload(type)) {\n      this.uploadBodyData = data.base64 && this.parseUploadModal({ headers: headers, base64: data.base64 });\n    } else {\n      var uploadBodyData = util.parseRawJson(storage.get('composerUploadBody'), true);\n      if (uploadBodyData) {\n        Object.keys(uploadBodyData).forEach(function(name) {\n          var value = uploadBodyData[name];\n          if (Array.isArray(value)) {\n            value.forEach(parseValue);\n          } else {\n            parseValue(value);\n          }\n        });\n        this.uploadBodyData = uploadBodyData;\n      }\n    }\n    return {\n      loading: true,\n      repeatTimes: 1,\n      historyData: [],\n      disableBody: !!storage.get('disableComposerBody'),\n      enableProxyRules: storage.get('composerProxyRules') !== '',\n      url: data.url,\n      method: METHODS.indexOf(method) === -1 ? 'GET' : method,\n      headers: getString(data.headers, MAX_HEADERS_SIZE),\n      body: body,\n      tabName: 'Request',\n      showPretty: showPretty,\n      useH2: useH2,\n      rules: typeof rules === 'string' ? rules : '',\n      type: type,\n      disableComposerRules: disableComposerRules,\n      isHexText: !!storage.get('showHexTextBody'),\n      isCRLF: !!storage.get('useCRLBody')\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    self.update(self.props.modal);\n    this.refs.uploadBody.update(this.uploadBodyData);\n    this.hintElem = $(findDOMNode(this.refs.hints));\n    dataCenter.onTakeTimeChange = function(time) {\n      self.setState({ composerTime: time });\n    };\n    events.on('_setComposerData', function(_, data) {\n      if (data) {\n        events.trigger('showComposerTab');\n        self.onCompose(data);\n      }\n    });\n    events.on('setComposer', function () {\n      if (self.state.pending || self.props.disabled) {\n        return;\n      }\n      var activeItem = self.props.modal;\n      if (!activeItem) {\n        return;\n      }\n      self.setState({ reqData: activeItem });\n      activeItem.frames && dataCenter.setComposerItem(activeItem);\n      var body = util.getBody(activeItem.req);\n      var updateComposer = function () {\n        var headers = activeItem.req.headers;\n        if (activeItem.h2Id) {\n          headers = $.extend({}, headers);\n          headers['x-whistle-alpn-protocol'] = activeItem.h2Id;\n          activeItem = $.extend({}, activeItem);\n          activeItem.req = $.extend({}, activeItem.req);\n          activeItem.req.headers = headers;\n        }\n        var state = {\n          useH2: activeItem.useH2,\n          url: activeItem.url,\n          headers: headers,\n          result: activeItem,\n          type: getType(headers),\n          method: activeItem.req.method,\n          tabName: 'Request'\n        };\n        var body = util.getBody(activeItem.req);\n        if (body) {\n          state.disableBody = false;\n          if (body.indexOf('\\n') !== -1) {\n            state.isCRLF = body.indexOf('\\r\\n') !== -1;\n          }\n        }\n        storage.set('useCRLBody', state.isCRLF ? 1 : '');\n        self.setState(state, function () {\n          self.update(activeItem);\n          self.onComposerChange();\n        });\n        storage.set('useH2InComposer', activeItem.useH2 ? 1 : '');\n      };\n      if (body.length > MAX_BODY_SIZE) {\n        win.confirm(\n          'Request body exceeds limit and will be truncated. Continue?',\n          function (allow) {\n            if (allow) {\n              updateComposer();\n            }\n          }\n        );\n      } else {\n        updateComposer();\n      }\n    });\n    events.on('showFramesInComposer', function() {\n      self.setState({ tabName: 'Frames' });\n    });\n    events.on('updateStrictMode', function () {\n      self.setState({});\n    });\n    self.updatePrettyData();\n    $(document).on('click mousedown', function(e) {\n      var target = $(e.target);\n      if (!(target.closest('.w-com-params').length ||\n        target.closest('.w-com-params-editor').length ||\n        target.closest('.w-com-dialog').length ||\n        target.closest('.w-win-dialog').length ||\n        target.closest('.w-ctx-menu').length)) {\n        self.hideParams();\n      }\n      if (!(target.closest('.w-com-history-data').length ||\n        target.closest('.w-keep-history-data').length ||\n        target.closest('.w-replay-count-dialog').length ||\n        target.closest('.w-com-history-btn').length ||\n        target.closest('.w-copy-text-with-tips').length ||\n        target.closest('.w-ie-dialog').length ||\n        target.closest('.w-service-dialog').length)) {\n        self.hideHistory();\n      }\n    });\n    events.trigger('composerDidMount');\n  },\n  repeatTimesChange: function (e) {\n    var count = e.target.value.replace(/^\\s*0*|[^\\d]+/, '');\n    var repeatTimes = count.slice(0, 3);\n    if (repeatTimes > MAX_REPEAT_TIMES) {\n      repeatTimes = MAX_REPEAT_TIMES;\n    }\n    this.setState({ repeatTimes: repeatTimes });\n  },\n  repeatRequest: function(e) {\n    if (e && e.type !== 'click' && e.keyCode !== 13) {\n      return;\n    }\n    this.refs.setRepeatTimes.hide();\n    if (this._isReplay) {\n      this.onReplay(this.state.repeatTimes);\n    } else {\n      this.execute(null, this.state.repeatTimes);\n    }\n  },\n  loadHistory: function () {\n    var self = this;\n    if (self.state.loading === 2) {\n      return;\n    }\n    self.state.loading = 2;\n    dataCenter.getHistory(function (data) {\n      if (Array.isArray(data)) {\n        self.setState({\n          loading: 0,\n          historyData: self.formatHistory(data)\n        });\n        return;\n      }\n      setTimeout(this.loadHistory, 6000);\n    });\n  },\n  getMethod: function () {\n    var curMethod = this.state.method || 'GET';\n    var method = findDOMNode(this.refs.method).value || curMethod;\n    return method === '+ Custom' ? method : curMethod;\n  },\n  updatePrettyData: function () {\n    if (!this.state.showPretty) {\n      return;\n    }\n    var headers = findDOMNode(this.refs.headers).value;\n    var prettyHeaders = util.parseHeaders(headers);\n    this.refs.prettyHeaders.update(prettyHeaders);\n    var body = findDOMNode(this.refs.body).value;\n    if (body && this.state.isHexText) {\n      body = hexToStr(body);\n    }\n    body = util.parseQueryString(body, null, null, decodeURIComponent);\n    this.refs.prettyBody.update(body);\n  },\n  update: function (item) {\n    if (!item) {\n      return;\n    }\n    var rulesHeaders = item.rulesHeaders;\n    var rules = rulesHeaders && rulesHeaders[RULES_HEADER];\n    if (rules) {\n      rulesHeaders = $.extend({}, rulesHeaders);\n      delete rulesHeaders[RULES_HEADER];\n      this.updateRules(rules);\n    }\n    var refs = this.refs;\n    var req = item.req;\n    findDOMNode(refs.url).value = item.url;\n    findDOMNode(refs.method).value = req.method;\n    findDOMNode(refs.headers).value = util.getOriginalReqHeaders(item, rulesHeaders);\n    var bodyElem = findDOMNode(refs.body);\n    if (req.method === 'GET') {\n      bodyElem.value = '';\n    } else {\n      var body = this.state.isHexText\n        ? util.getHexText(util.getHex(req))\n        : util.getBody(req);\n      var value = getString(body);\n      bodyElem.value = value;\n    }\n    this.updatePrettyData();\n    this.updateUploadForm(req);\n  },\n  parseUploadModal: function(req) {\n    var fields = util.parseUploadBody(req);\n    var uploadModal = {};\n    fields &&\n      fields.forEach(function (field) {\n        var name = field.name;\n        var list = uploadModal[name];\n        if (list) {\n          list.push(field);\n        } else {\n          uploadModal[name] = [field];\n        }\n      });\n    return uploadModal;\n  },\n  updateUploadForm: function(req) {\n    if (!isUpload(req.headers)) {\n      return false;\n    }\n    this.refs.uploadBody.update(this.parseUploadModal(req));\n    return true;\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  getComposerData: function() {\n    var refs = this.refs;\n    var method = this.getMethod();\n    var url = findDOMNode(this.refs.url).value.trim();\n    var headers = findDOMNode(this.refs.headers).value;\n\n    return {\n      url: url,\n      headers: headers,\n      method: method,\n      useH2: this.state.useH2 ? 1 : '',\n      body: replaceCRLF(findDOMNode(refs.body).value)\n    };\n  },\n  saveComposer: function () {\n    var data = this.getComposerData();\n    this.state.url = data.url;\n    this.state.headers = data.headers;\n    storage.set('composerData', JSON.stringify(data));\n    if (this.hasBody != hasReqBody(data.method, data.url, data.headers)) {\n      this.setState({});\n    }\n    return data;\n  },\n  addHistory: function (params) {\n    var self = this;\n    var historyData = self.state.historyData;\n    params.date = Date.now();\n    for (var i = 0, len = historyData.length; i < len; i++) {\n      var item = historyData[i];\n      if (\n        item.url === params.url &&\n        item.method === params.method &&\n        item.headers === params.headers &&\n        item.body === params.body\n      ) {\n        params.selected = item.selected;\n        historyData.splice(i, 1);\n        break;\n      }\n    }\n    historyData.unshift(params);\n    var overflow = historyData.length - MAX_COUNT;\n    if (overflow > 0) {\n      historyData.splice(MAX_COUNT, overflow);\n    }\n    self.setState({ historyData: self.formatHistory(historyData) });\n  },\n  formatHistory: function (data) {\n    var result = [];\n    var histroyUrls = [];\n    var groupList = [];\n    var map = {};\n    var hasSelected;\n    data.forEach(function (item) {\n      if (!item.url || typeof item.url !== 'string') {\n        return;\n      }\n      if (histroyUrls.indexOf(item.url) === -1) {\n        histroyUrls.push(item.url);\n      }\n      var opts = util.parseUrl(item.url);\n      var host = opts ? opts.host : '';\n      var group = map[host];\n      if (!group) {\n        group = { title: host, list: [] };\n        groupList.push(group);\n        map[host] = group;\n      }\n      if (item.selected) {\n        if (hasSelected) {\n          item.selected = false;\n        } else {\n          hasSelected = true;\n        }\n      }\n      group.list.push(item);\n      item.path = opts ? opts.path : item.url;\n      item.protocol = opts ? opts.protocol.slice(0, -1) : 'HTTP';\n      item.protocol = /^([\\w.-]+):\\/\\//i.test(item.url) ? RegExp.$1.toUpperCase() : 'HTTP';\n      item.body = item.body || '';\n      result.push(item);\n    });\n    if (!hasSelected && result[0]) {\n      result[0].selected = true;\n    }\n    result._groupList = groupList.reduce(function(list, group) {\n      list.push({ title: group.title});\n      group.list.forEach(function(item) {\n        list.push(item);\n      });\n      return list;\n    }, []);\n    this._histroyUrls = histroyUrls;\n    if (this.state.showHints) {\n      this.showHints();\n    }\n    return result;\n  },\n  onHexTextChange: function (e) {\n    var isHexText = e.target.checked;\n    storage.set('showHexTextBody', isHexText ? 1 : '');\n    var elem = findDOMNode(this.refs.body);\n    var body = elem.value;\n    if (body.trim()) {\n      if (isHexText) {\n        var isCRLF = this.state.isCRLF;\n        if (this._preBody === body && (!this._isCRLF === !isCRLF)) {\n          elem.value = this._preHex;\n        } else {\n          elem.value = strToHex(isCRLF ? replaceCRLF(body) : body);\n        }\n      } else {\n        this._preBody = hexToStr(body);\n        this._isCRLF = this.state.isCRLF;\n        this._preHex = body;\n        elem.value = this._preBody;\n      }\n    }\n    this.setState({ isHexText: isHexText });\n    this.saveComposer();\n  },\n  onCRLFChange: function (e) {\n    var isCRLF = e.target.checked;\n    storage.set('useCRLBody', isCRLF ? 1 : '');\n    this.setState({ isCRLF: isCRLF });\n  },\n  updateRules: function(rules) {\n    if (Array.isArray(rules)) {\n      rules = rules.join('\\n');\n    }\n    if (rules && typeof rules === 'string') {\n      rules = util.decodeURIComponentSafe(rules);\n      findDOMNode(this.refs.composerRules).value = rules;\n      this.setState({ rules: rules });\n      this.onRulesChange();\n      this.setRulesDisable(false);\n    }\n  },\n  onCompose: function (item) {\n    if (!item) {\n      return;\n    }\n    this.state.tabName = 'Request';\n    this.result = null;\n    var refs = this.refs;\n    var isHexText = !!item.isHexText;\n    var headers = item.headers;\n    var rules = [];\n    if (util.notEStr(item.rules)) {\n      rules.push(item.rules);\n    }\n    var req = { headers: {}, base64: item.base64 };\n    this.handleFrames();\n    if (util.notEStr(headers)) {\n      headers = headers.trim().split(/[\\r\\n]+/).filter(function(line) {\n        line = line.trim();\n        var index = line.indexOf(':');\n        var key = line;\n        var value;\n        if (index !== -1) {\n          key = line.substring(0, index);\n          value = line.substring(index + 1).trim();\n        }\n        key = key.toLowerCase();\n        // 忽略重名的字段\n        req.headers[key] = value;\n        if (key === RULES_HEADER) {\n          value && rules.push(value);\n          return false;\n        }\n        return true;\n      }).join('\\r\\n');\n    }\n    if (rules.length) {\n      this.updateRules(rules.join('\\n'));\n    }\n    if (util.isString(item.url)) {\n      findDOMNode(refs.url).value = item.url;\n      this.state.url = item.url;\n    }\n    if (util.isString(item.method)) {\n      findDOMNode(refs.method).value = item.method;\n      this.state.method = item.method;\n    }\n    if (util.isString(headers)) {\n      findDOMNode(refs.headers).value = headers;\n      this.state.headers = headers;\n    }\n    if (!isHexText && !item.body && item.base64) {\n      isHexText = true;\n    }\n    var body = isHexText && item.base64\n      ? util.getHexText(util.getHexFromBase64(item.base64))\n      : util.getText(item.body) || '';\n    this.state.tabName = 'Request';\n    this.state.result = '';\n    this.state.isHexText = isHexText;\n    this.state.useH2 = item.useH2;\n    if (item.disableBody != null) {\n      this.state.disableBody = !!item.disableBody;\n    } else if (body) {\n      this.state.disableBody = false;\n    }\n    if (item.isCRLF != null) {\n      this.state.isCRLF = !!item.isCRLF;\n      storage.set('useCRLBody', item.isCRLF ? 1 : '');\n    }\n    if (item.disableComposerRules != null) {\n      this.setRulesDisable(item.disableComposerRules);\n    }\n    if (item.enableProxyRules != null) {\n      this.state.enableProxyRules = !!item.enableProxyRules;\n      storage.set('composerProxyRules', item.enableProxyRules ? 1 : '');\n    }\n    if (!this.updateUploadForm(req)) {\n      findDOMNode(refs.body).value = body;\n    }\n    this.onComposerChange(true);\n    storage.set('disableComposerBody', this.state.disableBody ? 1 : '');\n    storage.set('useH2InComposer', item.useH2 ? 1 : '');\n    storage.set('showHexTextBody', isHexText ? 1 : '');\n  },\n  onReplay: function (times) {\n    if (this._selectedItem) {\n      this.sendRequest($.extend({}, this._selectedItem, { repeatCount: times || 1, needResponse: false }));\n    }\n  },\n  handleUrlKeyUp: function(e) {\n    if (e.keyCode === 27) {\n      this.hideHints();\n    }\n  },\n  onUrlChange: function(e) {\n    this.onComposerChange(e);\n    clearTimeout(this._urlTimer);\n    this._urlTimer = setTimeout(this.showHints, 300);\n  },\n  onComposerChange: function (e) {\n    var self = this;\n    clearTimeout(self.composerTimer);\n    self.composerTimer = setTimeout(self.saveComposer, 1000);\n    var target = e === true ? e : e && e.target;\n    if (target) {\n      if (target === true || target.nodeName === 'SELECT') {\n        var method = findDOMNode(self.refs.method).value;\n        self.setState({ method: method }, self.updatePrettyData);\n      }\n      if (target === true || target.name === 'headers') {\n        clearTimeout(self.typeTimer);\n        self.typeTimer = setTimeout(function () {\n          var headers = findDOMNode(self.refs.headers).value;\n          self.setState({\n            type: getType(util.parseHeaders(headers))\n          });\n        }, 1000);\n      }\n    }\n  },\n  onTypeChange: function (e) {\n    var target = e.target;\n    if (target.nodeName !== 'INPUT') {\n      return;\n    }\n    var type = target.getAttribute('data-type');\n    if (type) {\n      this.setState({ type: type });\n      type = TYPES[type];\n      var elem = findDOMNode(this.refs.headers);\n      var headers = util.parseHeaders(elem.value);\n      Object.keys(headers).forEach(function (name) {\n        if (name.toLowerCase() === 'content-type') {\n          if (type) {\n            var addon = TYPE_CONF_RE.test(headers[name]) ? RegExp['$&'] : '';\n            headers[name] = type + addon;\n            type = null;\n          } else {\n            delete headers[name];\n          }\n        }\n      });\n      if (type) {\n        headers['Content-Type'] = type;\n      }\n      elem.value = util.objectToString(headers);\n      this.updatePrettyData();\n      this.saveComposer();\n    }\n  },\n  addHeader: function () {\n    this.refs.prettyHeaders.onAdd();\n  },\n  addField: function () {\n    this.refs.prettyBody.onAdd();\n  },\n  addUploadFiled: function () {\n    this.refs.uploadBody.onAdd();\n  },\n  onHeaderChange: function (key, newKey) {\n    var refs = this.refs;\n    var headers = refs.prettyHeaders.toString();\n    findDOMNode(refs.headers).value = headers;\n    this.saveComposer();\n    if (\n      key.toLowerCase() === 'content-type' ||\n      (newKey && newKey.toLowerCase() === 'content-type')\n    ) {\n      this.setState({\n        type: getType(util.parseHeaders(headers))\n      });\n    }\n  },\n  onFieldChange: function () {\n    var refs = this.refs;\n    var body = refs.prettyBody.toString();\n    if (body && this.state.isHexText) {\n      body = strToHex(body);\n    }\n    findDOMNode(refs.body).value = body;\n    this.saveComposer();\n  },\n  updateUploadData: function () {\n    var fields = this.refs.uploadBody.getFields();\n    var result = {};\n    var maxLen = MAX_UPLOAD_SIZE;\n    var getValue = function(field) {\n      if (!field.data || field.data.length > maxLen) {\n        return field.value;\n      }\n      maxLen -= field.data.length;\n      return {\n        value: field.value,\n        type: field.type,\n        base64: util.bytesToBase64(field.data)\n      };\n    };\n    fields.forEach(function (field) {\n      var value = result[field.name];\n      if (Array.isArray(value)) {\n        value.push(getValue(field));\n      } else {\n        result[field.name] = value == null ? getValue(field) : [value, getValue(field)];\n      }\n    });\n    storage.set('composerUploadBody', JSON.stringify(result));\n  },\n  onProxyRules: function (e) {\n    var enable = e.target.checked;\n    storage.set('composerProxyRules', enable ? 1 : '');\n    this.setState({ enableProxyRules: enable });\n  },\n  onShowPretty: function (e) {\n    var show = e.target.checked;\n    storage.set('showPretty', show ? 1 : 0);\n    this.setState({ showPretty: show }, this.updatePrettyData);\n  },\n  toggleH2: function (e) {\n    var self = this;\n    if (!dataCenter.supportH2) {\n      win.confirm(\n        'HTTP/2 requires Node.js LTS version v16+. Please upgrade',\n        function (sure) {\n          sure && window.open('https://nodejs.org/');\n          self.setState({});\n        }\n      );\n      return;\n    }\n    var useH2 = e.target.checked;\n    storage.set('useH2InComposer', useH2 ? 1 : '');\n    self.setState({ useH2: useH2 });\n  },\n  hideHistory: function() {\n    if (this.state.showHistory) {\n      this.setState({ showHistory: false });\n    }\n  },\n  toggleHistory: function () {\n    var showHistory = !this.state.showHistory;\n    this.setState({ showHistory: showHistory });\n    showHistory && this.loadHistory();\n    this.hideHints();\n  },\n  setRulesDisable: function (disableComposerRules) {\n    storage.set('disableComposerRules', disableComposerRules ? 1 : 0);\n    this.setState({ disableComposerRules: disableComposerRules });\n  },\n  onDisableChange: function (e) {\n    this.setRulesDisable(!e.target.checked);\n  },\n  enableRules: function () {\n    if (this.state.disableComposerRules) {\n      this.setRulesDisable(false);\n    }\n  },\n  handeHistoryReplay: function(item, repeatTimes) {\n    this._selectedItem = item;\n    if (repeatTimes) {\n      this.showRepeatTimes(true);\n    } else {\n      this.onReplay();\n    }\n  },\n  handleHistoryEdit: function(item) {\n    this.onCompose(item);\n    this.hideHistory();\n  },\n  showRepeatTimes: function(isReplay) {\n    var self = this;\n    self.refs.setRepeatTimes.show();\n    self._isReplay = isReplay;\n    findDOMNode(self.refs.repeatBtn).innerHTML = isReplay ? 'Replay' : 'Send';\n    setTimeout(function () {\n      var input = findDOMNode(self.refs.repeatTimes);\n      input.select();\n      input.focus();\n    }, 300);\n  },\n  execute: function (e, times) {\n    times = times > 0 ? Math.min(MAX_REPEAT_TIMES, times) : undefined;\n    if (e && !times && e.target.nodeName === 'INPUT' && e.keyCode !== 13) {\n      return;\n    }\n    if (e && e.shiftKey) {\n      return this.showRepeatTimes();\n    }\n    var refs = this.refs;\n    var url = findDOMNode(refs.url).value.trim();\n    if (!url || this.state.pending) {\n      return;\n    }\n    this.onComposerChange();\n    this.setState({ tabName: 'Request' });\n    var disableComposerRules =\n      dataCenter.isStrictMode() || this.state.disableComposerRules;\n    var rules = disableComposerRules ? null : this.state.rules;\n    var headersStr = findDOMNode(refs.headers).value;\n    var headers = headersStr;\n    if (typeof rules === 'string' && (rules = rules.trim())) {\n      var obj = util.parseJSON(headers);\n      var result = [];\n      var customRules;\n      rules = [rules];\n      if (obj) {\n        Object.keys(obj).forEach(function (key) {\n          if (key.toLowerCase() === RULES_HEADER) {\n            var value = obj[key];\n            try {\n              value =\n                typeof value === 'string' ? decodeURIComponent(value) : '';\n            } catch (e) {}\n            value && rules.push(value);\n            delete obj[key];\n          }\n        });\n        customRules = escapeRules(rules);\n        if (customRules) {\n          obj[RULES_HEADER] = customRules;\n        }\n        headers = JSON.stringify(obj);\n      } else {\n        headers.split(/\\r\\n|\\r|\\n/).forEach(function (line) {\n          var index = line.indexOf(': ');\n          if (index === -1) {\n            index = line.indexOf(':');\n          }\n          var key = index === -1 ? line : line.substring(0, index);\n          key = key.toLowerCase();\n          if (key === RULES_HEADER) {\n            var value = line.substring(index + 1).trim();\n            try {\n              value = decodeURIComponent(value);\n            } catch (e) {}\n            rules.push(value);\n          } else {\n            result.push(line);\n          }\n        });\n        customRules = escapeRules(rules);\n        if (customRules) {\n          result.push(RULES_HEADER + ': ' + customRules);\n        }\n        headers = result.join('\\n');\n      }\n    }\n    var self = this;\n    var method = self.getMethod();\n    var body, base64, isHexText;\n    if (self.localFileBase64 != null) {\n      if (hasReqBody(method, url, headersStr)) {\n        base64 = self.localFileBase64;\n      }\n      self.localFileBase64 = null;\n    } else if (!self.state.disableBody && hasReqBody(method, url, headersStr)) {\n      if (self.state.type === 'upload') {\n        var uploadData = util.getMultiBody(this.refs.uploadBody.getFields());\n        var boundary = uploadData.boundary;\n        var ctnLen = uploadData.length;\n        base64 = uploadData.base64;\n        var obj2 = util.parseJSON(headers);\n        var type;\n        if (obj2) {\n          Object.keys(obj2).forEach(function (key) {\n            key = key.toLowerCase();\n            if (key === 'content-type') {\n              type = type || obj2[key];\n              delete obj2[key];\n            } else if (key === 'content-length') {\n              delete obj2[key];\n            }\n          });\n          obj2['Content-Type'] = getUploadType(type, boundary);\n          obj2['Content-Length'] = ctnLen;\n          headers = JSON.stringify(obj2);\n        } else {\n          var list = [];\n          headers.split(/\\r\\n|\\r|\\n/).forEach(function (line) {\n            var index = line.indexOf(': ');\n            if (index === -1) {\n              index = line.indexOf(':');\n            }\n            var key = index === -1 ? line : line.substring(0, index);\n            key = key.toLowerCase();\n            if (key === 'content-type') {\n              type = type || line.substring(index + 1).trim();\n            } else if (key !== 'content-length') {\n              list.push(line);\n            }\n          });\n          list.push('Content-Type: ' + getUploadType(type, boundary));\n          list.push('Content-Length: ' + ctnLen);\n          headers = list.join('\\n');\n        }\n      } else {\n        body = findDOMNode(refs.body).value;\n        isHexText = this.state.isHexText;\n        if (isHexText) {\n          base64 = util.getBase64FromHexText(body);\n          body = undefined;\n        } else if (body && this.state.isCRLF) {\n          body = replaceCRLF(body);\n        }\n      }\n    }\n    this.sendRequest({\n      useH2: this.state.useH2 ? 1 : '',\n      needResponse: true,\n      url: url.replace(/^\\/\\//, ''),\n      headers: headers,\n      method: method,\n      body: body,\n      base64: base64,\n      repeatCount: times,\n      isHexText: isHexText,\n      enableProxyRules: this.state.enableProxyRules\n    });\n  },\n  handleBody: function(res) {\n    var self = this;\n    var reqId = res && res.reqId;\n    var preReqId = self._curDataId;\n    preReqId && dataCenter.offComposeData(preReqId);\n    self._curDataId = reqId;\n    if (!reqId) {\n      return;\n    }\n    var body;\n    dataCenter.onComposeData(reqId, function(base64) {\n      if (!base64) {\n        return;\n      }\n      var result = self.state.result;\n      var res = result && result.res;\n      if (res) {\n        body = util.joinBase64(body, base64);\n        var data = {};\n        Object.keys(res).forEach(function(key) {\n          data[key] = res[key];\n        });\n        data.base64 = body;\n        result.res = data;\n        self.setState({});\n      }\n      if (body.length > MAX_RES_SIZE) {\n        dataCenter.offComposeData(reqId);\n      }\n    });\n  },\n  handleFrames: function(res) {\n    var headers = res && res.headers;\n    var id = headers && headers['x-whistle-req-id'];\n    var isFrames = headers && headers['x-whistle-frames-mode'] === '1';\n    dataCenter.curComposerReqId = id;\n    if (!id || !isFrames) {\n      dataCenter.setComposerItem();\n      this.setState({ reqData: null });\n      return;\n    }\n    var reqData = {\n      id: id,\n      frames: []\n    };\n    dataCenter.setComposerItem(reqData);\n    this.setState({ reqData: reqData });\n    return true;\n  },\n  sendRequest: function(params) {\n    var self = this;\n    var index = (self._reqIndex || 0) + 1;\n    self._reqIndex = index;\n    if (params.needResponse) {\n      clearTimeout(self.comTimer);\n      self.comTimer = setTimeout(function () {\n        self.setState({ pending: false });\n      }, 5000);\n    }\n    events.trigger('enableRecord');\n    self.handleFrames();\n    dataCenter.composeInner(params, function (data, xhr, em) {\n      if (!params.needResponse || self._reqIndex !== index) {\n        return;\n      }\n      clearTimeout(self.comTimer);\n      var state = {\n        pending: false,\n        tabName: 'Response'\n      };\n      var res = data && data.res;\n      if (self.handleFrames(res)) {\n        data.frames = [];\n        data.inComposer = true;\n      }\n      var reqId;\n      self.handleBody(res);\n      if (!data || data.ec !== 0) {\n        var status = xhr && xhr.status;\n        if (status) {\n          em = status;\n          util.showSystemError(xhr);\n        } else if (!em || typeof em !== 'string' || em === 'error') {\n          em = 'Please check the proxy settings or whether whistle has been started';\n        }\n        state.result = { url: params.url, req: '', res: { statusCode: em } };\n      } else {\n        if (res) {\n          reqId = res.headers && res.headers['x-whistle-req-id'];\n          res.rawHeaders = dataCenter.getRawHeaders(\n              res.headers,\n              res.rawHeaderNames\n            );\n          res.rawTrailers = dataCenter.getRawHeaders(\n              res.trailers,\n              res.rawTrailerNames\n            );\n        } else {\n          data.res = { statusCode: 200 };\n        }\n        data.url = params.url;\n        data.req = '';\n        state.result = data;\n      }\n      state.reqId = reqId;\n      self.setState(state);\n    });\n    params.date = Date.now();\n    params.body = params.body || '';\n    params.needResponse && this.addHistory(params);\n    events.trigger('autoRefreshNetwork');\n    self.setState({ result: '', pending: params.needResponse, composerTime: null });\n  },\n  selectAll: function (e) {\n    e.target.select();\n    this.showHints();\n  },\n  saveRules: function () {\n    var rules = findDOMNode(this.refs.composerRules).value;\n    this.state.rules = rules;\n    storage.set('composerRules', rules);\n    this.setState({});\n  },\n  formatJSON: function () {\n    var body = findDOMNode(this.refs.body);\n    if (!body.value.trim()) {\n      return;\n    }\n    var data = util.parseRawJson(body.value);\n    if (data) {\n      body.value = JSON.stringify(data, null, '  ');\n      this.saveComposer();\n    }\n  },\n  inspectJSON: function() {\n    var body = findDOMNode(this.refs.body).value;\n    events.trigger('showJsonViewDialog', body.trim());\n  },\n  onRulesChange: function () {\n    clearTimeout(this.rulesTimer);\n    this.rulesTimer = setTimeout(this.saveRules, 600);\n  },\n  onKeyDown: function (e) {\n    if (e.ctrlKey || e.metaKey) {\n      if (e.keyCode == 68) {\n        e.target.value = '';\n        e.preventDefault();\n        e.stopPropagation();\n      } else if (e.keyCode == 88) {\n        e.stopPropagation();\n      }\n    }\n  },\n  formatHeaders: function(e) {\n    util.handleTab(e);\n    this.onKeyDown(e);\n  },\n  onFormat: function(e) {\n    util.handleFormat(e, this.formatJSON);\n    util.handleTab(e);\n    this.onKeyDown(e);\n  },\n  showCookiesDialog: function() {\n    var self = this;\n    var url = findDOMNode(self.refs.url).value;\n    var host = util.getHostname(url).toLowerCase();\n    if (!/^[a-z.\\d_-]+$/.test(host)) {\n      return message.info('Cookies not found');\n    }\n    if (self._pending) {\n      return;\n    }\n    self._pending = true;\n    dataCenter.getCookies({ domain: host }, function (result, xhr) {\n      self._pending = false;\n      result = (result && result.cookies) || [];\n      var maxCount = 30;\n      if (result.length < maxCount) {\n        var list = dataCenter.networkModal.getList();\n        for (var i = list.length - 1; i >= 0; i--) {\n          var item = list[i];\n          if (util.getHostname(item.url) === host) {\n            var cookie = item.req.headers.cookie;\n            if (cookie && result.indexOf(cookie) === -1) {\n              result.push(cookie);\n              if (result.length >= maxCount) {\n                break;\n              }\n            }\n          }\n        }\n      }\n      if (!result.length) {\n        return message.info('Cookies not found');\n      }\n      if (result.length < maxCount) {\n        var cookies = self._cacheCookies;\n        if (cookies && cookies.domain === host) {\n          for (var j = 0, len = cookies.cookies.length; j < len; j++) {\n            var c = cookies.cookies[j];\n            if (c && result.indexOf(c) === -1) {\n              result.push(c);\n              if (result.length >= maxCount) {\n                break;\n              }\n            }\n          }\n        }\n      }\n\n      self._cacheCookies = {\n        domain: host,\n        cookies: result\n      };\n      self.refs.cookiesDialog.show(result);\n    });\n  },\n  insertCookie: function(cookie) {\n    var elem = findDOMNode(this.refs.headers);\n    var headers = util.parseHeaders(elem.value);\n    headers.cookie = cookie;\n    elem.value = util.objectToString(headers);\n    if (this.state.showPretty) {\n      this.refs.prettyHeaders.update(headers);\n    }\n    this.saveComposer();\n  },\n  setUrl: function(value) {\n    findDOMNode(this.refs.url).value = value || '';\n    this.hideHints();\n    this.onComposerChange();\n  },\n  clickHints: function(e) {\n    var value = e.target.title;\n    value && this.setUrl(value);\n  },\n  onUrlKeyDown: function(e) {\n    var elem;\n    if (e.keyCode === 38) {\n      // up\n      elem = this.hintElem.find('.w-active');\n      if (!this.state.showHints) {\n        this.showHints();\n      }\n      if (elem.length) {\n        elem.removeClass('w-active');\n        elem = elem.prev('li').addClass('w-active');\n      }\n\n      if (!elem.length) {\n        elem = this.hintElem.find('li:last');\n        elem.addClass('w-active');\n      }\n      util.ensureVisible(elem, this.hintElem);\n      e.preventDefault();\n    } else if (e.keyCode === 40) {\n      // down\n      elem = this.hintElem.find('.w-active');\n      if (!this.state.showHints) {\n        this.showHints();\n      }\n      if (elem.length) {\n        elem.removeClass('w-active');\n        elem = elem.next('li').addClass('w-active');\n      }\n\n      if (!elem.length) {\n        elem = this.hintElem.find('li:first');\n        elem.addClass('w-active');\n      }\n      util.ensureVisible(elem, this.hintElem);\n      e.preventDefault();\n    } else if (e.keyCode === 13) {\n      elem = this.hintElem.find('.w-active');\n      var value = elem.attr('title');\n      value && this.setUrl(value);\n      this.execute();\n    } else {\n      var curUrl = e.target.value;\n      this.onKeyDown(e);\n      if (curUrl && !e.target.value) {\n        this.showHints();\n        this.setUrl();\n      }\n    }\n  },\n  onTabChange: function (e) {\n    var tabName = e.target.name || 'Request';\n    if (tabName === this.state.tabName) {\n      return;\n    }\n    this.setState({ tabName: tabName });\n  },\n  onContextMenu: function(e) {\n    e.preventDefault();\n    var hideLoadTest = !dataCenter.whistleId;\n    var data = util.getMenuPosition(e, 150);\n    SEND_CTX_MENU[2].name = this.state.showHistory ? 'Hide History' : 'Show History';\n    SEND_CTX_MENU[3].hide = hideLoadTest;\n    data.list = SEND_CTX_MENU;\n    data.className = 'w-ctx-sub-menu-left';\n    this.refs.contextMenu.show(data);\n    this.hideHints();\n  },\n  showHints: function() {\n    var list = this._histroyUrls;\n    if (!list || !list.length) {\n      this.state.showHints = true;\n      return this.loadHistory();\n    }\n    var curUrl = findDOMNode(this.refs.url).value.trim();\n    var keyword = curUrl.toLowerCase();\n    var urlHints = keyword ? list.filter(function(url) {\n      return url.toLowerCase().indexOf(keyword) !== -1;\n    }) : list;\n    if (urlHints.length === 1 && curUrl === urlHints[0]) {\n      urlHints = null;\n    }\n    this.setState({ showHints: true, urlHints: urlHints });\n  },\n  hideHints: function() {\n    if (this.state.showHints) {\n      $(this.refs.hints).find('.w-active').removeClass('w-active');\n    }\n    this.setState({ showHints: false });\n  },\n  showParams: function() {\n    var url = findDOMNode(this.refs.url).value.replace(/#.*$/, '');\n    var index = url.indexOf('?');\n    var hasQuery = index !== -1;\n    var query = hasQuery ? url.substring(index + 1) : '';\n    var params = util.parseQueryString(query, null, null, decodeURIComponent);\n    this.refs.paramsEditor.update(params);\n    if (this.state.showParams) {\n      this.setState({ hasQuery: hasQuery });\n    } else {\n      this.setState({ showParams: true, hasQuery: hasQuery });\n    }\n  },\n  hideParams: function() {\n    if (this.state.showParams) {\n      this.setState({ showParams: false });\n    }\n  },\n  toggleParams: function() {\n    if (this.state.showParams) {\n      this.hideParams();\n    } else {\n      this.showParams();\n    }\n  },\n  clearQuery: function() {\n    var self = this;\n    win.confirm('Do you confirm the deletion of all params?', function(sure) {\n      if (sure) {\n        self.refs.paramsEditor.clear();\n        self.hideParams();\n      }\n    });\n  },\n  addQueryParam: function() {\n    this.refs.paramsEditor.onAdd();\n  },\n  onParamsChange: function () {\n    var query = this.refs.paramsEditor.toString();\n    var elem = findDOMNode(this.refs.url);\n    elem.value = util.replacQuery(elem.value, query);\n    this.saveComposer();\n    this.setState({ hasQuery: !!query });\n  },\n  onClickContextMenu: function (action) {\n    switch (action) {\n    case 'Replay Times':\n      return this.showRepeatTimes();\n    case 'history':\n      return this.toggleHistory();\n    case 'file':\n      return this.uploadFile();\n    case 'Export':\n      return this.export();\n    case 'createApiTest':\n      return util.showService('createApiTest');\n    case 'copyAsScript':\n      return util.showService('copyAsScript');\n    }\n  },\n  uploadFile: function() {\n    if (!this.reading) {\n      findDOMNode(this.refs.readLocalFile).click();\n    }\n  },\n  readLocalFile: function () {\n    var form = new FormData(findDOMNode(this.refs.readLocalFileForm));\n    var file = form.get('localFile');\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Maximum file size: 20MB');\n    }\n    var self = this;\n    self.reading = true;\n    util.readFile(file, function (data) {\n      self.reading = false;\n      self.localFileBase64 = util.bytesToBase64(data);\n      self.execute();\n    });\n    findDOMNode(this.refs.readLocalFile).value = '';\n  },\n  import: function(e) {\n    events.trigger('showImportDialog', 'composer');\n  },\n  copyAsCURL: function() {\n    var state = this.state;\n    var body = findDOMNode(this.refs.body).value;\n    var base64 = '';\n    if (state.isHexText) {\n      base64 = util.getBase64FromHexText(body);\n      body = '';\n    }\n    var text = util.asCURL({\n      url: state.url || '',\n      req: {\n        method: state.method,\n        headers: util.parseHeaders(state.headers),\n        base64: base64,\n        body: body\n      }\n    });\n    util.copyText(text, true);\n  },\n  export: function() {\n    var data = this.getComposerData();\n    var state = this.state;\n    data.disableBody = state.disableBody;\n    data.rules = state.rules;\n    data.disableComposerRules = state.disableComposerRules;\n    data.isHexText = state.isHexText;\n    data.isCRLF = state.isCRLF;\n    data.type = 'setComposerData';\n    data.enableProxyRules = state.enableProxyRules;\n    events.trigger('showExportDialog', ['composer', data]);\n  },\n  onBodyStateChange: function (e) {\n    var disableBody = !e.target.checked;\n    this.setState({ disableBody: disableBody });\n    storage.set('disableComposerBody', disableBody ? 1 : '');\n  },\n  focusEnableBody: function () {\n    this.setState({ disableBody: false });\n    storage.set('disableComposerBody', '');\n  },\n  shakeMethod: function() {\n    if (!this.hasBody) {\n      util.shakeElem($(findDOMNode(this.refs.method)));\n    }\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var type = state.type;\n    var rules = state.rules;\n    var showPretty = state.showPretty;\n    var useH2 = state.useH2;\n    var pending = state.pending;\n    var result = state.result || '';\n    var tabName = state.tabName;\n    var showParams = state.showParams;\n    var showRequest = tabName === 'Request';\n    var showResponse = tabName === 'Response';\n    var showFrames = tabName === 'Frames';\n    var statusCode = result ? result.res && result.res.statusCode : '';\n    var isForm = type === 'form';\n    var method = state.method;\n    var hasBody = hasReqBody(method, state.url, state.headers);\n    var showPrettyBody = showPretty && isForm && hasBody;\n    var showUpload = type === 'upload' && hasBody;\n    var isStrictMode = dataCenter.isStrictMode();\n    var disableComposerRules = isStrictMode || state.disableComposerRules;\n    var isHexText = state.isHexText;\n    var isCRLF = state.isCRLF;\n    var disableBody = state.disableBody;\n    var lockBody = pending || disableBody || !hasBody;\n    var showHistory = state.showHistory;\n    var urlHints = state.urlHints;\n    var hasQuery = state.hasQuery;\n    var enableProxyRules = state.enableProxyRules;\n    var reqData = state.reqData;\n    var tips = hasBody ? null : method + ' method is not allowed to have a request body';\n    var composerTime = state.composerTime;\n    var resProps = {\n      'Status Code': statusCode == null ? 'aborted' : statusCode,\n      'Take Time': getComposerTime(composerTime)\n    };\n\n    self.hasBody = hasBody;\n\n    return (\n      <div\n        className={\n          'fill box w-detail-ctn w-detail-com' +\n          (showHistory ? ' w-show-history' : '') +\n          (util.getBool(self.props.hide) ? ' hide' : '')\n        }\n      >\n        <div className=\"fill v-box\">\n          <div className=\"w-com-url box\">\n            <Icon name=\"dashboard\" className=\"w-com-history-btn\"\n              title={(showHistory ? 'Hide' : 'Show') + ' history list'}\n              onClick={this.toggleHistory}\n            />\n            <select\n              disabled={pending}\n              value={method}\n              onChange={this.onComposerChange}\n              ref=\"method\"\n              className=\"form-control w-com-method\"\n            >\n              {METHODS.map(function (m) {\n                return <option value={m}>{m}</option>;\n              })}\n            </select>\n            <input\n              readOnly={pending}\n              defaultValue={state.url}\n              onChange={this.onUrlChange}\n              onKeyUp={this.handleUrlKeyUp}\n              onKeyDown={this.onUrlKeyDown}\n              onFocus={this.selectAll}\n              onDoubleClick={this.showHints}\n              onBlur={this.hideHints}\n              ref=\"url\"\n              type=\"text\"\n              maxLength=\"8192\"\n              placeholder=\"Enter URL\"\n              className=\"fill w-com-input\"\n            />\n            <button\n              className=\"btn btn-default w-com-params\"\n              onClick={self.toggleParams}\n            >\n              Params\n            </button>\n            <button\n              disabled={pending}\n              onClick={this.execute}\n              onContextMenu={self.onContextMenu}\n              title={enableProxyRules ? null : 'Whistle Rules IGNORED'}\n              className={'btn w-com-execute btn-' + (enableProxyRules ? 'primary' : 'info')}\n            >\n              <Icon name=\"send\" />\n            </button>\n            <div\n              className=\"w-filter-hint\"\n              style={{ display: state.showHints && urlHints && urlHints.length ? '' : 'none' }}\n              onMouseDown={util.preventBlur}\n            >\n              <div className=\"w-filter-bar\">\n                <a onClick={this.toggleHistory}>\n                  <Icon name=\"dashboard\" />{showHistory ? 'Hide' : 'Show'} history\n                </a>\n                <CloseBtn onClick={self.hideHints} className=\"w-clear-hints\" />\n              </div>\n              <ul ref=\"hints\" onClick={this.clickHints}>\n                {\n                  urlHints ? urlHints.map(function(item) {\n                    return <li title={item}>{item}</li>;\n                  }) : null\n                }\n              </ul>\n            </div>\n          </div>\n          <div\n            className={'w-layer w-com-params-editor v-box' + (showParams ? '' : ' hide')}\n          >\n            <div className=\"w-filter-bar\">\n              <CloseBtn onClick={self.hideParams} className=\"w-close-params\" />\n              <a style={{display: hasQuery ? null : 'none'}} className=\"w-params-clear-btn\" onClick={this.clearQuery}>\n                <Icon name=\"trash\" />Clear\n              </a>\n              <a onClick={this.addQueryParam}>\n                +Param\n              </a>\n            </div>\n            <PropsEditor\n              ref=\"paramsEditor\"\n              onChange={this.onParamsChange}\n              callback={this.execute}\n            />\n          </div>\n          <Divider vertical=\"true\" leftWidth=\"72\">\n            <div\n              ref=\"rulesCon\"\n              onDoubleClick={this.enableRules}\n              title={isStrictMode ? TIPS : undefined}\n              className=\"v-box fill w-com-rules\"\n            >\n              <div className=\"w-detail-inspectors-title\">\n                <label className=\"w-com-rules-label\">\n                  <input\n                    disabled={pending}\n                    onChange={this.onDisableChange}\n                    checked={!state.disableComposerRules}\n                    type=\"checkbox\"\n                  />\n                  Rules\n                </label>\n                <label className=\"w-com-proxy-rules\" title=\"Whether to use the Rules in Whistle?\">\n                  <input\n                    disabled={pending}\n                    type=\"checkbox\"\n                    onChange={this.onProxyRules}\n                    checked={enableProxyRules}\n                  />\n                  Whistle Rules\n                </label>\n                <label className=\"w-com-use-h2\">\n                  <input\n                    disabled={pending}\n                    type=\"checkbox\"\n                    onChange={this.toggleH2}\n                    checked={dataCenter.supportH2 && useH2}\n                  />\n                  HTTP/2\n                </label>\n                <label className={'w-com-enable-body' + (hasBody ? '' : ' w-disabled')} title={tips} onClick={self.shakeMethod}>\n                  <input\n                    disabled={pending || !hasBody}\n                    checked={!disableBody && hasBody}\n                    type=\"checkbox\"\n                    onChange={this.onBodyStateChange}\n                  />\n                  Body\n                </label>\n                <div className=\"w-com-btns\">\n                  <a draggable=\"false\" onClick={self.import}>Import</a>\n                  <a draggable=\"false\" onClick={self.export}>Export</a>\n                  <a draggable=\"false\" onClick={self.copyAsCURL}>AsCURL</a>\n                </div>\n              </div>\n              <textarea\n                readOnly={disableComposerRules || pending}\n                defaultValue={rules}\n                onKeyDown={util.handleTab}\n                ref=\"composerRules\"\n                onChange={this.onRulesChange}\n                onDoubleClick={this.enableRules}\n                style={{\n                  background:\n                    !disableComposerRules && rules ? 'var(--b-filtered)' : undefined\n                }}\n                maxLength=\"8192\"\n                className=\"fill v-box w-com-rules\"\n                placeholder=\"Enter rules (Higher priority than Whistle Rules)\"\n              />\n            </div>\n            <div className=\"v-box fill\">\n              <div className=\"w-detail-inspectors-title w-com-tabs\">\n                <button\n                  onClick={this.onTabChange}\n                  name=\"Request\"\n                  className={getTabClass(showRequest)}\n                >\n                  <Icon name=\"edit\" />\n                  Request\n                </button>\n                <button\n                  title={result.url}\n                  onClick={this.onTabChange}\n                  name=\"Response\"\n                  style={{fontWeight: 'normal'}}\n                  className={getTabClass(showResponse)}\n                >\n                  <Icon name=\"arrow-left\" />\n                  Response\n                </button>\n                <button\n                  title={result.url}\n                  id=\"whistleComposerFrames\"\n                  onClick={this.onTabChange}\n                  name=\"Frames\"\n                  style={{fontWeight: 'normal'}}\n                  className={getTabClass(showFrames)}\n                >\n                  <Icon name=\"menu-hamburger\" />\n                  Frames\n                </button>\n              </div>\n              <Divider hide={!showRequest} vertical=\"true\">\n                <div className=\"fill v-box w-com-headers\">\n                  <div\n                    className=\"w-com-bar\"\n                    onChange={this.onTypeChange}\n                  >\n                    <label>\n                      <input\n                        onChange={this.onShowPretty}\n                        type=\"checkbox\"\n                        checked={showPretty}\n                      />\n                      Pretty\n                    </label>\n                    <label className=\"w-com-label\">Type:</label>\n                    <label>\n                      <input\n                        disabled={pending}\n                        data-type=\"form\"\n                        name=\"type\"\n                        type=\"radio\"\n                        checked={isForm}\n                      />\n                      Form\n                    </label>\n                    <label>\n                      <input\n                        disabled={pending}\n                        data-type=\"upload\"\n                        name=\"type\"\n                        type=\"radio\"\n                        checked={type === 'upload'}\n                      />\n                      Upload\n                    </label>\n                    <label>\n                      <input\n                        disabled={pending}\n                        data-type=\"json\"\n                        name=\"type\"\n                        type=\"radio\"\n                        checked={type === 'json'}\n                      />\n                      JSON\n                    </label>\n                    <label>\n                      <input\n                        disabled={pending}\n                        data-type=\"text\"\n                        name=\"type\"\n                        type=\"radio\"\n                        checked={type === 'text'}\n                      />\n                      Text\n                    </label>\n                    <label>\n                      <input\n                        data-type=\"custom\"\n                        name=\"type\"\n                        type=\"radio\"\n                        checked={type === 'custom'}\n                      />\n                      Raw\n                    </label>\n                    <div className=\"flex-1\" />\n                    <button\n                      disabled={pending}\n                      className=\"btn btn-default\"\n                      onClick={this.showCookiesDialog}\n                    >\n                      Cookies\n                    </button>\n                    <button\n                      disabled={pending}\n                      className=\"btn btn-primary\"\n                      onClick={this.addHeader}\n                    >\n                      +Header\n                    </button>\n                  </div>\n                  <textarea\n                    readOnly={pending}\n                    defaultValue={state.headers}\n                    onChange={this.onComposerChange}\n                    maxLength={MAX_HEADERS_SIZE}\n                    onKeyDown={this.formatHeaders}\n                    ref=\"headers\"\n                    placeholder=\"Enter headers\"\n                    name=\"headers\"\n                    className={\n                      'fill v-box' + (showPretty ? ' hide' : '')\n                    }\n                  />\n                  <PropsEditor\n                    disabled={pending}\n                    ref=\"prettyHeaders\"\n                    isHeader=\"1\"\n                    hide={!showPretty}\n                    onChange={this.onHeaderChange}\n                    callback={this.execute}\n                  />\n                </div>\n                <div className={'fill v-box w-com-body' + (disableBody ? ' w-com-disable-body' : '')}>\n                  <div className=\"w-com-bar\">\n                    <label className=\"w-com-label\" onClick={self.shakeMethod}>\n                      <input\n                        disabled={pending || !hasBody}\n                        checked={!disableBody && hasBody}\n                        type=\"checkbox\"\n                        onChange={this.onBodyStateChange}\n                      />\n                      Body\n                    </label>\n                    <label\n                      className={\n                        'w-com-hex-text' +\n                        (isHexText ? ' w-checked' : '') +\n                        (showUpload || showPrettyBody ? ' hide' : '')\n                      }\n                      onDoubleClick={this.focusEnableBody}\n                    >\n                      <input\n                        disabled={pending}\n                        checked={isHexText}\n                        type=\"checkbox\"\n                        onChange={this.onHexTextChange}\n                      />\n                      HexText\n                    </label>\n                    <label\n                      className={\n                        'w-com-crlf' +\n                        (isHexText || showPrettyBody || showUpload ? ' hide' : '') +\n                        (isCRLF ? ' w-checked' : '')\n                      }\n                      onDoubleClick={this.focusEnableBody}\n                    >\n                      <input\n                        disabled={pending}\n                        checked={isCRLF}\n                        onChangeCapture={this.onCRLFChange}\n                        type=\"checkbox\"\n                      />\n                      \\r\\n\n                    </label>\n                    <button\n                      disabled={pending}\n                      className={\n                        'btn btn-default' +\n                        (showPrettyBody || isHexText || showUpload\n                          ? ' hide'\n                          : '')\n                      }\n                      onClick={this.formatJSON}\n                    >\n                      Format\n                    </button>\n                    <button\n                      className={\n                        'btn btn-primary' +\n                        (showPrettyBody || isHexText || showUpload\n                          ? ' hide'\n                          : '')\n                      }\n                      onClick={this.inspectJSON}\n                    >\n                      Inspect\n                    </button>\n                    <button\n                      disabled={lockBody}\n                      className={\n                        'btn btn-primary' +\n                        ((showPrettyBody && !isHexText) || showUpload\n                          ? ''\n                          : ' hide')\n                      }\n                      onClick={\n                        showUpload ? this.addUploadFiled : this.addField\n                      }\n                    >\n                      +Param\n                    </button>\n                  </div>\n                  {tips && <div className=\"w-record-status\" onClick={self.shakeMethod}>{tips}</div>}\n                  <textarea\n                    readOnly={lockBody}\n                    defaultValue={state.body}\n                    onChange={this.onComposerChange}\n                    maxLength={MAX_BODY_SIZE}\n                    onDoubleClick={this.focusEnableBody}\n                    onClick={self.shakeMethod}\n                    style={{ fontFamily: isHexText ? 'monospace' : undefined }}\n                    onKeyDown={this.onFormat}\n                    ref=\"body\"\n                    placeholder={'Enter ' + (isHexText ? 'hex text' : 'body')}\n                    className={\n                      'fill v-box' +\n                      (showPrettyBody  || showUpload\n                        ? ' hide'\n                        : '')\n                    }\n                  />\n                  <PropsEditor\n                    onDoubleClick={this.focusEnableBody}\n                    disabled={lockBody}\n                    ref=\"prettyBody\"\n                    hide={!showPrettyBody || showUpload}\n                    onChange={this.onFieldChange}\n                    callback={this.execute}\n                  />\n                  <PropsEditor\n                    onDoubleClick={this.focusEnableBody}\n                    disabled={lockBody}\n                    ref=\"uploadBody\"\n                    hide={!showUpload}\n                    onChange={this.updateUploadData}\n                    onUpdate={this.updateUploadData}\n                    callback={this.execute}\n                    allowUploadFile\n                  />\n                </div>\n              </Divider>\n              <LazyInit inited={showResponse}>\n                <div\n                  style={{ display: showResponse ? undefined : 'none' }}\n                  className={'w-com-res w-com-res-' + getStatus(statusCode)}\n                >\n                  <button\n                    onClick={this.onTabChange}\n                    name=\"Request\"\n                    className=\"btn btn-default w-com-back-btn\"\n                    title=\"Back to Request\"\n                  >\n                    <Icon name=\"menu-left\" />\n                  </button>\n                  <Properties\n                    modal={resProps}\n                  />\n                  <ViewInspector reqId={state.reqId} />\n                </div>\n              </LazyInit>\n              <LazyInit inited={showResponse}>\n                <ResDetail\n                  inComposer=\"1\"\n                  modal={result}\n                  hide={!showResponse}\n                />\n              </LazyInit>\n              <LazyInit inited={showFrames}>\n                <Frames hide={!showFrames} data={reqData} frames={reqData && reqData.frames} />\n              </LazyInit>\n            </div>\n          </Divider>\n        </div>\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n        <Dialog ref=\"setRepeatTimes\" wstyle=\"w-replay-count-dialog\">\n          <div className=\"modal-body\">\n            <label>\n              Times:\n              <input\n                ref=\"repeatTimes\"\n                placeholder={'<= ' + MAX_REPEAT_TIMES}\n                onKeyDown={this.repeatRequest}\n                onChange={this.repeatTimesChange}\n                value={state.repeatTimes}\n                className=\"form-control\"\n                maxLength=\"3\"\n              />\n            </label>\n            <button\n              type=\"button\"\n              ref=\"repeatBtn\"\n              onKeyDown={this.repeatRequest}\n              tabIndex=\"0\"\n              onMouseDown={util.preventBlur}\n              className=\"btn btn-primary\"\n              onClick={this.repeatRequest}\n              disabled={!state.repeatTimes}\n            >\n              Send\n            </button>\n          </div>\n        </Dialog>\n        <CookiesDialog onInsert={this.insertCookie} ref=\"cookiesDialog\" />\n        <HistoryData\n          show={showHistory}\n          data={state.historyData}\n          onClose={this.hideHistory}\n          onReplay={this.handeHistoryReplay}\n          onEdit={this.handleHistoryEdit}\n        />\n        <form\n          ref=\"readLocalFileForm\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"readLocalFile\"\n            onChange={this.readLocalFile}\n            type=\"file\"\n            name=\"localFile\"\n          />\n        </form>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Composer;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/console.js",
    "content": "var $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar JSONTree = require('./components/react-json-tree')['default'];\nvar ExpandCollapse = require('./expand-collapse');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar FilterInput = require('./filter-input');\nvar DropDown = require('./dropdown');\nvar RecordBtn = require('./record-btn');\nvar events = require('./events');\nvar storage = require('./storage');\nvar win = require('./win');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_COUNT = dataCenter.MAX_LOG_LENGTH;\nvar MAX_FILE_SIZE = 1024 * 1024 * 2;\n\nvar allLogs = {\n  value: '',\n  text: 'All Logs'\n};\n\nfunction parseLog(log, expandRoot) {\n  if (log.view) {\n    return log.view;\n  }\n  try {\n    var data = JSON.parse(log.text);\n    var hasNonStr = data.some(function (obj) {\n      return typeof obj !== 'string' || obj === 'undefined';\n    });\n    log.view = data.map(function (data) {\n      if (typeof data === 'string' && data !== 'undefined') {\n        return <ExpandCollapse text={hasNonStr ? '\"' + data + '\"' : data} />;\n      }\n      if (!data || typeof data !== 'object') {\n        return (\n          <ExpandCollapse\n            wStyle={{ color: 'var(--c-has)' }}\n            text={data + ''}\n          />\n        );\n      }\n      return (\n        <JSONTree\n          data={data}\n          onSearch={function() {\n            util.showJSONDialog(data);\n          }}\n          shouldExpandNode={expandRoot ? undefined : false}\n        />\n      );\n    });\n    return log.view;\n  } catch (e) {}\n  return <ExpandCollapse text={log.text} />;\n}\n\nfunction getLogInfo(log) {\n  var result = ['Level: ' + log.level.toUpperCase()];\n  if (log.logId) {\n    result.unshift('LogID: ' + log.logId);\n  }\n  return ' (' + result.join(', ') + ')';\n}\n\nvar Console = React.createClass({\n  getInitialState: function () {\n    return {\n      scrollToBottom: true,\n      logIdList: [allLogs],\n      levels: [\n        {\n          value: '',\n          text: 'All Levels'\n        },\n        {\n          value: 'debug',\n          text: 'Debug'\n        },\n        {\n          value: 'info',\n          text: 'Info (Log)'\n        },\n        {\n          value: 'warn',\n          text: 'Warn'\n        },\n        {\n          value: 'error',\n          text: 'Error'\n        },\n        {\n          value: 'fatal',\n          text: 'Fatal'\n        }\n      ],\n      expandRoot: storage.get('expandJsonRoot') != 1\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    var container = (this.container = findDOMNode(\n      self.refs.container\n    ));\n    var content = (this.content = findDOMNode(self.refs.logContent));\n    var updateLogs = function (logs) {\n      var state = self.state;\n      var curLogs = state.logs;\n      if (curLogs !== logs && Array.isArray(curLogs)) {\n        logs.push.apply(logs, curLogs);\n      }\n      state.logs = util.filterLogList(logs, self.keyword, true);\n      if (self.props.hide) {\n        return;\n      }\n      var atBottom = util.scrollAtBottom(container, content);\n      if (atBottom) {\n        var len = logs.length - MAX_COUNT;\n        len > 9 && util.trimLogList(logs, len, self.keyword);\n      }\n      self.setState({});\n    };\n\n    if (dataCenter.uploadLogs) {\n      updateLogs(dataCenter.uploadLogs);\n      dataCenter.uploadLogs = null;\n    }\n    events.on('uploadLogs', function (_, result) {\n      if (self.props.hide) {\n        return;\n      }\n      var logs = result.logs;\n      var curLogs = self.state.logs;\n      if (curLogs) {\n        curLogs.push.apply(curLogs, logs);\n        var overflow = curLogs.length - MAX_COUNT;\n        overflow > 19 && util.trimLogList(curLogs, overflow, self.keyword);\n      } else {\n        curLogs = logs;\n      }\n      updateLogs(curLogs);\n    });\n    dataCenter.on('log', updateLogs);\n\n    events.on('consoleImportFile', function (_, file) {\n      self.importFile(file);\n    });\n    events.on('consoleImportData', function (_, data) {\n      self.importData(data);\n    });\n\n    $(container).on('scroll', function () {\n      var data = self.state.logs;\n      clearTimeout(self.scrollTimer);\n      if (\n        data &&\n        (self.state.scrollToBottom = util.scrollAtBottom(container, content))\n      ) {\n        self.scrollTimer = setTimeout(function () {\n          var len = data.length - MAX_COUNT;\n          self.scrollTimer = null;\n          if (len > 9) {\n            util.trimLogList(data, len, self.keyword);\n            self.setState({ logs: data });\n          }\n        }, 2000);\n      }\n    });\n  },\n  selectFile: function () {\n    events.trigger('showImportDialog', 'console');\n  },\n  importFile: function (file) {\n    if (!file || !/\\.log$/i.test(file.name)) {\n      return win.alert('Only .log files are supported');\n    }\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Maximum file size: 2MB');\n    }\n    util.readFileAsText(file, this.importData);\n  },\n  importData: function (logs) {\n    logs = util.parseLogs(logs);\n    logs && events.trigger('uploadLogs', { logs: logs });\n  },\n  changeLogId: function (option) {\n    dataCenter.changeLogId(option.value);\n  },\n  changeLevel: function (option) {\n    this.setState({ level: option.value });\n  },\n  clearLogs: function () {\n    dataCenter.clearedLogs = true;\n    dataCenter.clearLogList();\n    this.setState({ logs: [] });\n  },\n  scrollTop: function () {\n    this.container.scrollTop = 0;\n  },\n  autoRefresh: function () {\n    this.container.scrollTop = 10000000;\n  },\n  stopAutoRefresh: function () {\n    if (util.scrollAtBottom(this.container, this.content)) {\n      this.container.scrollTop = this.container.scrollTop - 10;\n    }\n  },\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    var toggleHide = hide != util.getBool(nextProps.hide);\n    if (toggleHide || !hide) {\n      if (!toggleHide && !hide) {\n        this.state.scrollToBottom = util.scrollAtBottom(\n          this.container,\n          this.content\n        );\n      }\n      clearTimeout(this.filterTimer);\n      clearTimeout(this.scrollTimer);\n      return true;\n    }\n    return false;\n  },\n  componentDidUpdate: function () {\n    if (!this.props.hide && this.state.scrollToBottom) {\n      this.container.scrollTop = 10000000;\n    }\n  },\n  onConsoleFilterChange: function (keyword) {\n    var self = this;\n    keyword = keyword.trim();\n    var logs = self.state.logs;\n    var consoleKeyword = util.parseKeyword(keyword);\n    self.keyword = keyword && consoleKeyword;\n    util.filterLogList(logs, consoleKeyword);\n    if (!keyword) {\n      var len = logs && logs.length - MAX_COUNT;\n      len > 9 && logs.splice(0, len);\n    }\n    clearTimeout(self.filterTimer);\n    self.filterTimer = setTimeout(function () {\n      self.filterTimer = null;\n      self.setState({});\n    }, 500);\n  },\n  handleAction: function (type) {\n    if (type === 'top') {\n      return this.scrollTop();\n    }\n    if (type === 'bottom') {\n      return this.autoRefresh();\n    }\n    if (type === 'pause') {\n      dataCenter.pauseConsoleRecord();\n      return;\n    }\n    var refresh = type === 'refresh';\n    dataCenter.stopConsoleRecord(!refresh);\n    if (refresh) {\n      return this.autoRefresh();\n    }\n  },\n  onBeforeShow: function () {\n    var list = dataCenter.getLogIdList() || [];\n    list = list.map(function (id) {\n      return {\n        value: id,\n        text: id\n      };\n    });\n    list.unshift(allLogs);\n    this.setState({\n      logIdList: list\n    });\n  },\n  changeExpandRoot: function (e) {\n    this.state.expandRoot = e.target.checked;\n  },\n  export: function () {\n    var logs = [];\n    this.state.logs.forEach(function (log) {\n      if (!log.hide) {\n        logs.push({\n          id: log.id,\n          text: log.text,\n          level: log.level,\n          date: log.date\n        });\n      }\n    });\n    events.trigger('showExportDialog', ['console', logs]);\n  },\n  render: function () {\n    var state = this.state;\n    var logs = state.logs || [];\n    var logIdList = state.logIdList;\n    var level = state.level;\n    var expandRoot = state.expandRoot;\n    var disabled = !util.hasVisibleLog(logs);\n    var index = 0;\n\n    return (\n      <div\n        className={\n          'fill v-box w-textarea w-log' +\n          (this.props.hide ? ' hide' : '')\n        }\n      >\n        <div className=\"w-log-action-bar\">\n          <DropDown\n            onBeforeShow={this.onBeforeShow}\n            help={util.getDocUrl('gui/console.html')}\n            onChange={this.changeLogId}\n            options={logIdList}\n          />\n          <DropDown onChange={this.changeLevel} options={state.levels} />\n          <label className=\"w-log-expand-root\">\n            <input\n              type=\"checkbox\"\n              defaultChecked={expandRoot}\n              onChange={this.changeExpandRoot}\n            />\n            Expand JSON Root\n          </label>\n          <div className=\"w-textarea-bar\">\n            <RecordBtn onClick={this.handleAction} />\n            <a onClick={this.selectFile} draggable=\"false\">\n              Import\n            </a>\n            <a\n              className={disabled ? 'w-disabled' : ''}\n              onClick={disabled ? null : this.export}\n              draggable=\"false\"\n            >\n              Export\n            </a>\n            <a\n              className={'w-clear' + (disabled ? ' w-disabled' : '')}\n              onClick={disabled ? undefined : this.clearLogs}\n              draggable=\"false\"\n            >\n              Clear\n            </a>\n          </div>\n        </div>\n        <div ref=\"container\" className=\"fill w-log-ctn\">\n          <ul ref=\"logContent\">\n            {logs.map(function (log, i) {\n              var date =\n                'Date: ' +\n                util.toLocaleString(new Date(log.date)) +\n                getLogInfo(log) + '\\r\\n';\n              var hide =\n                log.hide || (level && !hide && log.level !== level)\n                  ? ' hide'\n                  : '';\n              if (!hide) {\n                ++index;\n              }\n              return (\n                <li\n                  key={log.id}\n                  title={log.level.toUpperCase()}\n                  className={'w-' + log.level + hide}\n                >\n                  <pre>\n                    <strong>#{index}</strong>\n                    {date}\n                    {parseLog(log, expandRoot)}\n                  </pre>\n                </li>\n              );\n            })}\n          </ul>\n        </div>\n        <FilterInput onChange={this.onConsoleFilterChange} />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Console;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/context-menu.js",
    "content": "require('../css/context-menu.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar util = require('./util');\nvar EditorDialog = require('./editor-dialog');\nvar Icon = require('./icon');\n\nvar ContextMenu = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  componentDidMount: function () {\n    var self = this;\n    self.container = document.createElement('div');\n    document.body.appendChild(self.container);\n    self.componentDidUpdate();\n    $(document)\n      .on('mousedown click', function (e) {\n        if ($(e.target).closest('.w-ctx-menu').length) {\n          if (e.target.nodeName !== 'INPUT') {\n            e.preventDefault();\n          }\n          return;\n        }\n        self.hide();\n      })\n      .on('keydown', function (e) {\n        if (e.keyCode === 9) {\n          self.hide();\n        }\n      });\n    $(window).on('resize blur', function () {\n      self.hide();\n    });\n  },\n  componentDidUpdate: function () {\n    ReactDOM.unstable_renderSubtreeIntoContainer(\n      this,\n      this.getDialogElement(),\n      this.container\n    );\n  },\n  preventDefault: function (e) {\n    e.preventDefault();\n  },\n  isVisible: function () {\n    return this.state.visible;\n  },\n  onClick: function (e) {\n    var target = $(e.target).closest('li');\n    e.preventDefault();\n    if (\n      target.hasClass('w-ctx-sub-menu-list') ||\n      target.hasClass('w-ctx-item-disabled')\n    ) {\n      return;\n    }\n    var data = this.state;\n    !target.hasClass('w-ctx-item-multi') && !data.radio && this.hide();\n    var action = target.attr('data-menu-action');\n    if (this.props.onClick) {\n      this.props.onClick(\n        action,\n        e,\n        target.attr('data-parent-action'),\n        target.attr('data-name')\n      );\n    }\n    if ((e.shiftKey || e.ctrlKey || e.metaKey) && target.attr('data-shift-to-edit')) {\n      this.refs.editorDialog.show({\n        value: target.attr('data-clipboard-text')\n      });\n    }\n    if (data.radio) {\n      data.list.forEach(function (item) {\n        item.selected = item.action == action;\n      });\n      this.setState({});\n    }\n  },\n  getDialogElement: function () {\n    var self = this;\n    var data = self.state;\n    var list = data.list || [];\n    var radio = data.radio;\n    return (\n      <div\n        onClick={self.onClick}\n        className={'w-ctx-menu ' + (data.className || '')}\n        onContextMenu={self.onClick}\n        style={{\n          left: data.left,\n          top: data.top,\n          display: data.visible ? '' : 'none'\n        }}\n      >\n        <ul className=\"w-ctx-menu-list\">\n          {list.map(function (item) {\n            var subList = item.list;\n            var shiftToEdit = item.shiftToEdit ? 1 : undefined;\n            var multiple = !subList && item.multiple;\n            return (\n              <li\n                data-menu-action={item.action || item.name}\n                key={item.name}\n                className={\n                  'w-ctx-menu-item ' +\n                  (item.sep ? 'w-ctx-item-sep' : '') +\n                  (item.disabled ? ' w-ctx-item-disabled' : '') +\n                  (subList ? ' w-ctx-sub-menu-list' : '') +\n                  (item.copyText ? ' w-copy-text' : '')\n                }\n                data-clipboard-text={item.copyText}\n                style={{ display: item.hide ? 'none' : undefined }}\n                onClick={item.onClick}\n              >\n                <label className={'w-ctx-item-tt' + (item.selected ? ' w-ctx-selected' : '')}>\n                  {radio ? <span className={'w-ctx-checked' + (item.selected ? ' visible' : '')}>✔️</span> : undefined}\n                  {item.icon ? <Icon name={item.icon} /> : null}\n                  {multiple ? (\n                    <input type=\"checkbox\" checked={item.checked} />\n                  ) : null}\n                  {item.name}\n                </label>\n                {subList ? (\n                  <Icon name=\"play\" />\n                ) : undefined}\n                {subList ? <div className=\"w-ctx-menu-gap\"></div> : undefined}\n                {subList ? (\n                  <ul\n                    className=\"w-ctx-menu-list\"\n                    style={\n                      item.top > 0\n                        ? { top: -item.top * 30 - 1, maxHeight: item.maxHeight }\n                        : undefined\n                    }\n                  >\n                    {subList.map(function (subItem, i) {\n                      return (\n                        <li\n                          title={subItem.title}\n                          data-parent-action={item.action}\n                          data-name={subItem.name}\n                          data-menu-action={subItem.action || subItem.name}\n                          key={i}\n                          onClick={subItem.onClick}\n                          className={\n                            'w-ctx-menu-item ' +\n                            (subItem.sep ? 'w-ctx-item-sep' : '') +\n                            (subItem.disabled ? ' w-ctx-item-disabled' : '') +\n                            (subItem.copyText ? ' w-copy-text' : '')\n                          }\n                          style={{ display: subItem.hide ? 'none' : undefined }}\n                          data-clipboard-text={subItem.copyText}\n                          data-shift-to-edit={shiftToEdit}\n                        >\n                          <label className=\"w-ctx-item-tt\">\n                            {subItem.multiple ? (\n                              <input\n                                type=\"checkbox\"\n                                checked={subItem.checked}\n                              />\n                            ) : null}\n                            {subItem.name}\n                          </label>\n                        </li>\n                      );\n                    })}\n                  </ul>\n                ) : undefined}\n              </li>\n            );\n          })}\n        </ul>\n      </div>\n    );\n  },\n  show: function (data) {\n    data.visible = true;\n    this.setState(data);\n  },\n  hide: function () {\n    this.setState({ visible: false });\n  },\n  update: function () {\n    this.state.visible && this.setState({});\n  },\n  render: function () {\n    return <EditorDialog ref=\"editorDialog\" />;\n  }\n});\n\nContextMenu.util = util;\nContextMenu.$ = $;\n\nmodule.exports = ContextMenu;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/cookies-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar CloseBtn = require('./close-btn');\n\nvar CookiesDialog = React.createClass({\n  getInitialState: function() {\n    return { cookies: [] };\n  },\n  show: function (cookies) {\n    this.refs.cookiesDialog.show();\n    this._hideDialog = false;\n    this.setState({ cookies: cookies || [] });\n  },\n  hide: function () {\n    this.refs.cookiesDialog.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  insert: function (e) {\n    var i = e.target.getAttribute('data-index');\n    var cookie = this.state.cookies[i];\n    cookie && this.props.onInsert(cookie, true);\n    this.hide();\n  },\n  render: function () {\n    var self = this;\n    var cookies = self.state.cookies;\n    return (\n      <Dialog ref=\"cookiesDialog\" wstyle=\"w-com-cookies-dialog\">\n        <div className=\"modal-body\">\n          <CloseBtn onClick={self.hide} />\n          <table className=\"table\">\n            <thead>\n              <th className=\"w-com-cookie-order\">#</th>\n              <th className=\"w-com-cookie-value\">Cookie</th>\n              <th className=\"w-com-cookie-operation\">Operation</th>\n            </thead>\n            <tbody className=\"w-hover-body\">\n              {cookies.map(function (cookie, i) {\n                return (\n                  <tr>\n                    <th className=\"w-com-cookie-order\">{i + 1}</th>\n                    <td className=\"w-com-cookie-value\">\n                      {cookie}\n                    </td>\n                    <td className=\"w-com-cookie-operation\">\n                      <a\n                        className=\"w-copy-text-with-tips\"\n                        data-clipboard-text={cookie}\n                      >\n                        Copy\n                      </a>\n                      <a\n                        data-index={i}\n                        onClick={self.insert}\n                      >\n                        Insert\n                      </a>\n                    </td>\n                  </tr>\n                );\n              })}\n            </tbody>\n          </table>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            onClick={self.hide}\n          >\n            Close\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = CookiesDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/copy-btn.js",
    "content": "var React = require('react');\n\nvar CopyBtn = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  handleLeave: function () {\n    this.setState({ copied: false });\n  },\n  handleCopy: function () {\n    this.setState({ copied: true });\n  },\n  render: function () {\n    var copied = this.state.copied;\n    return (\n      <a\n        onMouseLeave={this.handleLeave}\n        onClick={this.handleCopy}\n        className={'w-copy-btn' + (copied ? ' w-copied-text' : ' w-copy-text')}\n        draggable=\"false\"\n        data-clipboard-text={this.props.value || ''}\n      >\n        {(copied ? 'Copied' : 'Copy') + (this.props.name || '')}\n      </a>\n    );\n  }\n});\n\nmodule.exports = CopyBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/data-center.js",
    "content": "var $ = require('jquery');\nvar createCgiObj = require('./cgi');\nvar util = require('./util');\nvar NetworkModal = require('./network-modal');\nvar storage = require('./storage');\nvar events = require('./events');\nvar workers = require('./workers');\nvar message = require('./message');\n\nvar updateWorkers = workers.updateWorkers;\nvar createCgi = createCgiObj.createCgi;\nvar MAX_INCLUDE_LEN = 5120;\nvar MAX_EXCLUDE_LEN = 5120;\nvar MAX_FRAMES_LENGTH = (exports.MAX_FRAMES_LENGTH = 256);\nvar TIMEOUT = 1000 * 36;\nvar LONG_TIMEOUT = TIMEOUT + 1000 * 10;\nvar dataCallbacks = [];\nvar serverInfoCallbacks = [];\nvar framesUpdateCallbacks = [];\nvar logCallbacks = [];\nvar directCallbacks = [];\nvar dataList = [];\nvar logList = [];\nvar svrLogList = [];\nvar setDataCenter = NetworkModal.setDataCenter;\nvar networkModal = new NetworkModal(dataList);\nvar curServerInfo;\nvar initialDataPromise, initialData, startedLoad;\nvar lastPageLogTime = -2;\nvar lastSvrLogTime = -2;\nvar curLogId;\nvar curSvrLogId;\nvar dataIndex = 1000000;\nvar MAX_PATH_LENGTH = 1024;\nvar MAX_LOG_LENGTH = 360;\nvar lastRowId;\nvar endId;\nvar hashFilterObj;\nvar clearNetwork;\nvar inited;\nvar logId;\nvar port;\nvar clientId;\nvar account;\nvar dataKeys = [];\nvar dumpCount = 0;\nvar updateCount = 0;\nvar MAX_UPDATE_COUNT = 4;\nvar MAX_WAIT_TIME = 1000 * 60 * 3;\nvar onlyViewOwnData = storage.get('onlyViewOwnData') == 1;\nvar pluginsMap = {};\nvar disabledPlugins = {};\nvar disabledAllPlugins;\nvar reqTabList = [];\nvar resTabList = [];\nvar tabList = [];\nvar toolTabList = [];\nvar comTabList = [];\nvar pluginColumns = [];\nvar webWorkerList = [];\nvar reqIndex = 0;\nvar composeDataMap = {};\nvar rulesMFlag = '';\nvar DEFAULT_CONF = {\n  timeout: TIMEOUT,\n  xhrFields: {\n    withCredentials: true\n  },\n  data: {}\n};\nvar composerItem;\nvar manualLogout; // 手动登出\nvar hasUpdater;\nvar HAS_RULES_KEY = window.Symbol ? window.Symbol('hasRules') : '__hasRules';\n\nexports.HAS_RULES_KEY = HAS_RULES_KEY;\nexports.enabledRulesCount = 0;\nexports.setComposerItem = function(item) {\n  composerItem = item;\n};\n\nexports.checkPluginUpdates = function (plugin, callback) {\n  if (!exports.whistleId) {\n    return callback();\n  }\n  // TODO: 从服务端检查插件更新\n  callback(true);\n};\n\nexports.clientIp = '127.0.0.1';\nexports.MAX_INCLUDE_LEN = MAX_INCLUDE_LEN;\nexports.MAX_EXCLUDE_LEN = MAX_EXCLUDE_LEN;\nexports.MAX_LOG_LENGTH = MAX_LOG_LENGTH - 20;\nexports.changeLogId = function (id) {\n  logId = id;\n};\n\nexports.getPort = function () {\n  return port;\n};\n\nexports.setDumpCount = function (count) {\n  dumpCount = count > 0 ? count : 0;\n};\n\nexports.clearLogList = function() {\n  logList = [];\n};\n\nexports.clearSvgLogList = function() {\n  svrLogList = [];\n};\n\nexports.setOnlyViewOwnData = function (enable) {\n  onlyViewOwnData = enable !== false;\n  storage.set('onlyViewOwnData', onlyViewOwnData ? 1 : 0);\n};\nexports.isOnlyViewOwnData = function () {\n  return onlyViewOwnData;\n};\n\nexports.filterIsEnabled = function () {\n  if (onlyViewOwnData) {\n    return true;\n  }\n  var settings = getFilterText();\n  if (\n    !settings ||\n    (settings.disabledFilterText && settings.disabledExcludeText)\n  ) {\n    return;\n  }\n  var text = !settings.disabledFilterText && settings.filterText.trim();\n  return text || (!settings.disabledExcludeText && settings.excludeText.trim());\n};\n\nfunction updateRulesInfo(data) {\n  var enabledCount = data.enabledCount || 0;\n  exports.backRulesFirst = data.backRulesFirst;\n  if (exports.enabledRulesCount !== enabledCount) {\n    exports.enabledRulesCount = enabledCount;\n    events.trigger('enabledRulesCountChange', enabledCount);\n  }\n}\n\nfunction compareFilter(filter) {\n  if (filter !== hashFilterObj) {\n    return false;\n  }\n  if (!filter) {\n    return true;\n  }\n  if (filter.url !== hashFilterObj.url || filter.ip !== hashFilterObj.ip) {\n    return false;\n  }\n  return (\n    filter.name === hashFilterObj.name && filter.value === hashFilterObj.value\n  );\n}\n\nfunction handleHashFilterChanged(e) {\n  var hash = location.hash.substring(1);\n  var index = hash.indexOf('?');\n  var filter;\n  if (index !== -1) {\n    var obj = util.parseQueryString(\n      hash.substring(index + 1),\n      null,\n      null,\n      decodeURIComponent\n    );\n    var curRuleName = obj.rulesName || obj.ruleName;\n    var curValueName = obj.valuesName || obj.valueName;\n    if (curRuleName !== exports.activeRulesName) {\n      exports.activeRulesName = curRuleName;\n      events.trigger('activeRules');\n    }\n    if (curValueName !== exports.activeValuesName) {\n      exports.activeValuesName = curValueName;\n      events.trigger('activeValues');\n    }\n    if (obj.url) {\n      filter = {};\n      filter.url = obj.url;\n    }\n    for (var i = 0; i < 6; i++) {\n      var key = 'name' + (i || '');\n      var header = obj[key];\n      if (header) {\n        filter = filter || {};\n        filter[key] = header;\n        var value = 'value' + (i || '');\n        filter[value] = obj[value] || '';\n      }\n    }\n    if (obj.ip) {\n      filter = filter || {};\n      filter.ip = obj.ip;\n    }\n    if (filter && filter.name && obj.mtype === 'exact') {\n      filter.mtype = 1;\n    }\n    if (!inited && obj.clearNetwork === 'true') {\n      clearNetwork = true;\n    }\n  }\n  exports.hashFilterObj = filter;\n  if (e && !compareFilter(filter)) {\n    events.trigger('hashFilterChange');\n  }\n  hashFilterObj = filter;\n}\nhandleHashFilterChanged();\n$(window).on('hashchange', handleHashFilterChanged);\n\nfunction setFilterText(settings) {\n  settings = settings || {};\n  storage.set(\n    'filterText',\n    JSON.stringify({\n      disabledFilterText: settings.disabledFilterText,\n      filterText: settings.filterText,\n      disabledExcludeText: settings.disabledExcludeText,\n      excludeText: settings.excludeText\n    })\n  );\n}\nexports.setFilterText = setFilterText;\n\nfunction getFilterText() {\n  var settings = util.parseJSON(storage.get('filterText'));\n  return settings\n    ? {\n      disabledFilterText: settings.disabledFilterText,\n      filterText: util\n          .toString(settings.filterText)\n          .substring(0, MAX_INCLUDE_LEN),\n      disabledExcludeText: settings.disabledExcludeText,\n      excludeText: util\n          .toString(settings.excludeText)\n          .substring(0, MAX_EXCLUDE_LEN)\n    }\n    : {\n      filterText: '',\n      excludeText: ''\n    };\n}\nexports.getFilterText = getFilterText;\n\nfunction setNetworkColumns(settings) {\n  settings = settings || {};\n  storage.set(\n    'networkColumns',\n    JSON.stringify({\n      columns: settings.columns\n    })\n  );\n}\n\nexports.setNetworkColumns = setNetworkColumns;\n\nfunction getNetworkColumns() {\n  return util.parseJSON(storage.get('networkColumns')) || {};\n}\n\nexports.getNetworkColumns = getNetworkColumns;\n\nvar FILTER_TYPES_RE = /^(m|i|h|b|c|d|H):/;\nvar FILTER_TYPES = {\n  m: 'method',\n  i: 'ip',\n  h: 'headers',\n  b: 'body',\n  c: 'body',\n  d: 'host',\n  H: 'host'\n};\nvar filterCache = [];\n\nfunction getFilterCache(text) {\n  var len = filterCache.length;\n  var result = len\n    ? util.findArray(filterCache, function (item) {\n      return item.text === text;\n    })\n    : null;\n  len -= 10;\n  if (len > 2) {\n    filterCache = filterCache.slice(len);\n    if (result && filterCache.indexOf(result) === -1) {\n      filterCache.push(result);\n    }\n  }\n  return result && result.filter;\n}\n\nfunction resolveFilterText(text) {\n  text = text && text.trim();\n  if (!text) {\n    return;\n  }\n  var result = getFilterCache(text);\n  if (result) {\n    return result;\n  }\n  var pattern;\n  text.split(/\\s+/).forEach(function (line) {\n    if (FILTER_TYPES_RE.test(line)) {\n      var type = FILTER_TYPES[RegExp.$1];\n      var not = line[2] === '!';\n      line = line.substring(not ? 3 : 2);\n      if (line) {\n        result = result || [];\n        pattern = util.toRegExp(line);\n        result.push({\n          type: type,\n          not: not,\n          pattern: pattern,\n          keyword: pattern ? null : line.toLowerCase()\n        });\n      }\n    } else if (line) {\n      result = result || [];\n      pattern = util.toRegExp(line);\n      result.push({\n        pattern: pattern,\n        keyword: pattern ? null : line.toLowerCase()\n      });\n    }\n  });\n  if (result) {\n    filterCache.push({\n      text: text,\n      filter: result\n    });\n  }\n  return result;\n}\n\nfunction checkFilterField(str, filter, needDecode) {\n  if (!str) {\n    return false;\n  }\n  var result;\n  if (filter.pattern) {\n    result = filter.pattern.test(str);\n  } else {\n    if (needDecode) {\n      try {\n        var text = decodeURIComponent(str);\n        if (text !== str) {\n          str += '\\n' + text;\n        }\n      } catch (e) {}\n    }\n    result = toLowerCase(str).indexOf(filter.keyword) !== -1;\n  }\n  return filter.not ? !result : result;\n}\n\nfunction checkFilter(item, list) {\n  for (var i = 0, len = list.length; i < len; i++) {\n    var filter = list[i];\n    switch (filter.type) {\n    case 'method':\n      if (checkFilterField(item.req.method, filter)) {\n        return true;\n      }\n      break;\n    case 'ip':\n      if (checkFilterField(item.req.ip || '127.0.0.1', filter)) {\n        return true;\n      }\n      break;\n    case 'headers':\n      if (\n          checkFilterField(util.objectToString(item.req.headers), filter, true)\n        ) {\n        return true;\n      }\n      break;\n    case 'host':\n      if (\n          checkFilterField(\n            item.isHttps ? item.url : util.getHost(item.url),\n            filter\n          )\n        ) {\n        return true;\n      }\n      break;\n    case 'body':\n      if (checkFilterField(util.getBody(item.req, true), filter)) {\n        return true;\n      }\n      break;\n    default:\n      if (\n          checkFilterField((item.isHttps ? 'tunnel://' : '') + item.url, filter)\n        ) {\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nvar POST_CONF = $.extend(\n  {\n    type: 'post'\n  },\n  DEFAULT_CONF\n);\nvar GET_CONF = $.extend(\n  {\n    cache: false\n  },\n  DEFAULT_CONF\n);\nvar cgi = createCgiObj(\n  {\n    getData: 'cgi-bin/get-data',\n    getInitial: 'cgi-bin/init'\n  },\n  GET_CONF\n);\n\nexports.createCgi = function (url, cancel, post) {\n  return createCgi(\n    {\n      url: url,\n      mode: cancel ? 'cancel' : null\n    },\n    post ? POST_CONF : GET_CONF\n  );\n};\n\nfunction toLowerCase(str) {\n  return String(str == null ? '' : str)\n    .trim()\n    .toLowerCase();\n}\n\nvar certs = createCgiObj(\n  {\n    remove: 'cgi-bin/certs/remove',\n    active: {\n      url:'cgi-bin/certs/active',\n      contentType: 'application/json'\n    },\n    upload: {\n      url: 'cgi-bin/certs/upload',\n      contentType: 'application/json'\n    },\n    all: {\n      url: 'cgi-bin/certs/all',\n      type: 'get'\n    }\n  },\n  POST_CONF\n);\n\nexports.certs = certs;\n\nexports.uploadCerts = function (data, cb) {\n  if (typeof data !== 'string') {\n    data = JSON.stringify(data);\n  }\n  return certs.upload(data, function (data, xhr) {\n    if (!data) {\n      return util.showSystemError(xhr);\n    }\n    if (typeof cb === 'function') {\n      cb(data);\n    } else {\n      events.trigger('showCustomCerts');\n    }\n  });\n};\n\nexports.values = createCgiObj(\n  {\n    recycleList: {\n      type: 'get',\n      url: 'cgi-bin/values/recycle/list'\n    },\n    recycleView: {\n      type: 'get',\n      url: 'cgi-bin/values/recycle/view'\n    },\n    recycleRemove: 'cgi-bin/values/recycle/remove',\n    moveTo: {\n      mode: 'chain',\n      url: 'cgi-bin/values/move-to'\n    },\n    list: {\n      type: 'get',\n      url: 'cgi-bin/values/list'\n    },\n    add: 'cgi-bin/values/add',\n    remove: 'cgi-bin/values/remove',\n    rename: 'cgi-bin/values/rename'\n  },\n  POST_CONF\n);\n\nexports.plugins = createCgiObj(\n  {\n    disablePlugin: 'cgi-bin/plugins/disable-plugin',\n    disableAllPlugins: 'cgi-bin/plugins/disable-all-plugins',\n    getRegistryList: 'cgi-bin/plugins/registry-list',\n    installPlugins: 'cgi-bin/plugins/install',\n    uninstallPlugins: 'cgi-bin/plugins/uninstall',\n    addRegistry: 'cgi-bin/plugins/add-registry'\n  },\n  POST_CONF\n);\n\nexports.installPluginsFromService = function (plugins, registry) {\n  if (!plugins || !exports.whistleId) {\n    return;\n  }\n  plugins = util.isString(plugins) ? plugins.trim().split(/\\s*,\\s*/) : (Array.isArray(plugins) ? plugins : []);\n  plugins = plugins.map(function(p) {\n    return p.indexOf('/') === -1 ? exports.whistleId + '/' + p : p;\n  }).join();\n  if (!plugins) {\n    return;\n  }\n  exports.plugins.installPlugins({\n    registry: /^https?:\\/\\/[^/]/.test(registry) ? registry : '',\n    plugins: plugins\n  }, util.showHandlePluginInfo);\n};\n\nexports.rules = createCgiObj(\n  {\n    disableAllRules: 'cgi-bin/rules/disable-all-rules',\n    recycleList: {\n      type: 'get',\n      url: 'cgi-bin/rules/recycle/list'\n    },\n    recycleView: {\n      type: 'get',\n      url: 'cgi-bin/rules/recycle/view'\n    },\n    recycleRemove: 'cgi-bin/rules/recycle/remove',\n    moveTo: {\n      mode: 'chain',\n      url: 'cgi-bin/rules/move-to'\n    },\n    getEnabledRules: {\n      url: 'cgi-bin/rules/enabled',\n      mode: 'cancel'\n    },\n    list: {\n      type: 'get',\n      url: 'cgi-bin/rules/list'\n    },\n    add: 'cgi-bin/rules/add',\n    disableDefault: 'cgi-bin/rules/disable-default',\n    enableDefault: 'cgi-bin/rules/enable-default',\n    remove: 'cgi-bin/rules/remove',\n    rename: 'cgi-bin/rules/rename',\n    select: 'cgi-bin/rules/select',\n    unselect: 'cgi-bin/rules/unselect',\n    allowMultipleChoice: {\n      mode: 'ignore',\n      url: 'cgi-bin/rules/allow-multiple-choice'\n    },\n    enableBackRulesFirst: {\n      mode: 'ignore',\n      url: 'cgi-bin/rules/enable-back-rules-first'\n    },\n    setSysHosts: 'cgi-bin/rules/set-sys-hosts'\n  },\n  POST_CONF\n);\n\n\nexports.log = createCgiObj(\n  {\n    set: 'cgi-bin/log/set'\n  },\n  POST_CONF\n);\n\nvar COMPOSE_CONF = $.extend(\n  {\n    type: 'post',\n    contentType: 'application/json',\n    processData: false\n  },\n    DEFAULT_CONF\n  );\n\nfunction createCompose(cancel) {\n  return createCgiObj( {\n    _: {\n      url: 'cgi-bin/composer',\n      mode: cancel ? 'cancel' : null\n    }\n  }, COMPOSE_CONF)._;\n}\n\nexports.createCompose = createCompose;\n\nvar composeParallel = createCompose();\nvar composeInner = createCompose(true);\n\nfunction handleCompose(data, cb, options, handler) {\n  if (typeof data !== 'string') {\n    data = JSON.stringify(data);\n  }\n  return handler(data, cb, options);\n}\n\nexports.composeInner = function (data, cb, options) {\n  data.reqId = getReqId();\n  return handleCompose(data, cb, options, composeInner);\n};\n\nexports.createComposeInterrupt = function () {\n  var composeInterrupt = createCompose(true);\n  return function (data, cb, options) {\n    return handleCompose(data, cb, options, composeInterrupt);\n  };\n};\n\nexports.compose = function (data, cb, options) {\n  return handleCompose(data, cb, options, composeParallel);\n};\n\n$.extend(\n  exports,\n  createCgiObj(\n    {\n      interceptHttpsConnects: 'cgi-bin/intercept-https-connects',\n      enableHttp2: 'cgi-bin/enable-http2',\n      abort: 'cgi-bin/abort',\n      setCustomColumn: 'cgi-bin/set-custom-column',\n      addRulesAndValues: {\n        url: 'cgi-bin/add-rules-values',\n        contentType: 'application/json'\n      },\n      createTempFile: {\n        url: 'cgi-bin/temp/create',\n        contentType: 'application/json'\n      },\n      saveSessions: {\n        url: 'cgi-bin/saved/save',\n        contentType: 'application/json'\n      },\n      removeSavedSessions: {\n        url: 'cgi-bin/saved/remove',\n        contentType: 'application/json'\n      },\n      setDnsOrder: 'cgi-bin/set-dns-order',\n      save: {\n        url: 'cgi-bin/service/save',\n        contentType: 'application/json'\n      },\n      login: 'cgi-bin/service/login',\n      logout: 'cgi-bin/service/logout',\n      updateClient: 'cgi-bin/update'\n    },\n    POST_CONF\n  )\n);\n$.extend(\n  exports,\n  createCgiObj(\n    {\n      donotShowAgain: 'cgi-bin/do-not-show-again',\n      checkUpdate: 'cgi-bin/check-update',\n      importRemote: 'cgi-bin/import-remote',\n      getHistory: 'cgi-bin/history',\n      getCookies: 'cgi-bin/cookies',\n      getTempFile: 'cgi-bin/temp/get',\n      getComposeData: 'cgi-bin/compose-data',\n      getSavedList: 'cgi-bin/saved/list',\n      getSavedSessions: 'cgi-bin/saved/sessions'\n    },\n    GET_CONF\n  )\n);\n\nvar getSavedList = exports.getSavedList;\nvar savedListIndex = 0;\nvar loadedSavedListIndex = -1;\nvar saveDataTimer;\nfunction getSavedListSafe (cb) {\n  var index = ++savedListIndex;\n  clearTimeout(saveDataTimer);\n  saveDataTimer = null;\n  return getSavedList(function (data) {\n    ++loadedSavedListIndex;\n    if (index < loadedSavedListIndex) {\n      return;\n    }\n    if (index < savedListIndex || (data && !data.ec)) {\n      return data && !data.ec && cb(data.list);\n    }\n    saveDataTimer = saveDataTimer || setTimeout(function() {\n      getSavedListSafe(cb);\n    }, 160);\n  });\n}\n\nexports.getSavedListSafe = getSavedListSafe;\n\nexports.socket = $.extend(\n  createCgiObj(\n    {\n      changeStatus: {\n        mode: 'cancel',\n        url: 'cgi-bin/socket/change-status'\n      },\n      abort: {\n        mode: 'ignore',\n        url: 'cgi-bin/socket/abort'\n      },\n      send: {\n        mode: 'ignore',\n        url: 'cgi-bin/socket/data'\n      }\n    },\n    POST_CONF\n  )\n);\n\nfunction updateCertStatus(data) {\n  if (exports.hasInvalidCerts != data.hasInvalidCerts) {\n    exports.hasInvalidCerts = data.hasInvalidCerts;\n    events.trigger('updateUI');\n  }\n}\n\nexports.getReqTabs = function () {\n  return reqTabList;\n};\n\nexports.getResTabs = function () {\n  return resTabList;\n};\n\nexports.getTabs = function () {\n  return tabList;\n};\n\nexports.getToolTabs = function() {\n  return toolTabList;\n};\n\nexports.getComTabs = function () {\n  return comTabList;\n};\n\nexports.getAccount = function() {\n  return account;\n};\n\nfunction filterComposeData(key) {\n  return composeDataMap[key];\n}\n\nfunction getComposeData(keys, cb) {\n  return exports.getComposeData({ ids: keys.join() }, cb);\n}\n\nvar INTERVAL = 1500;\n\nfunction loadComposeData(keys) {\n  var len = keys && keys.length;\n  if (len) {\n    keys = keys.filter(filterComposeData);\n    len = keys.length;\n  } else {\n    keys = Object.keys(composeDataMap);\n    len = keys.length;\n  }\n  if (!len) {\n    return setTimeout(loadComposeData, INTERVAL);\n  }\n  var curKeys = keys.slice(0, 5);\n  keys = keys.slice(5);\n  getComposeData(curKeys, function (data) {\n    if (data) {\n      curKeys.forEach(function(key) {\n        var base64 = data[key];\n        if (base64 === '') {\n          return;\n        }\n        var list = composeDataMap[key];\n        if (base64 == null) {\n          delete composeDataMap[key];\n        }\n        list && list.forEach(function(cb) {\n          cb(base64 || '');\n        });\n      });\n    }\n    setTimeout(function() {\n      loadComposeData(keys);\n    }, INTERVAL);\n  });\n}\n\nexports.getInitialData = function (callback) {\n  if (!initialDataPromise) {\n    initialDataPromise = $.Deferred();\n\n    var load = function () {\n      cgi.getInitial(function (data) {\n        if (!data) {\n          return setTimeout(load, 1000);\n        }\n        loadComposeData();\n        exports.isCapture = !!data.interceptHttpsConnects;\n        var server = data.server;\n        port = server && server.port;\n        account = server && server.account;\n        updateWhistleId(server);\n        hasUpdater = server && server.hasUpdater;\n        exports.version = server && server.version;\n        exports.supportH2 = data.supportH2;\n        exports.isWin = server && server.isWin;\n        updateRulesInfo(data.rules);\n        exports.custom1 = data.custom1;\n        exports.custom2 = data.custom2;\n        exports.custom1Key = data.custom1Key;\n        exports.custom2Key = data.custom2Key;\n        initialData = data;\n        clientId = data.clientId;\n        DEFAULT_CONF.data.clientId = clientId;\n        if (data.lastLogId) {\n          lastPageLogTime = data.lastLogId;\n        }\n        if (data.lastSvrLogId) {\n          lastSvrLogTime = data.lastSvrLogId;\n        }\n        curLogId = data.curLogId;\n        curSvrLogId = data.curSvrLogId;\n        if (data.lastDataId) {\n          lastRowId = data.lastDataId;\n        }\n        exports.whistleName = data.wName;\n        exports.account = data.account;\n        exports.disableInstaller = data.disableInstaller;\n        exports.upload = createCgiObj(\n          {\n            importSessions: 'cgi-bin/sessions/import?clientId=' + clientId,\n            importRules: 'cgi-bin/rules/import?clientId=' + clientId,\n            importValues: 'cgi-bin/values/import?clientId=' + clientId\n          },\n          $.extend(\n            {\n              type: 'post'\n            },\n            DEFAULT_CONF,\n            {\n              contentType: false,\n              processData: false,\n              timeout: LONG_TIMEOUT\n            }\n          )\n        );\n        initialDataPromise.resolve(data);\n        if (data.clientIp) {\n          exports.clientIp = data.clientIp;\n        }\n        updateCertStatus(data);\n      });\n    };\n    load();\n  }\n\n  initialDataPromise.done(callback);\n};\n\nfunction checkDataChanged(data, mclientName, mtimeName) {\n  if (!data[mtimeName] || initialData.clientId === data[mclientName]) {\n    return false;\n  }\n\n  var mclient = data[mclientName];\n  var mtime = data[mtimeName];\n  if (\n    initialData[mclientName] === mclient &&\n    initialData[mtimeName] === mtime\n  ) {\n    return false;\n  }\n  initialData[mclientName] = mclient;\n  initialData[mtimeName] = mtime;\n  return true;\n}\n\nfunction emitRulesChanged(data) {\n  if (checkDataChanged(data, 'mrulesClientId', 'mrulesTime')) {\n    events.trigger('rulesChanged');\n  }\n}\n\nfunction emitValuesChanged(data) {\n  if (checkDataChanged(data, 'mvaluesClientId', 'mvaluesTime')) {\n    events.trigger('valuesChanged');\n  }\n}\n\nfunction checkTabList(list1, list2, len) {\n  for (var i = 0; i < len; i++) {\n    var tab1 = list1[i];\n    var tab2 = list2[i];\n    if (tab1.name !== tab2.name || tab1.action !== tab2.action) {\n      return true;\n    }\n  }\n}\n\nfunction hasPluginColsChange(curCols, oldClos) {\n  var len = curCols.length;\n  if (len !== oldClos.length) {\n    return true;\n  }\n  for (var i = 0; i < len; i++) {\n    var cur = curCols[i];\n    var old = oldClos[i];\n    if (cur.title !== old.title || cur.key !== old.key ||\n      cur.iconKey !== old.iconKey || cur.width !== old.width) {\n      return true;\n    }\n  }\n}\n\nfunction emitCustomTabsChange(curList, oldList, name) {\n  var curLen = curList.length;\n  var oldLen = oldList.length;\n  if (!curLen) {\n    oldLen && events.trigger(name);\n    return;\n  }\n  if (curLen === 1) {\n    if (\n      !oldLen ||\n      oldLen > 1 ||\n      curList[0].name !== oldList[0].name ||\n      curList[0].action !== oldList[0].action\n    ) {\n      events.trigger(name);\n    }\n    return;\n  }\n  if (!oldLen || oldLen === 1) {\n    return events.trigger(name);\n  }\n  if (curLen !== oldLen || checkTabList(curList, oldList, curLen)) {\n    events.trigger(name);\n  }\n}\n\nfunction getBase64Len(base64) {\n  if (!base64) {\n    return 0;\n  }\n  var len = base64.length;\n  if (base64[len - 1] === '=') {\n    len -= 2;\n    if (base64[len] === '=') {\n      --len;\n    }\n  }\n  return len;\n}\n\nfunction updateItem(item, newItem) {\n  Object.keys(newItem).forEach(function(key) {\n    var data = newItem[key];\n    if (key === 'rules' || key === 'rulesHeaders' || key === 'url') {\n      data = data || item[key];\n    } else if (key === 'req' || key === 'res') {\n      var oldData = item[key];\n      var base64 = oldData.base64;\n      data.headers = data.headers || oldData.headers;\n      data.rawHeaderNames = data.rawHeaderNames || oldData.rawHeaderNames;\n      if (data.base64) {\n        if (data.preLen > 0) {\n          data.base64 = (base64 ? base64.substring(0, data.preLen) : '') + data.base64;\n        }\n      } else {\n        data.base64 = base64;\n        data[util.BODY_KEY] = oldData[util.BODY_KEY];\n        data[util.HEX_KEY] = oldData[util.HEX_KEY];\n        data[util.JSON_KEY] = oldData[util.JSON_KEY];\n      }\n    }\n    item[key] = data;\n  });\n}\n\nfunction getStatus(item) {\n  var result = [''];\n  // 跟后台联动，不能改成 &\n  if (!item.requestTime) {\n    result[0] = getBase64Len(item.req.base64);\n  }\n  if (!item.endTime) {\n    result[1] = getBase64Len(item.res.base64);\n  }\n  return result.join('-');\n}\n\nfunction getComposerItem() {\n  var elem = document.querySelector('#whistleComposerFrames');\n  return elem && elem.offsetWidth ? composerItem : null;\n}\n\nvar hiddenTime = Date.now();\nfunction startLoadData() {\n  if (startedLoad) {\n    return;\n  }\n  startedLoad = true;\n  function load() {\n    if (document.hidden) {\n      if (Date.now() - hiddenTime > MAX_WAIT_TIME) {\n        return setTimeout(load, 100);\n      }\n    } else {\n      hiddenTime = Date.now();\n    }\n\n    if (networkModal.clearNetwork) {\n      lastRowId = endId || lastRowId;\n      networkModal.clearNetwork = false;\n    }\n\n    var startTime = getStartTime();\n    var len = logList.length;\n    var svrLen = svrLogList.length;\n    var startLogTime = -1;\n    var startSvrLogTime = -1;\n    var pendingIds = [];\n    var statusIds = [];\n    var tunnelIds = [];\n    dataList.forEach(function (item) {\n      if (!item.endTime && !item.lost) {\n        pendingIds.push(item.id);\n        statusIds.push(getStatus(item));\n      }\n      if (item.reqPlugin > 0 && item.reqPlugin < 10) {\n        ++item.reqPlugin;\n        tunnelIds.push(item.id);\n      }\n    });\n    var clearedLogs = exports.clearedLogs;\n    var clearedSvrLogs = exports.clearedSvrLogs;\n    exports.clearedLogs = exports.clearedSvrLogs = false;\n    if (!exports.pauseConsoleRefresh && len < MAX_LOG_LENGTH) {\n      startLogTime = (clearedLogs && curLogId) || lastPageLogTime;\n    }\n\n    if (!exports.pauseServerLogRefresh && svrLen < MAX_LOG_LENGTH) {\n      startSvrLogTime =  (clearedSvrLogs && curSvrLogId) || lastSvrLogTime;\n    }\n\n    var curActiveItem = getComposerItem() || networkModal.getActive();\n    var curFrames = curActiveItem && curActiveItem.frames;\n    var lastFrameId, curReqId;\n    if (curFrames && !curActiveItem.pauseRecordFrames) {\n      if (curActiveItem.stopRecordFrames) {\n        curReqId = curActiveItem.id;\n        lastFrameId = -3;\n      } else if (curFrames.length <= MAX_FRAMES_LENGTH) {\n        curReqId = curActiveItem.id;\n        lastFrameId = curActiveItem.lastFrameId;\n      }\n    }\n    var count = inited ? 20 : networkModal.getDisplayCount();\n    var composerReqId = exports.curComposerReqId;\n    var options = {\n      startLogTime: exports.stopConsoleRefresh ? -3 : startLogTime,\n      startSvrLogTime: exports.stopServerLogRefresh ? -3 : startSvrLogTime,\n      ids: pendingIds.join(),\n      status: statusIds.join(),\n      startTime: startTime,\n      dumpCount: dumpCount,\n      lastRowId: inited || !count ? lastRowId : undefined,\n      curReqId: curReqId,\n      lastFrameId: lastFrameId,\n      logId: logId || '',\n      count: count || 20,\n      tunnelIds: tunnelIds,\n      composerReqId: composerReqId\n    };\n    inited = true;\n    $.extend(options, hashFilterObj);\n    if (onlyViewOwnData) {\n      options.ip = 'self';\n    }\n    cgi.getData(options, function (data) {\n      var hasNewData = data && data.data && data.data.hasNew;\n      setTimeout(load, hasNewData ? 100 : 900);\n      updateServerInfo(data);\n      if (!data || data.ec !== 0) {\n        return;\n      }\n      if (Array.isArray(data.installErrors) && data.installErrors.length) {\n        message.error(data.installErrors.join('\\n'));\n      }\n      var preCapture = exports.isCapture;\n      if (preCapture === 0) {\n        exports.isCapture = false;\n      } else if (preCapture === 1) {\n        exports.isCapture = true;\n      } else {\n        var capture = !!data.interceptHttpsConnects;\n        if (exports.isCapture !== capture) {\n          exports.isCapture = capture;\n          events.trigger('reqTabsChange');\n          events.trigger('resTabsChange');\n        }\n      }\n      var server = data.server;\n      port = server && server.port;\n      account = server && server.account;\n      hasUpdater = server && server.hasUpdater;\n      updateWhistleId(server);\n      exports.whistleName = data.wName;\n      exports.account = data.account;\n      exports.disableInstaller = data.disableInstaller;\n      exports.supportH2 = data.supportH2;\n      exports.version = server && server.version;\n      exports.isWin = server && server.isWin;\n      updateRulesInfo(data);\n      exports.custom1 = data.custom1;\n      exports.custom2 = data.custom2;\n      exports.custom1Key = data.custom1Key;\n      exports.custom2Key = data.custom2Key;\n      if (options.dumpCount > 0) {\n        dumpCount = 0;\n      }\n      if (data.clientIp) {\n        exports.clientIp = data.clientIp;\n      }\n      updateCertStatus(data);\n      emitRulesChanged(data);\n      emitValuesChanged(data);\n      directCallbacks.forEach(function (cb) {\n        cb(data);\n      });\n      var len = data.log.length;\n      var svrLen = data.svrLog.length;\n      var _reqTabList = reqTabList;\n      var _resTabList = resTabList;\n      var _tabList = tabList;\n      var _comTabList = comTabList;\n      var _toolTabList = toolTabList;\n      var _pluginCols = [];\n      var _workers = [];\n      var hasWorkerChanged;\n      var curTabList = [];\n      pluginsMap = data.plugins || {};\n      disabledPlugins = data.disabledPlugins || {};\n      if (!disabledAllPlugins) {\n        Object.keys(pluginsMap).forEach(function (name) {\n          var pluginName = name.slice(0, -1);\n          if (!disabledPlugins[pluginName]) {\n            var plugin = pluginsMap[name];\n            curTabList.push({\n              mtime: plugin.mtime,\n              priority: plugin.priority,\n              _key: name,\n              plugin: pluginName,\n              reqTab: plugin.reqTab,\n              resTab: plugin.resTab,\n              tab: plugin.tab,\n              comTab: plugin.comTab,\n              toolTab: plugin.toolTab,\n              col: plugin.networkColumn\n            });\n            var worker = plugin.webWorker;\n            if (worker && _workers.indexOf(worker) === -1) {\n              _workers.push(worker);\n              hasWorkerChanged = hasWorkerChanged ||  webWorkerList.indexOf(worker) === -1;\n            }\n          }\n        });\n      }\n      curTabList.sort(util.comparePlugin);\n      reqTabList = [];\n      resTabList = [];\n      tabList = [];\n      comTabList = [];\n      toolTabList = [];\n      dataKeys = [];\n      curTabList.forEach(function (info) {\n        var reqTab = info.reqTab;\n        var resTab = info.resTab;\n        var tab = info.tab;\n        var toolTab = info.toolTab;\n        var comTab = info.comTab;\n        var plugin = info.plugin;\n        var col = info.col;\n        if (reqTab) {\n          reqTab.plugin = plugin;\n          reqTabList.push(reqTab);\n        }\n        if (resTab) {\n          resTab.plugin = plugin;\n          resTabList.push(resTab);\n        }\n        if (tab) {\n          tab.plugin = plugin;\n          tabList.push(tab);\n        }\n        if (comTab) {\n          comTab.plugin = plugin;\n          comTabList.push(comTab);\n        }\n        if (toolTab) {\n          toolTab.plugin = plugin;\n          toolTabList.push(toolTab);\n        }\n        if (col) {\n          col.name = col.className = 'whistle.' + info.plugin;\n          col.isPlugin = true;\n          _pluginCols.push(col);\n          dataKeys.push(col.key);\n        }\n      });\n      emitCustomTabsChange(reqTabList, _reqTabList, 'reqTabsChange');\n      emitCustomTabsChange(resTabList, _resTabList, 'resTabsChange');\n      emitCustomTabsChange(tabList, _tabList, 'tabsChange');\n      emitCustomTabsChange(comTabList, _comTabList, 'comTabsChange');\n      emitCustomTabsChange(toolTabList, _toolTabList, 'toolTabsChange');\n      if (hasPluginColsChange(_pluginCols, pluginColumns)) {\n        pluginColumns = _pluginCols;\n        events.trigger('pluginColumnsChange');\n      }\n      if (hasWorkerChanged || webWorkerList.length !== _workers.length) {\n        webWorkerList = _workers;\n        updateWorkers(webWorkerList);\n      }\n      disabledAllPlugins = data.disabledAllPlugins;\n      if (len || svrLen) {\n        if (len) {\n          logList.push.apply(logList, data.log);\n          lastPageLogTime = data.log[len - 1].id;\n        }\n\n        if (svrLen) {\n          svrLogList.push.apply(svrLogList, data.svrLog);\n          lastSvrLogTime = data.svrLog[svrLen - 1].id;\n        }\n\n        logCallbacks.forEach(function (cb) {\n          cb(logList, svrLogList);\n        });\n      }\n      if (data.lastLogId) {\n        lastPageLogTime = data.lastLogId;\n      }\n      if (data.lastSvrLogId) {\n        lastSvrLogTime = data.lastSvrLogId;\n      }\n      data = data.data;\n      var hasChanged;\n      var framesLen = data.frames && data.frames.length;\n      var time = data.composerTime;\n\n      if (time && composerReqId === exports.curComposerReqId && exports.onTakeTimeChange) {\n        if (time.endTime) {\n          exports.curComposerReqId = undefined;\n        }\n        exports.onTakeTimeChange(time);\n      }\n\n      if (framesLen) {\n        curActiveItem.lastFrameId = data.frames[framesLen - 1].frameId;\n        curFrames.push.apply(curFrames, data.frames);\n      } else if (data.lastFrameId) {\n        curActiveItem.lastFrameId = data.lastFrameId;\n      }\n      if (curReqId) {\n        var status = data.socketStatus;\n        if (status) {\n          curActiveItem.closed = undefined;\n          if (status.sendStatus > -1) {\n            hasChanged = curActiveItem.sendStatus !== status.sendStatus;\n            curActiveItem.sendStatus = status.sendStatus;\n          }\n          if (status.receiveStatus > -1) {\n            hasChanged =\n              hasChanged ||\n              curActiveItem.receiveStatus !== status.receiveStatus;\n            curActiveItem.receiveStatus = status.receiveStatus;\n          }\n        } else {\n          if (!curActiveItem.closed) {\n            hasChanged = true;\n            curActiveItem.closed = true;\n          }\n        }\n      }\n      if (data.lastId) {\n        lastRowId = data.lastId;\n      }\n      if (data.endId) {\n        endId = data.endId;\n      }\n      var tunnelIps = data.tunnelIps || '';\n      if (\n        (!data.ids.length && !data.newIds.length) ||\n        networkModal.clearNetwork\n      ) {\n        if (hasChanged || framesLen) {\n          framesUpdateCallbacks.forEach(function (cb) {\n            cb();\n          });\n        }\n        if (Object.keys(tunnelIps).length) {\n          var hasNewIp;\n          dataList.forEach(function (item) {\n            var realIp = tunnelIps[item.id];\n            if (realIp) {\n              delete item.reqPlugin;\n              item.realIp = realIp;\n              hasNewIp = true;\n            }\n          });\n          hasNewIp && events.trigger('updateUI');\n        }\n        return;\n      }\n      var ids = data.newIds;\n      var curHLList = [];\n      data = data.data;\n      dataList.forEach(function (item) {\n        var newItem = data[item.id];\n        if (newItem) {\n          updateItem(item, newItem);\n          setReqData(item);\n          workers.postMessage(item);\n        } else {\n          item.lost = true;\n          if (!item.endTime) {\n            workers.postMessage(item);\n          }\n        }\n        var realIp = tunnelIps[item.id];\n        if (realIp) {\n          delete item.reqPlugin;\n          item.realIp = realIp;\n        }\n      });\n      if (ids.length) {\n        var filter = getFilterText();\n        var excludeFilter = filter.disabledExcludeText\n          ? null\n          : resolveFilterText(filter.excludeText);\n        var includeFilter = filter.disabledFilterText\n          ? null\n          : resolveFilterText(filter.filterText);\n        exports.curNewIdList = ids.filter(function (id) {\n          var item = data[id];\n          if (item) {\n            workers.postMessage(item);\n            if (item.fc) {\n              curHLList.push(item);\n              item.highlight = true;\n            }\n            if (\n              (!excludeFilter || !checkFilter(item, excludeFilter)) &&\n              (!includeFilter || checkFilter(item, includeFilter))\n            ) {\n              setReqData(item);\n              dataList.push(item);\n              return true;\n            }\n          }\n        });\n        if (curHLList.length) {\n          setTimeout(function() {\n            curHLList.forEach(function(item) {\n              delete item.highlight;\n            });\n          }, 800);\n        }\n      }\n      dataCallbacks.forEach(function (cb) {\n        cb(networkModal);\n      });\n    });\n  }\n  load();\n}\n\nfunction getRawHeaders(headers, rawHeaderNames) {\n  if (!headers || !rawHeaderNames) {\n    return;\n  }\n  var rawHeaders = {};\n  Object.keys(headers).forEach(function (name) {\n    rawHeaders[rawHeaderNames[name] || name] = headers[name];\n  });\n  return rawHeaders;\n}\n\nexports.getRawHeaders = getRawHeaders;\n\nwindow.getWhistlePageId = window.getWhistleClientId = function () {\n  return clientId;\n};\n\nfunction getReqId() {\n  return clientId + '/' + (reqIndex++);\n}\n\nexports.getReqId = getReqId;\n\nexports.onComposeData = function(reqId, cb) {\n  var index = util.isString(reqId) && typeof cb === 'function' ? reqId.indexOf(clientId + '/') : -1;\n  if (index) {\n    return;\n  }\n  index = reqId.substring(clientId.length + 1);\n  if (/[^\\d]/.test(index) || !(index >= 0 && index < reqIndex)) {\n    return;\n  }\n  var list = composeDataMap[reqId] || [];\n  if (list.indexOf(cb) === -1) {\n    list.push(cb);\n  }\n  composeDataMap[reqId] = list;\n  return true;\n};\n\nexports.offComposeData = function(reqId, cb) {\n  var list = composeDataMap[reqId];\n  if (list) {\n    if (cb) {\n      var index = list.indexOf(cb);\n      if (index !== -1) {\n        list.splice(index, 1);\n      }\n    } else {\n      delete composeDataMap[reqId];\n    }\n  }\n};\n\nexports.getPageId = function () {\n  return clientId;\n};\n\nfunction isFrames(item) {\n  if (!item) {\n    return false;\n  }\n  if (item.useFrames) {\n    return true;\n  }\n  if (item.reqError || item.resError) {\n    return false;\n  }\n  var status = item.res.statusCode;\n  if (/^wss?:\\/\\//.test(item.url)) {\n    return status == 101;\n  }\n  return item.inspect && status == 200;\n}\n\nexports.isFrames = isFrames;\n\nfunction getStyleValue(style) {\n  var index = style.indexOf('&');\n  if (index !== -1) {\n    style = style.substring(0, index);\n  }\n  index = style.indexOf('!');\n  if (index !== -1) {\n    style = style.substring(0, index);\n  }\n  if (style[0] === '@') {\n    style = '#' + style.substring(1);\n  }\n  return style.length > 32 ? undefined : style;\n}\n\nfunction getCustomValue(style, isFirst) {\n  var index = style.lastIndexOf('&custom' + (isFirst ? '1=' : '2='));\n  if (index === -1) {\n    return;\n  }\n  style = style.substring(index + 9);\n  index = style.indexOf('&');\n  style = index === -1 ? style : style.substring(0, index);\n  if (style.indexOf('%') !== -1) {\n    try {\n      return decodeURIComponent(style);\n    } catch (e) {}\n  }\n  return style;\n}\n\nfunction setStyle(item) {\n  item.style = undefined;\n  var style = item.rules && item.rules.style;\n  style = style && style.list;\n  if (!style) {\n    return;\n  }\n  style =\n    '&' +\n    style\n      .map(function (rule) {\n        rule = rule.value || rule.matcher;\n        return rule.substring(rule.indexOf('://') + 3);\n      })\n      .join('&');\n  var color, fontStyle, bgColor;\n  var colorIndex = style.lastIndexOf('&color=');\n  if (colorIndex !== -1) {\n    color = getStyleValue(style.substring(colorIndex + 7));\n  }\n  var styleIndex = style.lastIndexOf('&fontStyle=');\n  if (styleIndex !== -1) {\n    fontStyle = getStyleValue(style.substring(styleIndex + 11));\n  }\n  var bgIndex = style.lastIndexOf('&bgColor=');\n  if (bgIndex !== -1) {\n    bgColor = getStyleValue(style.substring(bgIndex + 9));\n  }\n  if (color || fontStyle || bgColor) {\n    item.style = {\n      color: color,\n      fontStyle: fontStyle,\n      backgroundColor: bgColor\n    };\n  }\n  var key1 = exports.custom1Key;\n  var key2 = exports.custom2Key;\n  if (!util.notEStr(key1)) {\n    item.custom1 = getCustomValue(style, true);\n  }\n  if (!util.notEStr(key2)) {\n    item.custom2 = getCustomValue(style);\n  }\n}\n\nvar APPS = 'alipay,baidu,brave,chrome,mac,android,ipad,iphone,windows,crmo,crios,cicc,edge,electron,firefox,huawei,opera,jd,pdd,qq,safari,uc,wework,wechat,dingtalk,weibo'.split(',');\nvar APP_RE = /w[ex]work\\/|Alipay|Brave\\/|%e6%b7%98%e5%ae%9d\\/|opera|%e6%94%af%e4%bb%98%e5%ae%9d\\/|%e5%a4%a9%e7%8c%ab\\/|uc%e6%b5%8f%e8%a7%88%e5%99%a8\\/|pinduoduo|%e9%92%89%e9%92%89\\/|UCBrowser\\/|dingtalk|jd(?:4|mall)|weibo|tmall|qq\\/|Firefox\\/|FxiOS\\/|ciccwm\\/|WhistleClient\\/|edg(?:e|ios|a)?\\/|zztapp|baidu/i;\nvar COMMON_APP_RE = /MicroMessenger|taobao|amap_sdk|Electron\\/|CFNetwork\\/|cronet/i;\n\nfunction getAppName(ua) {\n  var result = ua && (APP_RE.exec(ua) || COMMON_APP_RE.exec(ua));\n  if (!result) {\n    if (ua) {\n      if (/\\b(?:chrome|crmo|crios)\\//i.test(ua) || /^Chrome\\s/.test(ua)) {\n        return 'chrome';\n      }\n      if (/Version\\//.test(ua) && /Safari\\//.test(ua)) {\n        return 'safari';\n      }\n      if (/Windows NT|Microsoft NCSI|Win64|Win32|Windows 10|Windows 11/i.test(ua)) {\n        return 'windows';\n      }\n      if (/android/i.test(ua)) {\n        return 'android';\n      }\n      if (/HarmonyOS|HMSCore|huawei/i.test(ua)) {\n        return 'huawei';\n      }\n      if (/iPad/i.test(ua) || (/Macintosh/i.test(ua) && /Mobile/i.test(ua))) {\n        return 'ipad';\n      }\n      if (/iPhone|iPod/i.test(ua)) {\n        return 'iphone';\n      }\n      if (/Macintosh/i.test(ua)) {\n        return 'mac';\n      }\n    }\n    return 'browser';\n  }\n  result = result[0].toLowerCase();\n  switch (result) {\n  case 'micromessenger':\n    return 'wechat';\n  case 'brave/':\n    return 'brave';\n  case 'qq/':\n    return 'qq';\n  case 'firefox/':\n  case 'fxios/':\n    return 'firefox';\n  case 'cfnetwork/':\n    return 'cfnetwork';\n  case 'ciccwm/':\n  case 'zztapp':\n    return 'cicc';\n  case 'wework/':\n  case 'wxwork/':\n    return 'wework';\n  case 'amap_sdk':\n    return 'amap';\n  case '%e6%94%af%e4%bb%98%e5%ae%9d/':\n    return 'alipay';\n  case 'electron/':\n    return 'electron';\n  case 'whistleclient/':\n    return 'whistle';\n  case '%e6%b7%98%e5%ae%9d/':\n    return 'taobao';\n  case '%e5%a4%a9%e7%8c%ab/':\n    return 'tmall';\n  case '%e9%92%89%e9%92%89/':\n    return 'dingtalk';\n  case 'ucbrowser/':\n  case 'uc%e6%b5%8f%e8%a7%88%e5%99%a8/':\n    return 'uc';\n  case 'pinduoduo':\n    return 'pdd';\n  case 'jd4':\n  case 'jdmall':\n    return 'jd';\n  case 'edg/':\n  case 'edge/':\n  case 'edga/':\n  case 'edgios/':\n    return 'edge';\n  default:\n    return result;\n  }\n}\n\nfunction setAppName(item) {\n  if (item.fc) {\n    item.appName = 'whistle';\n    return;\n  }\n  var appName = item.appName;\n  if (!appName || !APPS.indexOf(appName) !== -1) {\n    item.appName = getAppName(item.req.headers['user-agent']);\n  }\n}\n\nvar NOT_BOLD_RULES = {\n  plugin: 1,\n  pac: 1,\n  reqWrite: 1,\n  resWrite: 1,\n  reqWriteRaw: 1,\n  resWriteRaw: 1,\n  responseFor: 1,\n  style: 1,\n  G: 1,\n  ignore: 1\n};\n\nfunction hasRules(rules) {\n  var keys = rules && Object.keys(rules);\n  if (keys && keys.length) {\n    for (var i = 0, len = keys.length; i < len; i++) {\n      var rule = rules[keys[i]];\n      var enable = rule && rule.list && rule.list.length === 1 && rule.list[0].matcher;\n      if (rule && !NOT_BOLD_RULES[keys[i]] && enable !== 'enable://capture' &&  enable !== 'enable://intercept') {\n        return true;\n      }\n    }\n  }\n\n  return false;\n}\n\nfunction setReqData(item) {\n  var url = item.url;\n  var req = item.req;\n  var res = item.res;\n  item.method = req.method;\n  var end = item.endTime;\n  var defaultValue = end ? '' : '-';\n  var resHeaders = res.headers || '';\n  setAppName(item);\n  item.hostIp = res.ip || defaultValue;\n  item[HAS_RULES_KEY] = item[HAS_RULES_KEY] || hasRules(item.rules);\n  item.clientIp = req.ip || '127.0.0.1';\n  item.date = item.date || util.toLocaleString(new Date(item.startTime));\n  item.clientPort = req.port;\n  item.serverPort = item.res.port;\n  item.contentEncoding =\n    (resHeaders['content-encoding'] || '') +\n    (item.res.hasGzipError ? ' (Incorrect header)' : '');\n  var reqSize = req.size == null ? defaultValue : req.size;\n  var resSize = res.size == null ? defaultValue : res.size;\n  var reqSizeStr = util.getDisplaySize(reqSize, req.unzipSize);\n  var resSizeStr = util.getDisplaySize(resSize, res.unzipSize);\n  item.body =  reqSizeStr === '' && resSizeStr === '' ? '' : reqSizeStr  + ', ' + resSizeStr;\n  item.bodySize = (req.size || 0) + (res.size || 0);\n  var result = res.statusCode == null ? defaultValue : res.statusCode;\n  item.result = (/^[1-9]/.test(result) && parseInt(result, 10)) || result;\n  item.type = (resHeaders['content-type'] || '')\n    .split(';')[0]\n    .toLowerCase();\n  item.dns =\n    item.request =\n    item.response =\n    item.download =\n    item.time =\n      defaultValue;\n  if (item.dnsTime > 0) {\n    item.dns = item.dnsTime - item.startTime + 'ms';\n    if (item.requestTime > 0) {\n      item.request = item.requestTime - item.dnsTime + 'ms';\n    }\n    if (item.responseTime > 0) {\n      if (!item.requestTime || item.requestTime > item.responseTime) {\n        item.response = item.responseTime - item.dnsTime + 'ms';\n      } else {\n        item.response = item.responseTime - item.requestTime + 'ms';\n      }\n      if (end > 0) {\n        item.download = end - item.responseTime + 'ms';\n        item.time = end - item.startTime + 'ms';\n      }\n    }\n  }\n  req._hasError = item.reqError;\n  res._hasError = item.resError;\n  req.rawHeaders = getRawHeaders(req.headers, req.rawHeaderNames);\n  res.rawHeaders = getRawHeaders(res.headers, res.rawHeaderNames);\n  res.rawTrailers = getRawHeaders(res.trailers, res.rawTrailerNames);\n  setStyle(item);\n  if (item.rules && item.pipe) {\n    item.rules.pipe = item.pipe;\n  }\n  if (!item.path) {\n    if (item.isHttps) {\n      item.protocol =  util.getTransProto(res) || util.getTransProto(req) || 'HTTP';\n    } else {\n      item.protocol =item.useH2\n        ? 'H2' : util.getProtocol(url);\n    }\n    item.hostname = item.isHttps ? 'Tunnel to' : util.getHost(url);\n    var pathIndex = url.indexOf('://');\n    if (pathIndex !== -1) {\n      pathIndex = url.indexOf('/', pathIndex + 3);\n      item.path = pathIndex === -1 ? '/' : url.substring(pathIndex);\n    } else {\n      item.path = url;\n    }\n    if (item.path.length > MAX_PATH_LENGTH) {\n      item.shortPath = item.path.substring(0, MAX_PATH_LENGTH) + '...';\n    }\n  } else if (item.useH2) {\n    item.protocol = 'H2';\n  } else if (item.protocol === 'H2') {\n    item.protocol = item.isHttps ? 'HTTP' : util.getProtocol(url);\n  }\n  if (item.useHttp && (item.protocol === 'HTTPS' || item.protocol === 'WSS')) {\n    item.protocol = item.protocol + ' > ' + item.protocol.slice(0, -1);\n  }\n  if (!item.frames && isFrames(item)) {\n    item.frames = [];\n  }\n}\n\nvar PROTOCOL_RE = /^(?:https?|wss?):\\/\\//;\n\nfunction checkUrl(data) {\n  var url = data && data.url;\n  if (!url || typeof url !== 'string' || url.indexOf('#') !== -1) {\n    return false;\n  }\n  if (data.isHttps) {\n    return url.indexOf('/') === -1 && url.indexOf('?') === -1;\n  }\n  return PROTOCOL_RE.test(url);\n}\n\nexports.addNetworkList = function (list) {\n  if (!Array.isArray(list) || !list.length) {\n    return;\n  }\n  var hasData;\n  var curNewIdList = [];\n  var curNewList = [];\n  list.forEach(function (data) {\n    if (\n      !data ||\n      !(data.startTime >= 0) ||\n      !data.req ||\n      !data.req.headers ||\n      !data.res ||\n      !checkUrl(data)\n    ) {\n      return;\n    }\n    var req = data.req;\n    delete data.active;\n    delete data.selected;\n    delete data.hide;\n    delete data.order;\n    delete req.json;\n    delete data.res.json;\n    delete data.data;\n    delete data.stopRecordFrames;\n    delete data.pauseRecordFrames;\n    if (!util.isString(data.fwdHost)) {\n      delete data.fwdHost;\n    }\n    if (Array.isArray(data.frames)) {\n      data.frames = data.frames.filter(function (frame) {\n        if (frame) {\n          delete frame.json;\n          delete frame.data;\n        }\n        return frame;\n      });\n    }\n    data.lost = true;\n    data.importedData = true;\n    data.highlight = true;\n    data.id = data.startTime + '-' + ++dataIndex;\n    setReqData(data);\n    dataList.push(data);\n    curNewIdList.push(data.id);\n    curNewList.push(data);\n    hasData = true;\n    workers.postMessage(data);\n  });\n  if (hasData) {\n    events.trigger('autoRefreshNetwork');\n    setTimeout(function() {\n      curNewList.forEach(function(item) {\n        delete item.highlight;\n      });\n    }, 800);\n    exports.curNewIdList = curNewIdList;\n    dataCallbacks.forEach(function (cb) {\n      cb(networkModal);\n    });\n  }\n};\n\nfunction overflowCount() {\n  return dataList.length - NetworkModal.MAX_COUNT - 1;\n}\n\nexports.overflowCount = overflowCount;\n\nexports.networkModal = networkModal;\n\nfunction getStartTime() {\n  if (!inited) {\n    return clearNetwork ? -2 : '';\n  }\n  if (overflowCount() > 0 || exports.stopRefresh) {\n    return -1;\n  }\n  return lastRowId || '0';\n}\n\nfunction updateServerInfo(data) {\n  if (!serverInfoCallbacks.length) {\n    updateCount = 0;\n    return;\n  }\n\n  if (!(data = data && data.server)) {\n    ++updateCount;\n    if (updateCount >= MAX_UPDATE_COUNT) {\n      curServerInfo = data;\n      serverInfoCallbacks.forEach(function (cb) {\n        cb(false);\n      });\n    }\n    return;\n  }\n  updateCount = 0;\n  if (exports.setServerInfo) {\n    exports.setServerInfo(data);\n  }\n  if (curServerInfo) {\n    if (curServerInfo.strictMode != data.strictMode) {\n      curServerInfo.strictMode = data.strictMode;\n      events.trigger('updateStrictMode');\n    }\n    if (curServerInfo.version !== data.version || curServerInfo.latestVersion !== data.latestVersion\n      || curServerInfo.latestClientVersion !== data.latestClientVersion) {\n      curServerInfo.version = data.version;\n      curServerInfo.latestVersion = data.latestVersion;\n      curServerInfo.latestClientVersion = data.latestClientVersion;\n      events.trigger('updateVersion', data);\n    }\n  }\n  if (\n    curServerInfo &&\n    curServerInfo.version == data.version &&\n    curServerInfo.rulesMode === data.rulesMode &&\n    curServerInfo.cmdName === data.cmdName &&\n    curServerInfo.networkMode === data.networkMode &&\n    curServerInfo.pluginsMode === data.pluginsMode &&\n    curServerInfo.multiEnv === data.multiEnv &&\n    curServerInfo.baseDir == data.baseDir &&\n    curServerInfo.username == data.username &&\n    curServerInfo.nodeVersion == data.nodeVersion &&\n    curServerInfo.port == data.port &&\n    curServerInfo.host == data.host &&\n    curServerInfo.pid == data.pid &&\n    curServerInfo.whistleId == data.whistleId &&\n    curServerInfo.ipv6Only == data.ipv6Only &&\n    curServerInfo.ipv4.sort().join() == data.ipv4.sort().join() &&\n    curServerInfo.ipv6.sort().join() == data.ipv6.sort().join()\n  ) {\n    curServerInfo = data;\n    return;\n  }\n  curServerInfo = data;\n  serverInfoCallbacks.forEach(function (cb) {\n    cb(data);\n  });\n}\n\nexports.isDiableCustomCerts = function () {\n  return curServerInfo && curServerInfo.dcc;\n};\n\nexports.isMultiEnv = function () {\n  return curServerInfo && (curServerInfo.multiEnv || curServerInfo.notHTTPS);\n};\n\nexports.isPureProxy = function () {\n  return curServerInfo && curServerInfo.pureProxy;\n};\n\nexports.needEnableHttps = function () {\n  return !exports.isMultiEnv() &&  !exports.isCapture;\n};\n\nexports.isStrictMode = function () {\n  return (curServerInfo && curServerInfo.strictMode) || false;\n};\n\nexports.getServerInfo = function () {\n  return curServerInfo || '';\n};\n\nexports.on = function (type, callback) {\n  startLoadData();\n  if (type == 'data') {\n    if (typeof callback == 'function') {\n      dataCallbacks.push(callback);\n      callback(networkModal);\n    }\n  } else if (type == 'serverInfo') {\n    if (typeof callback == 'function') {\n      serverInfoCallbacks.push(callback);\n    }\n  } else if (type == 'log') {\n    if (typeof callback == 'function') {\n      logCallbacks.push(callback);\n      callback(logList, svrLogList);\n    }\n  } else if (type === 'plugins' || type === 'settings' || type === 'rules') {\n    if (typeof callback == 'function') {\n      directCallbacks.push(callback);\n    }\n  } else if (type == 'framesUpdate') {\n    if (typeof callback == 'function') {\n      framesUpdateCallbacks.push(callback);\n    }\n  }\n};\n\nexports.networkModal = networkModal;\n\nexports.stopNetworkRecord = function (stop) {\n  if (!stop && exports.pauseRefresh) {\n    networkModal.clearNetwork = false;\n  } else {\n    networkModal.clearNetwork = !stop;\n  }\n  exports.pauseRefresh = false;\n  exports.stopRefresh = stop;\n};\nexports.pauseNetworkRecord = function () {\n  networkModal.clearNetwork = false;\n  exports.pauseRefresh = true;\n  exports.stopRefresh = true;\n};\n\nexports.pauseConsoleRecord = function () {\n  exports.stopConsoleRefresh = false;\n  exports.pauseConsoleRefresh = true;\n};\n\nexports.stopConsoleRecord = function (stop) {\n  exports.pauseConsoleRefresh = false;\n  exports.stopConsoleRefresh = stop;\n};\n\nexports.pauseServerLogRecord = function () {\n  exports.stopServerLogRefresh = false;\n  exports.pauseServerLogRefresh = true;\n};\n\nexports.stopServerLogRecord = function (stop) {\n  exports.pauseServerLogRefresh = false;\n  exports.stopServerLogRefresh = stop;\n};\n\nfunction isDisabledPlugin(name) {\n  return disabledAllPlugins || disabledPlugins[name.slice(0, -1)];\n}\n\nexports.getPlugin = function (name) {\n  return isDisabledPlugin(name) ? null : pluginsMap[name];\n};\n\nexports.getInstalledPlugins = function () {\n  return Object.keys(pluginsMap).sort(util.getPluginComparator(pluginsMap))\n  .map(function (name) {\n    var plugin = pluginsMap[name];\n    var disabled = !!isDisabledPlugin(name);\n    name = name.slice(0, -1);\n    return {\n      active: !disabledPlugins[name],\n      disabled: disabled,\n      name: name,\n      moduleName: plugin.moduleName,\n      version: plugin.version\n    };\n  });\n};\n\nexports.setDisabledPlugins = function(plugins) {\n  disabledPlugins = plugins;\n};\n\nfunction getMenus(menuName) {\n  var list = account && account[menuName];\n  if (!Array.isArray(list)) {\n    list = [];\n  }\n  if (disabledAllPlugins) {\n    return list;\n  }\n  Object.keys(pluginsMap).forEach(function (name) {\n    var plugin = pluginsMap[name];\n    var menus = plugin[menuName];\n    if (menus) {\n      var simpleName = name.slice(0, -1);\n      if (!disabledPlugins[simpleName]) {\n        menus.forEach(function (menu) {\n          menu.title = simpleName + ' extension menu';\n          menu.mtime = plugin.mtime;\n          menu.priority = plugin.priority;\n          menu._key = name;\n          menu._urlPattern = util.toRegExp(menu.urlPattern) || util.toRegExp(menu.namePattern);\n          list.push(menu);\n        });\n      }\n    }\n  });\n  return list.length > 1 ? list.sort(util.comparePlugin) : list;\n}\n\nexports.getNetworkMenus = function () {\n  return getMenus('networkMenus');\n};\n\nexports.getRulesMenus = function () {\n  return getMenus('rulesMenus');\n};\n\nexports.getValuesMenus = function () {\n  return getMenus('valuesMenus');\n};\n\nexports.getPluginsMenus = function () {\n  return getMenus('pluginsMenus');\n};\n\nexports.getPluginColumns = function() {\n  return pluginColumns;\n};\n\nexports.getPluginRegistry = function() {\n  var result = [];\n  Object.keys(pluginsMap).forEach(function(key) {\n    var registry = pluginsMap[key].registry;\n    if (registry && result.indexOf(registry) === -1) {\n      result.push(registry);\n    }\n  });\n  return result;\n};\n\nvar valuesModal;\n\nexports.setValuesModal = function(modal) {\n  valuesModal = modal;\n};\n\nexports.getValuesModal = function() {\n  return valuesModal;\n};\n\nexports.getRulesModal = function() {\n  return exports.rulesModal;\n};\n\nexports.getDataKeys = function() {\n  var result = [];\n  if (exports.custom1Key) {\n    result.push('custom1');\n  }\n  if (exports.custom2Key) {\n    result.push('custom2');\n  }\n  return result.concat(dataKeys);\n};\n\nexports.getRemoteData = function (url, callback) {\n  var opts = {  url: url };\n  exports.importRemote(opts,  function (data, xhr) {\n    if (!data) {\n      util.showSystemError(xhr);\n      return callback(true);\n    }\n    if (data.ec !== 0) {\n      message.error(data.em || 'Error');\n      return callback(true);\n    }\n    try {\n      var value = data.body || data.value;\n      data = value && JSON.parse(value);\n      return callback(false, data || {});\n    } catch (e) {\n      message.error(e.message);\n    }\n    callback(true);\n  });\n};\n\nexports.showLatestClientVersion = function() {\n  if (!hasUpdater) {\n    return;\n  }\n  exports.updateClient(function (result, xhr) {\n    if (!result) {\n      return util.showSystemError(xhr);\n    }\n    if (result.ec) {\n      message.error(result.em || 'Update failed');\n    }\n  });\n  return true;\n};\n\nfunction toString(options) {\n  return typeof options === 'string' ? options : JSON.stringify(options);\n}\n\nfunction triggerWhistleIdChanged(server, byServer) {\n  var whistleId = server && server.whistleId;\n  var hasWhistleToken = server && server.hasWhistleToken;\n  if (!hasWhistleToken !== !exports.hasWhistleToken) {\n    exports.hasWhistleToken = hasWhistleToken;\n    events.trigger('hasWhistleTokenChanged', hasWhistleToken);\n  }\n  if (whistleId !== exports.whistleId) {\n    if (byServer) {\n      if (manualLogout) {\n        if (server) {\n          server.whistleId = undefined;\n        }\n        manualLogout = false;\n        return;\n      }\n    } else {\n      manualLogout = true;\n    }\n    exports.whistleId = whistleId;\n    events.trigger('whistleIdChanged', whistleId);\n  }\n}\n\nexports.triggerWhistleIdChanged = triggerWhistleIdChanged;\n\nfunction updateWhistleId(server) {\n  var whistleId = server && server.whistleId;\n  triggerWhistleIdChanged(server, true);\n  if (whistleId && rulesMFlag !== server.rulesMFlag) {\n    rulesMFlag = server.rulesMFlag;\n    events.trigger('rulesMFlagChanged', rulesMFlag);\n  }\n}\n\nexports.getRulesMFlag = function() {\n  return rulesMFlag || '';\n};\n\nexports.saveToService = function(options, callback) {\n  if (!exports.whistleId) {\n    return;\n  }\n  exports.save(toString(options), callback);\n};\n\nsetDataCenter(exports);\n\nworkers.setup(networkModal);\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/decode.js",
    "content": "var toByteArray = require('base64-js').toByteArray;\nvar base64Decode = require('js-base64').Base64.decode;\nvar isUtf8 = require('./is-utf8');\n\nvar gbkDecoder;\nif (self.TextDecoder) {\n  try {\n    gbkDecoder = new self.TextDecoder('GB18030');\n  } catch (e) {}\n}\n\nfunction base64toBytes(base64) {\n  try {\n    return toByteArray(base64);\n  } catch (e) {}\n}\n\nself.getText = function(base64) {\n  var arr = base64 && base64toBytes(base64);\n  if (!arr) {\n    return '';\n  }\n  if (!isUtf8(arr)) {\n    try {\n      if (gbkDecoder) {\n        return gbkDecoder.decode(arr);\n      }\n    } catch (e) {}\n  }\n  try {\n    return base64Decode(base64);\n  } catch (e) {}\n  return '';\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/detail.js",
    "content": "require('../css/detail.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar events = require('./events');\nvar BtnGroup = require('./btn-group');\nvar Overview = require('./overview');\nvar Inspectors = require('./inspectors');\nvar Timeline = require('./timeline');\nvar ComposerList = require('./composer-list');\nvar Tools = require('./tools');\nvar Saved = require('./saved');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar ReqData = React.createClass({\n  getInitialState: function () {\n    return {\n      tabs: [\n        {\n          name: 'Overview',\n          icon: 'eye-open'\n        },\n        {\n          name: 'Inspectors',\n          icon: 'search'\n        },\n        {\n          name: 'Timeline',\n          icon: 'time'\n        },\n        {\n          name: 'Composer',\n          icon: 'send'\n        },\n        {\n          name: 'Tools',\n          icon: 'heart'\n        },\n        {\n          name: 'Saved',\n          icon: 'saved'\n        }\n      ],\n      initedOverview: false,\n      initedInspectors: false,\n      initedFrames: false,\n      initedTimeline: false,\n      initedComposer: false,\n      initedTools: false,\n      initedSaved: false\n    };\n  },\n  componentDidMount: function () {\n    if (this.props.data) {\n      return;\n    }\n    var self = this;\n    var tabs = self.state.tabs;\n    var timer;\n    var update = function () {\n      self.setState({});\n    };\n    events\n      .on('showOverview', function () {\n        events.trigger('overviewScrollTop');\n        self.toggleTab(tabs[0]);\n      })\n      .on('showInspectors', function () {\n        self.toggleTab(tabs[1]);\n      })\n      .on('showTimeline', function () {\n        self.toggleTab(tabs[2]);\n      })\n      .on('showLog', function () {\n        self.toggleTab(tabs[4]);\n      })\n      .on('composer', function (e, item) {\n        var modal = self.props.modal;\n        self.showComposer(item || (modal && modal.getActive()));\n        setTimeout(function() {\n          self.shakeComposerTab();\n        }, 100);\n      })\n      .on('showComposerTab', function() {\n        self.showComposer();\n      })\n      .on('networkStateChange', function () {\n        clearTimeout(timer);\n        timer = setTimeout(update, 100);\n      })\n      .on('toggleDetailTab', function () {\n        var tab = self.state.tab;\n        if (tab === tabs[0]) {\n          self.toggleTab(tabs[1]);\n        } else if (tab === tabs[1]) {\n          self.toggleTab(tabs[2]);\n        } else {\n          self.toggleTab(tabs[0]);\n        }\n      }).on('shakeSavedTab', self.shakeSavedTab);\n  },\n  shakeSavedTab: function() {\n    util.shakeElem($(findDOMNode(this.refs.tabs)).find('button[data-name=\"Saved\"]'));\n  },\n  showComposer: function (item) {\n    if (item) {\n      this.state.activeItem = item;\n    }\n    this.toggleTab(this.state.tabs[3], function () {\n      item && events.trigger('setComposer');\n    });\n  },\n  isShowingSaved: function() {\n    var tab = this.state.tab;\n    return tab && tab.name === 'Saved';\n  },\n  onDragEnter: function (e) {\n    if (e.dataTransfer.types.indexOf('reqdataid') != -1) {\n      !this.isShowingSaved() && this.showComposer();\n      e.preventDefault();\n    }\n  },\n  getItemById: function(id, list) {\n    for (var i = 0, len = list.length; i < len; i++) {\n      var item = list[i];\n      if (item && item.id === id) {\n        return item;\n      }\n    }\n  },\n  onDrop: function (e) {\n    var modal = this.props.modal;\n    var id = e.dataTransfer.getData('reqDataId');\n    var list = modal && modal.list;\n    var len = list && list.length;\n    if (!id || !len) {\n      return;\n    }\n    var item = this.getItemById(id, list);\n    if (this.isShowingSaved()) {\n      return events.trigger('saveSessions', [item]);\n    }\n    item && this.showComposer(item);\n  },\n  onDoubleClick: function () {\n    events.trigger('ensureSelectedItemVisible');\n  },\n  toggleTab: function (tab, callback) {\n    if (tab.name === 'Inspectors' && this.state.initedInspectors) {\n      var inspectors = $('.w-detail-inspectors');\n      if (inspectors.length) {\n        var detail = $('.w-detail');\n        detail.find('>.fill').addClass('hide');\n        inspectors.removeClass('hide');\n        var btnGroup = detail.find('>.w-tabs-sm>button');\n        btnGroup.removeClass('active');\n        btnGroup.eq(1).addClass('active');\n      }\n    }\n    this.selectTab(tab);\n    this.setState({ tab: tab }, callback);\n  },\n  selectTab: function (tab) {\n    this.state.tabs.forEach(function (tab) {\n      tab.active = false;\n    });\n    tab.active = true;\n    this.state.tab = tab;\n    this.state['inited' + tab.name] = true;\n  },\n  shakeComposerTab: function() {\n    util.shakeElem($(findDOMNode(this.refs.tabs)).find('button[data-name=\"Composer\"]'));\n  },\n  render: function () {\n    var modal = this.props.modal;\n    var state = this.state;\n    var data = this.props.data;\n    var tabs = state.tabs;\n    var selectedList = !data && modal && modal.getSelectedList();\n    var activeItem;\n    var overview;\n    if (selectedList && selectedList.length > 1) {\n      overview = {\n        req: {\n          size: 0,\n          unzipSize: 0,\n          headers: {}\n        },\n        res: {\n          size: 0,\n          unzipSize: 0,\n          headers: {}\n        }\n      };\n      selectedList.forEach(function (item) {\n        if (overview.startTime == null || overview.startTime > item.startTime) {\n          overview.startTime = item.startTime;\n        }\n        if (overview.endTime == null || overview.endTime < item.endTime) {\n          overview.endTime = item.endTime;\n        }\n        var req = item.req;\n        if (req.size > 0) {\n          overview.req.size += req.size;\n          overview.req.unzipSize +=\n            req.unzipSize == null ? req.size : req.unzipSize;\n        }\n        var res = item.res;\n        if (res.size > 0) {\n          overview.res.size += res.size;\n          overview.res.unzipSize +=\n            res.unzipSize == null ? res.size : res.unzipSize;\n        }\n      });\n    } else if (data) {\n      overview = activeItem = data;\n    } else {\n      overview = activeItem = modal && modal.getActive();\n      if (!activeItem || activeItem.hide) {\n        overview = activeItem = selectedList && selectedList[0];\n      }\n    }\n    var curTab = state.tab;\n    if (!curTab && overview) {\n      curTab = tabs[0];\n      tabs.forEach(function (tab) {\n        tab.active = false;\n      });\n      this.selectTab(curTab);\n    }\n    var name = curTab && curTab.name;\n    var frames = activeItem && activeItem.frames;\n    var dockToBottom = this.props.dockToBottom;\n\n    return (\n      <div\n        className={\n          'fill v-box w-detail' +\n          (dockToBottom ? ' w-detail-bottom' : '')\n        }\n        onDragEnter={this.onDragEnter}\n        onDrop={this.onDrop}\n      >\n        <BtnGroup\n          ref=\"tabs\"\n          dockBtn={\n            <button\n              onClick={this.props.onDockChange}\n              className=\"w-dock-btn\"\n              title={\n                'Dock to ' + (dockToBottom ? 'right' : 'bottom') + ' (F12)'\n              }\n            >\n              <Icon name={'menu-' + (dockToBottom ? 'right' : 'down')} className={data ? 'hide' : ''} />\n            </button>\n          }\n          onDoubleClick={this.onDoubleClick}\n          onClick={this.toggleTab}\n          tabs={tabs}\n        />\n        {state.initedOverview ? (\n          <Overview modal={overview} rulesModal={this.props.rulesModal} hide={name != tabs[0].name} />\n        ) : null}\n        {state.initedInspectors ? (\n          <Inspectors\n            modal={activeItem}\n            frames={frames}\n            hide={name != tabs[1].name}\n          />\n        ) : null}\n        {state.initedTimeline ? (\n          <Timeline data={data} modal={modal} hide={name != tabs[2].name} />\n        ) : null}\n        {state.initedComposer ? (\n          <ComposerList\n            modal={state.activeItem}\n            hide={name != tabs[3].name}\n          />\n        ) : null}\n        {state.initedTools ? <Tools hide={name != tabs[4].name} /> : null}\n        {state.initedSaved ? <Saved hide={name != tabs[5].name} /> : null}\n      </div>\n    );\n  }\n});\n\nmodule.exports = ReqData;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/dialog.js",
    "content": "var $ = (window.jQuery = require('jquery'));\nvar React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar Dialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  componentDidMount: function () {\n    var self = this;\n    this.container = $(document.createElement('div'));\n    var clazz = this.props.fullCustom ? ' w-custom-dialog' : '';\n    this.container.addClass(\n      'modal fade' + clazz + (this.props.wstyle ? ' ' + this.props.wstyle : '')\n    );\n    document.body.appendChild(this.container[0]);\n    this.componentDidUpdate();\n    if (typeof this.props.customRef === 'function') {\n      this.props.customRef(this);\n    }\n    if (typeof this.props.onClose === 'function') {\n      this.container.on('hidden.bs.modal', this.props.onClose);\n    }\n    this.container.on('hide.bs.modal', function() {\n      self._isVisible = false;\n    });\n    this.container.on('show.bs.modal', function() {\n      self._isVisible = true;\n    });\n    if (typeof self.props.onShow === 'function') {\n      this.container.on('shown.bs.modal', function() {\n        self.props.onShow(self);\n      });\n    }\n  },\n  componentDidUpdate: function () {\n    ReactDOM.unstable_renderSubtreeIntoContainer(\n      this,\n      this.getDialogElement(),\n      this.container[0]\n    );\n  },\n  getDialogElement: function () {\n    var props = this.props;\n    var className = props.wclassName;\n    var style;\n    if (props.width) {\n      style = style || {};\n      style.width = props.width;\n    }\n    if (props.fullCustom && props.height) {\n      style = style || {};\n      style.height = props.height;\n    }\n    return (\n      <div\n        style={style}\n        className={'modal-dialog' + (className ? ' ' + className : '')}\n      >\n        <div className=\"modal-content\">{this.props.children}</div>\n      </div>\n    );\n  },\n  componentWillUnmount: function () {\n    ReactDOM.unmountComponentAtNode(this.container[0]);\n    document.body.removeChild(this.container[0]);\n  },\n  show: function () {\n    if (this.container.hasClass('in')) {\n      return;\n    }\n    this._isVisible = true;\n    this.container.modal(\n      this.props.disableBackdrop\n        ? {\n          show: true,\n          backdrop: false\n        }\n        : 'show'\n    );\n  },\n  isVisible: function () {\n    return this._isVisible;\n  },\n  hide: function () {\n    this.container.modal('hide');\n  },\n  destroy: function () {\n    this.hide();\n    this.container && this.componentWillUnmount();\n  },\n  render: function () {\n    return null;\n  }\n});\n\nmodule.exports = Dialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/divider.js",
    "content": "require('../css/divider.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar util = require('./util');\n\nvar HIDE = { display: 'none' };\n\nutil.addDragEvent('.w-divider', function (target, x, y) {\n  target = target.parent();\n  var con = target.parent();\n  var isVertical = !con.hasClass('box');\n  var isRight = target.hasClass('w-divider-right');\n  var size = isVertical\n    ? target[0].offsetHeight - (isRight ? y : -y)\n    : target[0].offsetWidth - (isRight ? x : -x);\n  var conSize = con[0][isVertical ? 'offsetHeight' : 'offsetWidth'];\n  target[isVertical ? 'height' : 'width'](\n    Math.min(conSize - 5, Math.max(5, size))\n  );\n});\n\nvar Divider = React.createClass({\n  componentDidMount: function () {\n    this.reset();\n  },\n  componentDidUpdate: function () {\n    this._needReset && this.reset();\n  },\n  triggerDOMReady: function () {\n    if (this.__inited) {\n      return;\n    }\n    this.__inited = true;\n    this.props.onDOMReady && this.props.onDOMReady();\n  },\n  reset: function () {\n    var self = this;\n    var divider = ReactDOM.findDOMNode(self.refs.divider);\n    if (!divider.offsetHeight) {\n      self._needReset = true;\n      return;\n    }\n    self._needReset = false;\n    var vertical = util.getBool(self.props.vertical);\n    var prop = vertical ? 'height' : 'width';\n    var con = $(divider);\n    var leftElem = con.children('.w-divider-left');\n    var rightElem = con.children('.w-divider-right');\n    leftElem.add(rightElem).css({ height: 'auto', width: 'auto' });\n    if (self._leftWidth > 0) {\n      leftElem[prop](self._leftWidth);\n      self.triggerDOMReady();\n      return;\n    }\n\n    var rightWidth = parseInt(self.props.rightWidth, 10);\n    if (!(rightWidth > 0)) {\n      setTimeout(function () {\n        var ratio = self.props.splitRatio;\n        rightWidth =\n          (vertical ? divider.offsetHeight : divider.offsetWidth) * (ratio > 0 ? ratio : 1 / 2);\n        rightElem[prop](Math.max(rightWidth, 5));\n        self.triggerDOMReady();\n      }, 10);\n      return;\n    }\n\n    rightElem[prop](Math.max(rightWidth, 5));\n    self.triggerDOMReady();\n  },\n  render: function () {\n    var vertical = util.getBool(this.props.vertical);\n    var divider = <div className=\"w-divider\" onDoubleClick={this.reset} />;\n    var hideLeft = this.props.hideLeft;\n    var hideRight = this.props.hideRight;\n    var leftWidth = parseInt(this.props.leftWidth, 10);\n    if (leftWidth > 0) {\n      this._leftWidth = leftWidth;\n    } else {\n      leftWidth = 0;\n    }\n    var noLeft = leftWidth || hideLeft;\n\n    return (\n      <div\n        ref=\"divider\"\n        className={\n          (vertical ? 'v-box' : 'box') +\n          ' fill w-divider-con ' +\n          (this.props.className || '') +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <div\n          style={hideLeft ? HIDE : undefined}\n          className={\n            (leftWidth ? '' : 'fill ') +\n            'w-divider-left v-box ' +\n            (this.props.leftClassName || '')\n          }\n        >\n          {leftWidth && !hideRight ? divider : null}\n          {this.props.children[0]}\n        </div>\n        <div\n          style={hideRight ? HIDE : undefined}\n          className={\n            (noLeft ? 'fill ' : '') +\n            'w-divider-right v-box ' +\n            (this.props.rightClassName || '')\n          }\n        >\n          {noLeft ? null : divider}\n          {this.props.children[1]}\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Divider;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/dns-servers-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar CloseBtn = require('./close-btn');\n\nvar DNSDialog = React.createClass({\n  getInitialState: function () {\n    return { servers: '' };\n  },\n  show: function (data) {\n    if (!data || !data.dns) {\n      return;\n    }\n    this._hideDialog = false;\n    var servers = data.dns;\n    if (!data.doh) {\n      servers = data.dns\n        .split(',')\n        .map(function (dns, i) {\n          return 'DNS Server' + (i + 1) + ':  ' + dns;\n        })\n        .join('\\n');\n    }\n    this.setState({\n      ipv6: data.r6,\n      useDefault: data.df,\n      servers: servers,\n      doh: data.doh\n    });\n    this.refs.dnsServersDialog.show();\n  },\n  hide: function () {\n    this.refs.dnsServersDialog.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  render: function () {\n    var state = this.state;\n    var title;\n    if (state.doh) {\n      title = 'Resolve IP address from follow URL';\n    } else {\n      title =\n        'Resolve ' +\n        (state.ipv6 ? 'IPv6' : 'IPv4') +\n        ' address from follow DNS servers' +\n        (state.useDefault ? ' first' : '');\n    }\n    return (\n      <Dialog ref=\"dnsServersDialog\" wstyle=\"w-dns-servers-dialog\">\n        <div className=\"modal-header\">\n          {title}\n          <CloseBtn />\n        </div>\n        <pre className=\"modal-body\">{state.servers}</pre>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          <button\n            type=\"button\"\n            data-dismiss=\"modal\"\n            className=\"btn btn-primary w-copy-text-with-tips\"\n            data-clipboard-text={state.servers}\n          >\n            Copy\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = DNSDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/dropdown.js",
    "content": "require('../css/dropdown.css');\nvar React = require('react');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar DropDown = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  onChange: function (option) {\n    var onChange = this.props.onChange;\n    if (onChange) {\n      onChange(option);\n    }\n    if (this.props.value == null) {\n      this.setState({\n        selectedOption: option\n      });\n    }\n  },\n  onMouseEnter: function () {\n    var onBeforeShow = this.props.onBeforeShow;\n    if (onBeforeShow) {\n      onBeforeShow();\n    }\n    this.setState({ hover: true });\n  },\n  onMouseLeave: function () {\n    this.setState({ hover: false });\n  },\n  getSelectedOption: function () {\n    var props = this.props;\n    var value = props.value;\n    if (value == null) {\n      return;\n    }\n    return util.findArray(props.options, function (item) {\n      return item === value || item.value === value;\n    });\n  },\n  render: function () {\n    var self = this;\n    var help = self.props.help;\n    var options = self.props.options || [];\n    var firstOption = options[0] || {};\n    var disabled = self.props.disabled;\n    var selectedOption =\n      this.getSelectedOption() || self.state.selectedOption || firstOption;\n\n    return (\n      <div\n        className=\"dropdown w-dropdown\"\n        onMouseEnter={self.onMouseEnter}\n        onMouseLeave={self.onMouseLeave}\n      >\n        <div\n          style={{\n            color:\n              selectedOption === firstOption\n                ? undefined\n                : selectedOption.color || 'var(--c-error)'\n          }}\n          title={selectedOption.text}\n          className={\n            'dropdown-toggle w-dropdown-text' + (disabled ? ' w-disabled' : '')\n          }\n        >\n          {selectedOption.icon ? (\n            <Icon name={selectedOption.icon} />\n          ) : undefined}\n          {selectedOption.text}\n          <span className=\"caret\"></span>\n        </div>\n        <ul\n          style={{\n            display: !disabled && self.state.hover ? 'block' : 'none',\n            padding: help ? undefined : 0\n          }}\n          className=\"dropdown-menu\"\n        >\n          {options.map(function (option) {\n            return (\n              <li\n                key={option.value}\n                title={option.text}\n                data-value={option.value}\n                onClick={function () {\n                  self.onMouseLeave();\n                  if (option === selectedOption) {\n                    return;\n                  }\n                  self.onChange(option);\n                }}\n              >\n                {option.icon ? <Icon name={option.icon} /> : undefined}\n                {option.text}\n              </li>\n            );\n          })}\n          {help ? <li role=\"separator\" className=\"divider\"></li> : undefined}\n          {help ? (\n            <li style={{ padding: 0 }}>\n              <a href={help} target=\"_blank\">\n                <Icon name=\"question-sign\" />\n                Help\n              </a>\n            </li>\n          ) : undefined}\n        </ul>\n      </div>\n    );\n  }\n});\n\nmodule.exports = DropDown;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/editor-dialog.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar events = require('./events');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar message = require('./message');\nvar win = require('./win');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_LEN = 1024 * 1024 * 11;\nvar fakeIframe = 'javascript:\"<style>html,body{padding:0;margin:0}</style><textarea></textarea>\"';\nvar iframeStyle = {\n  padding: 0,\n  border: 'none',\n  width: 980,\n  height: 550,\n  margin: 0,\n  verticalAlign: 'top'\n};\n\nfunction getTempFile(tempFile, cb) {\n  if (tempFile === 'blank') {\n    return cb('');\n  }\n  dataCenter.getTempFile({\n    filename: tempFile\n  }, function (result, xhr) {\n    if (!result) {\n      return util.showSystemError(xhr);\n    }\n    if (result.em) {\n      message.error(result.em);\n      if (result.ec) {\n        return;\n      }\n    }\n    cb(result.value || '');\n  });\n}\n\nvar EditorDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  show: function (data) {\n    this._hideDialog = false;\n    this.setState(data);\n    var textarea = this._textarea;\n    if (this.props.textEditor && textarea) {\n      textarea.value = (data && data.value) || '';\n      setTimeout(function() {\n        textarea.focus();\n      }, 600);\n    }\n    this.refs.editorDialog.show();\n  },\n  hide: function () {\n    this.refs.editorDialog.hide();\n    this._hideDialog = true;\n  },\n  onChange: function (e) {\n    this.setState({ value: e.target.value });\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  componentDidMount: function() {\n    var self = this;\n    if (!self.props.textEditor) {\n      return;\n    }\n    events.on('uploadTempFile', function(_, file) {\n      self.readFile(file);\n    });\n    var iframe = findDOMNode(self.refs.iframe);\n    var initTextArea = function() {\n      var textarea = iframe.contentWindow.document.querySelector('textarea');\n      var style = textarea && textarea.style;\n      self._textarea = textarea;\n      if (style) {\n        style.resize = 'none';\n        style.width = iframeStyle.width + 'px';\n        style.height = iframeStyle.height + 'px';\n        style.padding = '5px';\n        style.border = '1px solid var(--c-border, #ccc)';\n        style.borderRadius = '3px';\n        textarea.maxLength = MAX_LEN;\n        textarea.placeholder = self.props.placeholder || 'Enter text';\n        textarea.onkeydown = function(e) {\n          if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) {\n            e.preventDefault();\n            self.props.textEditor && self.onSave();\n          }\n          util.handleFormat(e, self.formatValue);\n          util.handleTab(e);\n        };\n      }\n    };\n    iframe.onload = initTextArea;\n    initTextArea();\n    this.props.standalone && events.on('showEditorDialog', function(_, data, elem) {\n      if (data.name) {\n        var item = dataCenter.valuesModal.get(data.name);\n        var value = item && item.value || '';\n        self._keyName = data.name;\n        self.show({\n          value: value,\n          title: item ? 'Update value for key \\'' + data.name + '\\' in Values' : 'Create a new key \\'' + data.name + '\\' to Values',\n          isTempFile: false\n        });\n      } else {\n        var rulesItem = elem && dataCenter.rulesModal.get(data.ruleName);\n        if (rulesItem) {\n          var tempFile = data.tempFile;\n          self._tempFile = tempFile;\n          self._fileElem = elem;\n          self._rulesItem = rulesItem;\n          tempFile = tempFile || 'blank';\n          var isBlank = tempFile === 'blank' || /[\\\\/]/.test(tempFile);\n          getTempFile(tempFile, function(value) {\n            self.show({\n              value: value,\n              title: (isBlank ? 'Create' : 'Modify') + ' temp file' + (isBlank ? '' : ' (temp/' + tempFile + ')'),\n              isTempFile: true\n            });\n          });\n        }\n      }\n    });\n  },\n  getValue: function() {\n    var value = this._textarea ? this._textarea.value : this.state.value;\n    return value || '';\n  },\n  onConfirm: function() {\n    var result = this.props.onConfirm(this.getValue());\n    if (result !== false) {\n      this.hide();\n    }\n  },\n  onSave: function(base64) {\n    var self = this;\n    var isBase64 = typeof base64 === 'string';\n    var value = isBase64 ? base64 : self.getValue();\n    if (!isBase64 && !self.state.isTempFile) {\n      dataCenter.values.add({\n        name: self._keyName,\n        value: value\n      }, function (data, xhr) {\n        if (data && data.ec === 0) {\n          events.trigger('addNewValuesFile', {\n            filename: self._keyName,\n            data: value,\n            update: true\n          });\n          self.hide();\n        } else {\n          util.showSystemError(xhr);\n        }\n      });\n      return;\n    }\n    var params = {  clientId: dataCenter.getPageId() };\n    params[isBase64 ? 'base64' : 'value'] = value;\n    dataCenter.createTempFile(JSON.stringify(params), function (result, xhr) {\n      if (!result || result.ec !== 0) {\n        return util.showSystemError(xhr);\n      }\n      var elem = self._fileElem;\n      var line = elem.closest('.CodeMirror-line')[0];\n      var list = elem.closest('.CodeMirror-code').find('.CodeMirror-line');\n      var index = 0;\n      for (var i = 0, len = list.length; i < len; i++) {\n        if (list[i] === line) {\n          index = i;\n          break;\n        }\n      }\n      var text = elem.text();\n      var newText;\n      var tempFile = self._tempFile;\n      if (tempFile) {\n        var suffix = tempFile.lastIndexOf('.');\n        if (suffix === -1) {\n          newText = text.replace('temp/' + tempFile, result.filepath);\n        } else {\n          newText = text.replace(tempFile.substring(0, suffix), result.filepath);\n          if (newText.indexOf('://') === -1) {\n            newText = 'file://' + newText;\n          }\n        }\n\n      } else {\n        newText = text.replace(/temp(\\.[\\w-]+)?$/, result.filepath + '$1');\n      }\n      var rulesText = self._rulesItem.value.split(/\\r\\n|\\r|\\n/).map(function(l, i) {\n        if (i === index) {\n          l = l.trim().split(/\\s+/).map(function(part) {\n            return part === text ? newText : part;\n          }).join(' ');\n        }\n        return l;\n      }).join('\\n');\n      var filename = self._rulesItem.name;\n      dataCenter.rules.add(\n        {\n          name: filename,\n          value: rulesText,\n          selected: self._rulesItem.selected ? '1' : ''\n        },\n        function (result, xhr) {\n          if (result && result.ec === 0) {\n            events.trigger('addNewRulesFile', {\n              filename: filename,\n              data: rulesText,\n              update: true\n            });\n            self.hide();\n          } else {\n            util.showSystemError(xhr);\n          }\n        }\n      );\n    });\n  },\n  formatValue: function() {\n    var textarea = this._textarea;\n    try {\n      var val = textarea.value.trim();\n      if (val[0] === '{' || val[0] === '[') {\n        var formattedVal = JSON.stringify(JSON.parse(val), null, '  ');\n        if (textarea.value !== formattedVal) {\n          textarea.value = formattedVal;\n        }\n      }\n    } catch (e) {\n      message.error(e.message);\n    }\n  },\n  clearValue: function() {\n    this._textarea.value = '';\n  },\n  onUpload: function () {\n    if (!this.reading) {\n      findDOMNode(this.refs.readLocalFile).click();\n    }\n  },\n  readFile: function(file) {\n    var self = this;\n    self.reading = true;\n    util.readFile(file, function (data) {\n      self.reading = false;\n      self.onSave(util.bytesToBase64(data));\n    });\n  },\n  readLocalFile: function () {\n    var form = new FormData(findDOMNode(this.refs.readLocalFileForm));\n    var file = form.get('localFile');\n    if (file.size > MAX_LEN) {\n      return win.alert('Total file size must not exceed 10MB');\n    }\n    this.readFile(file);\n    findDOMNode(this.refs.readLocalFile).value = '';\n  },\n  render: function () {\n    var state = this.state;\n    var props = this.props;\n    var value = state.value;\n    var title = props.title || state.title;\n    var textEditor = this.props.textEditor;\n    var showUpload = textEditor && !props.onConfirm;\n\n    return (\n      <Dialog ref=\"editorDialog\" wstyle={'w-editor-dialog' + (textEditor ? ' w-big-editor-dialog' : '') +\n      (showUpload ? ' w-show-upload-temp-file' : '')}>\n        <div className=\"modal-header\">\n          <h4>{title || 'Edit copied text'}</h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body\">\n          {\n            textEditor ? <div className=\"w-mock-action\">\n              {props.hideFormat ? null : <a onClick={this.formatValue}>Format</a>}\n              <a onClick={this.clearValue}>Clear</a>\n            </div> : null\n          }\n          {\n            textEditor ? <div className=\"w-fake-iframe w-fix-drag\"><iframe ref=\"iframe\" data-type=\"fake\"\n              src={fakeIframe} onLoad={dataCenter.handleIframeLoad} style={iframeStyle}/></div> :\n              <textarea onChange={this.onChange} value={value} />\n          }\n        </div>\n        {textEditor ? <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          {props.onConfirm ? null : <button\n            type=\"button\"\n            className=\"btn btn-info\"\n            onClick={this.onUpload}\n          >\n            <Icon name=\"folder-open\" />\n            Upload\n          </button>}\n          <button\n            type=\"button\"\n            className=\"btn btn-primary\"\n            onClick={props.onConfirm ? this.onConfirm : this.onSave}\n          >\n            {props.onConfirm ? 'Confirm' : 'Save'}\n          </button>\n        </div> : <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          <button\n            type=\"button\"\n            data-dismiss=\"modal\"\n            className=\"btn btn-primary w-copy-text-with-tips\"\n            data-clipboard-text={state.value}\n            disabled={!value}\n          >\n            Copy\n          </button>\n        </div>}\n        <form\n          ref=\"readLocalFileForm\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"readLocalFile\"\n            onChange={this.readLocalFile}\n            type=\"file\"\n            name=\"localFile\"\n          />\n        </form>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = EditorDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/editor-settings.js",
    "content": "require('../css/editor-settings.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar events = require('./events');\nvar themes = require('./util').EDITOR_THEMES;\n\nvar fontSizeOptions = [13];\nfor (var i = 14; i <= 36; i += 2) {\n  fontSizeOptions.push(i);\n}\n\nvar EditorSettings = React.createClass({\n  componentDidMount: function () {\n    var self = this;\n    events.on('toggle' + (this.props.name === 'rules' ? 'Rules' : 'Values') + 'LineNumbers', function () {\n      $(ReactDOM.findDOMNode(self.refs.showLineNumbers)).trigger('click');\n    });\n  },\n  render: function () {\n    return (\n      <div className=\"w-editor-settings\">\n        <p>\n          <label>\n            <span className=\"w-label\">Theme:</span>\n            <select\n              value={this.props.theme}\n              onChange={this.props.onThemeChange}\n              className=\"form-control\"\n            >\n              {themes.map(function(theme) {\n                return <option key={theme} value={theme}>{theme}</option>;\n              })}\n            </select>\n          </label>\n        </p>\n        <p>\n          <label>\n            <span className=\"w-label\">Font Size:</span>\n            <select\n              value={this.props.fontSize}\n              onChange={this.props.onFontSizeChange}\n              className=\"form-control\"\n            >\n              {\n                fontSizeOptions.map(function(size) {\n                  return <option key={size} value={size + 'px'}>{size + 'px'}</option>;\n                })\n              }\n            </select>\n          </label>\n        </p>\n        <p className=\"w-editor-settings-box\">\n          <label className=\"w-align-items\">\n            <input\n              ref=\"showLineNumbers\"\n              checked={this.props.lineNumbers}\n              onChange={this.props.onLineNumberChange}\n              type=\"checkbox\"\n            />{' '}\n            Show line number\n          </label>\n        </p>\n        <p className=\"w-editor-settings-box\">\n          <label className=\"w-align-items\">\n            <input\n              checked={this.props.lineWrapping}\n              onChange={this.props.onLineWrappingChange}\n              type=\"checkbox\"\n            />{' '}\n            Auto line wrapping\n          </label>\n        </p>\n      </div>\n    );\n  }\n});\n\nmodule.exports = EditorSettings;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/editor.js",
    "content": "require('codemirror/lib/codemirror.css');\nrequire('codemirror/theme/neat.css');\nrequire('codemirror/theme/elegant.css');\nrequire('codemirror/theme/erlang-dark.css');\nrequire('codemirror/theme/night.css');\nrequire('codemirror/theme/monokai.css');\nrequire('codemirror/theme/cobalt.css');\nrequire('codemirror/theme/eclipse.css');\nrequire('codemirror/theme/rubyblue.css');\nrequire('codemirror/theme/lesser-dark.css');\nrequire('codemirror/theme/xq-dark.css');\nrequire('codemirror/theme/xq-light.css');\nrequire('codemirror/theme/ambiance.css');\nrequire('codemirror/theme/blackboard.css');\nrequire('codemirror/theme/vibrant-ink.css');\nrequire('codemirror/theme/solarized.css');\nrequire('codemirror/theme/twilight.css');\nrequire('codemirror/theme/midnight.css');\nrequire('codemirror/addon/dialog/dialog.css');\nrequire('codemirror/addon/search/matchesonscrollbar.css');\nrequire('codemirror/addon/fold/foldgutter.css');\n\nrequire('../css/editor.css');\n\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar CodeMirror = require('codemirror');\nvar message = require('./message');\nvar util = require('./util');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar themes = util.EDITOR_THEMES;\nvar INIT_LENGTH = 1024 * 16;\nvar GUTTER_STYLE = [\n  'CodeMirror-linenumbers',\n  'CodeMirror-foldgutter',\n  'CodeMirror-lint-markers'\n];\n\nrequire('codemirror/mode/javascript/javascript');\nrequire('codemirror/mode/css/css');\nrequire('codemirror/mode/xml/xml');\nrequire('codemirror/mode/htmlmixed/htmlmixed');\nrequire('codemirror/mode/markdown/markdown');\nrequire('codemirror/addon/dialog/dialog');\nrequire('codemirror/addon/search/searchcursor');\nrequire('codemirror/addon/search/search');\nrequire('codemirror/addon/scroll/annotatescrollbar');\nrequire('codemirror/addon/search/matchesonscrollbar');\n\nrequire('codemirror/addon/fold/foldcode');\nrequire('codemirror/addon/fold/foldgutter');\nrequire('codemirror/addon/fold/brace-fold');\nrequire('codemirror/addon/fold/comment-fold');\n\nvar rulesHint = require('./rules-hint');\nvar events = require('./events');\n\nrequire('./rules-mode');\nvar DEFAULT_THEME = 'cobalt';\nvar DEFAULT_FONT_SIZE = '14px';\nvar RULES_COMMENT_RE = /^(\\s*)#\\s*/;\nvar JS_COMMENT_RE = /^(\\s*)\\/\\/+\\s?/;\nvar NO_SPACE_RE = /\\S/;\nvar FOLD_MODE = ['javascript', 'htmlmixed', 'css'];\nvar SEARCH_OPTIONS = { caseFold: true, multiline: true };\n\nfunction hasSelector(selector) {\n  return document.querySelector\n    ? document.querySelector(selector)\n    : $(selector).length;\n}\n\nvar Editor = React.createClass({\n  getThemes: function () {\n    return themes;\n  },\n  setMode: function (mode) {\n    if (/^(javascript|css|xml|rules|markdown)$/i.test(mode)) {\n      mode = RegExp.$1.toLowerCase();\n    } else if (/^(js|pac|jsx|json)$/i.test(mode)) {\n      mode = 'javascript';\n    } else if (/^(html|wtpl)$/i.test(mode)) {\n      mode = 'htmlmixed';\n    } else if (/^md$/i.test(mode)) {\n      mode = 'markdown';\n    }\n    if (this._mode !== mode) {\n      this._mode = mode;\n      if (this._editor) {\n        this._editor.setOption('mode', mode);\n      }\n      if (this._foldGutter) {\n        this._editor.setOption('foldGutter', false);\n        this._editor.setOption('foldGutter', true);\n      }\n      this.setFoldGutter(this.props.foldGutter);\n    }\n  },\n  setValue: function (value) {\n    value = this._value = value == null ? '' : value + '';\n    if (!this._editor || this._editor.getValue() == value) {\n      return;\n    }\n    this._editor.setValue(value);\n  },\n  setHistory: function(init, history) {\n    var modal = this.props.modal;\n    if (!modal) {\n      return;\n    }\n    var activeItem = modal.getActive();\n    var list = modal.list;\n    var len = list.length;\n    var map = this._editorHistoryMap || {};\n    if (!len) {\n      this._editorHistoryMap = {};\n    } else if (len !== this._listLen) {\n      Object.keys(map).forEach(function(key) {\n        if (list.indexOf(key) === -1) {\n          delete map[key];\n        }\n      });\n    }\n    this._listLen = len;\n    if(!activeItem) {\n      return;\n    }\n    var name = activeItem.name;\n    this._editorHistoryMap = map;\n    if(init) {\n      this._editorCurrentHistoryKey = name;\n      this._editor.clearHistory();\n    } else {\n      if(this._editorCurrentHistoryKey !== name) {\n        map[this._editorCurrentHistoryKey] = history;\n        this._editorCurrentHistoryKey = name;\n        this._editor.clearHistory();\n        if(map[name]) {\n          this._editor.setHistory(map[name]);\n          delete map[name];\n        }\n      }\n    }\n  },\n  getValue: function () {\n    return this._editor ? '' : this._editor.getValue();\n  },\n  setTheme: function (theme) {\n    theme = this._theme = theme || DEFAULT_THEME;\n    if (!this._editor) {\n      return;\n    }\n    this._editor.setOption('theme', theme);\n  },\n  setFontSize: function (fontSize) {\n    fontSize = this._fontSize = fontSize || DEFAULT_FONT_SIZE;\n    if (this._editor) {\n      findDOMNode(this.refs.editor).style.fontSize = fontSize;\n    }\n  },\n  showLineNumber: function (show) {\n    show = this._showLineNumber = show === false ? false : true;\n    if (this._editor) {\n      this._editor.setOption('lineNumbers', show);\n    }\n  },\n  showLineWrapping: function (show) {\n    show = this._showLineNumber = show === false ? false : true;\n    if (this._editor) {\n      this._editor.setOption('lineWrapping', show);\n    }\n  },\n  setReadOnly: function (readOnly) {\n    readOnly = this._readOnly =\n      readOnly === false || readOnly === 'false' ? false : true;\n    if (this._editor) {\n      this._editor.setOption('readOnly', readOnly);\n    }\n  },\n  handleKeyUp: function(_, e) {\n    clearTimeout(this._timer);\n    var _byDelete = e.keyCode === 8;\n    if (_byDelete || e.keyCode === 13) {\n      var editor = this._editor;\n      this._timer = setTimeout(function () {\n        if (!hasSelector('.CodeMirror-hints')) {\n          editor._byDelete = true;\n          editor._byEnter = !_byDelete;\n          editor.execCommand('autocomplete');\n        }\n      }, 300);\n    }\n  },\n  setAutoComplete: function () {\n    var isRules = this.isRulesEditor();\n    var option = isRules && !this.props.readOnly ? rulesHint.getExtraKeys() : {};\n    if (!/\\(Macintosh;/i.test(window.navigator.userAgent)) {\n      option['Ctrl-F'] = 'findPersistent';\n    }\n    option['Cmd-F'] = 'findPersistent';\n    var editor = this._editor;\n    editor.setOption('extraKeys', option);\n    editor.off('keyup', this.handleKeyUp);\n    isRules && editor.on('keyup', this.handleKeyUp);\n  },\n\n  // 设置代码折叠\n  setFoldGutter: function (foldGutter) {\n    if (this.props.mode === 'rules') {\n      return;\n    }\n    foldGutter = foldGutter !== false && FOLD_MODE.indexOf(this._mode) !== -1;\n    if (this._foldGutter !== foldGutter && this._editor) {\n      this._foldGutter = foldGutter;\n      this._editor.setOption('foldGutter', foldGutter);\n      this._editor.setOption('gutters', foldGutter ? GUTTER_STYLE : []);\n    }\n  },\n\n  isRulesEditor: function () {\n    return this.props.mode === 'rules' || this._mode === 'rules';\n  },\n  componentDidMount: function () {\n    var timeout;\n    var timer;\n    var self = this;\n    var elem = findDOMNode(self.refs.editor);\n    var $elem = $(elem);\n    var editor = CodeMirror(elem);\n    var preKeyeord;\n    var preCursor;\n    var find = function(keyword, prev, start) {\n      if (!$elem.is(':visible')) {\n        return;\n      }\n      var cursor = start ? null : editor.getCursor();\n      var value  =editor.getValue();\n      if (keyword !== preKeyeord) {\n        if (cursor && preCursor && preKeyeord && cursor.ch === preCursor.ch && cursor.line === preCursor.line) {\n          cursor.ch = Math.max(cursor.ch + (prev ? preKeyeord.length : -preKeyeord.length), 0);\n        }\n        preKeyeord = keyword;\n      } else if (prev) {\n        if (cursor) {\n          cursor.ch = Math.max(cursor.ch - preKeyeord.length, 0);\n        } else {\n          var len = value.length;\n          cursor = {line: len, ch: len};\n        }\n      }\n      if (keyword) {\n        events.editorMatchedCount = value.toLowerCase().split(keyword.toLowerCase()).length - 1;\n      } else {\n        events.editorMatchedCount = 0;\n      }\n      cursor = editor.getSearchCursor(keyword, cursor, SEARCH_OPTIONS);\n      preCursor = null;\n      var result = prev ? cursor.findPrevious() : cursor.findNext();\n      if(result) {\n        editor.setSelection(cursor.from(), cursor.to());\n        editor.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);\n      } else if (!start) {\n        find(keyword, prev, true);\n      } else {\n        preCursor = editor.getCursor();\n        preCursor && editor.setSelection(preCursor, preCursor);\n      }\n      preCursor = preCursor || editor.getCursor();\n    };\n    var findEditor = function(e, keyword) {\n      find(keyword, e.type === 'findEditorPrev');\n    };\n    self._editor = editor;\n\n    events.on('findEditorNext', findEditor);\n    events.on('findEditorPrev', findEditor);\n\n    events.on('updatePlugins', function() {\n      if (self.isRulesEditor()) {\n        timer && clearTimeout(timer);\n        if (self.props.hide) {\n          timer = null;\n          self._waitingUpdate = true;\n        } else {\n          timer = setTimeout(function() {\n            timer = null;\n            if (self.isRulesEditor()) {\n              if (self.props.hide) {\n                self._waitingUpdate = true;\n              } else {\n                self._waitingUpdate = false;\n                editor.setOption('mode', '');\n                editor.setOption('mode', 'rules');\n              }\n            }\n          }, 600);\n        }\n      }\n    });\n    editor.on('change', function (e) {\n      if (\n        typeof self.props.onChange == 'function' &&\n        editor.getValue() !== (self.props.value || '')\n      ) {\n        self.props.onChange.call(self, e);\n      }\n    });\n    editor.on('mousedown', function (_, e) {\n      if (!(e.ctrlKey || e.metaKey)) {\n        return;\n      }\n      var target = $(e.target);\n      if (\n        target.hasClass('cm-js-type') ||\n        target.hasClass('cm-js-at') ||\n        target.hasClass('cm-js-http-url')\n      ) {\n        e.preventDefault();\n      }\n    });\n    self._init(true);\n    $(elem).find('.CodeMirror').addClass('fill');\n    setTimeout(resize, 10);\n    $(window).on('resize', resetThrottle);\n    events.on('editorResize', resetThrottle);\n    function resize() {\n      timeout = null;\n      var height = elem.offsetHeight || 0;\n      var width = elem.offsetWidth || 0;\n      if (height < 10 || width < 10) {\n        timeout && clearTimeout(timeout);\n        timeout = setTimeout(resize, 300);\n      } else {\n        editor.setSize(width, height);\n      }\n    }\n    function resetThrottle() {\n      timeout = timeout || setTimeout(resize, 30);\n    }\n    var getCh = function (ch, dis) {\n      return Math.max(0, ch + dis);\n    };\n    $(elem).on('dblclick', '.CodeMirror-linenumber', function (e) {\n      var num = parseInt($(e.target).text(), 10);\n      if (!(num > 0)) {\n        return;\n      }\n      var lineNum = num - 1;\n      var line = editor.getLine(lineNum);\n      if (!line || !line.trim()) {\n        return;\n      }\n      var isRules = self.isRulesEditor();\n      var commentRE = isRules ? RULES_COMMENT_RE : JS_COMMENT_RE;\n      var origLine = line;\n      if (commentRE.test(line)) {\n        line = line.replace(commentRE, '$1');\n      } else {\n        line = (isRules ? '#' : '//') + (/^\\s/.test(line) ? '' : ' ') + line;\n      }\n      var list = editor.listSelections();\n      var resetRange;\n      var len = list && list.length;\n      var dis = line.length - origLine.length;\n      if (list && list.length) {\n        for (var i = 0; i < len; i++) {\n          var pre = list[i];\n          var hLine = pre.head.line;\n          var aLine = pre.anchor.line;\n          if (hLine === lineNum) {\n            resetRange = true;\n            pre.head.ch = getCh(pre.head.ch, dis);\n            if (aLine === hLine && pre.head !== pre.anchor) {\n              pre.anchor.ch = getCh(pre.anchor.ch, dis);\n            }\n            break;\n          }\n          if (aLine === lineNum) {\n            resetRange = true;\n            pre.anchor.ch = getCh(pre.anchor.ch, dis);\n            break;\n          }\n        }\n      }\n      editor.replaceRange(\n        line + '\\n',\n        { line: lineNum, ch: 0 },\n        { line: num, ch: 0 }\n      );\n      if (resetRange) {\n        editor.setSelections(list);\n      }\n      events.trigger('toggleCommentInEditor');\n    });\n    $(elem).on('keydown', function (e) {\n      var isRules = self.isRulesEditor();\n      var isJS = self._mode == 'javascript';\n      if (isRules) {\n        var options = {\n          name: self.props.mode,\n          url: location.href\n        };\n        if (!e.ctrlKey && !e.metaKey && e.keyCode === 112) {\n          var helpUrl = rulesHint.getHelpUrl(self._editor, options);\n          if (!helpUrl) {\n            return;\n          }\n          window.open(helpUrl);\n          e.stopPropagation();\n          e.preventDefault();\n          return true;\n        }\n        try {\n          var onKeyDown = window.parent.onWhistleRulesEditorKeyDown;\n          if (\n            typeof onKeyDown === 'function' &&\n            onKeyDown(e, options) === false\n          ) {\n            e.stopPropagation();\n            e.preventDefault();\n            return true;\n          }\n        } catch (e) {}\n      }\n      if (e.shiftKey && (e.metaKey || e.ctrlKey)) {\n        var onFormat = self.props.onFormat;\n        var onInspect = self.props.onInspect;\n        if (typeof onFormat === 'function' && e.keyCode === 70) {\n          onFormat(e);\n        } else if (typeof onInspect === 'function' && e.keyCode === 73) {\n          onInspect(e);\n        }\n      }\n      if (\n        (!isRules && !isJS) ||\n        !(e.ctrlKey || e.metaKey) ||\n        e.keyCode != 191\n      ) {\n        return;\n      }\n\n      var list = editor.listSelections();\n      if (!list || !list.length) {\n        return;\n      }\n      var commentRE = isRules ? RULES_COMMENT_RE : JS_COMMENT_RE;\n      var isShiftKey = e.shiftKey;\n      var isEmpty;\n      var ranges = [];\n      list.forEach(function (range) {\n        var anchor = range.anchor;\n        var head = range.head;\n        var lines = [];\n        var hasComment, hasRule, revert;\n\n        if (anchor.line > head.line) {\n          revert = anchor;\n          anchor = head;\n          head = revert;\n        }\n\n        for (var i = anchor.line; i <= head.line; i++) {\n          var line = editor.getLine(i);\n          if (commentRE.test(line)) {\n            hasComment = true;\n          } else if (NO_SPACE_RE.test(line)) {\n            hasRule = true;\n          }\n          lines.push(line);\n        }\n\n        if ((isEmpty = !hasComment && !hasRule)) {\n          return;\n        }\n        var lastIndex, firstLine, lastLine;\n        if (hasRule) {\n          lastIndex = lines.length - 1;\n          firstLine = lines[0];\n          lastLine = lines[lastIndex];\n          lines = lines.map(function (line) {\n            if (!NO_SPACE_RE.test(line)) {\n              return line;\n            }\n            if (isShiftKey && commentRE.test(line)) {\n              return line.replace(commentRE, '$1');\n            }\n            return (isRules ? '# ' : '// ') + line;\n          });\n        } else {\n          firstLine = lines[0];\n          lastIndex = lines.length - 1;\n          lastLine = lines[lastIndex];\n          lines = lines.map(function (line) {\n            return line.replace(commentRE, '$1');\n          });\n        }\n        if (anchor.ch != 0) {\n          anchor.ch += lines[0].length - firstLine.length;\n          if (anchor.ch < 0) {\n            anchor.ch = 0;\n          }\n        }\n        if (head.ch != 0 && head != anchor) {\n          head.ch += lines[lastIndex].length - lastLine.length;\n          if (head.ch < 0) {\n            head.ch = 0;\n          }\n        }\n        if (revert) {\n          editor.replaceRange(\n            lines.join('\\n') + '\\n',\n            { line: head.line + 1, ch: 0 },\n            { line: anchor.line, ch: 0 }\n          );\n          ranges.push({ anchor: head, head: anchor });\n        } else {\n          editor.replaceRange(\n            lines.join('\\n') + '\\n',\n            { line: anchor.line, ch: 0 },\n            { line: head.line + 1, ch: 0 }\n          );\n          ranges.push({ anchor: anchor, head: head });\n        }\n      });\n      if (!isEmpty) {\n        editor.setSelections(ranges);\n      }\n    });\n  },\n  _init: function (init) {\n    var self = this;\n    var mode = self.props.mode;\n    if (self._waitingUpdate && mode === 'rules') {\n      self._editor.setOption('mode', '');\n      self._mode = '';\n    }\n    self.setMode(mode);\n    self._waitingUpdate = false;\n    var value = self.props.value;\n    var history = self._editor.getHistory();\n    if (init && value && value.length > INIT_LENGTH) {\n      var elem = message.info('Loading...');\n      self.timer = setTimeout(function () {\n        elem.hide();\n        self.timer = null;\n        self.setValue(self.props.value); // 节流\n        self.setHistory(init, history);\n      }, 500);\n    } else if (!self.timer) {\n      self.setValue(value);\n      self.setHistory(init, history);\n    }\n    self.setTheme(self.props.theme);\n    self.setFontSize(self.props.fontSize);\n    self.setTheme(self.props.theme);\n    self.showLineNumber(self.props.lineNumbers || false);\n    self.showLineWrapping(self.props.lineWrapping || false);\n    self.setReadOnly(self.props.readOnly || false);\n    self.setAutoComplete();\n    self.setFoldGutter(self.props.foldGutter);\n  },\n  componentDidUpdate: function () {\n    this._init();\n  },\n  render: function () {\n    return (\n      <div\n        tabIndex=\"0\"\n        ref=\"editor\"\n        className=\"fill v-box w-list-content w-fix-drag\"\n      />\n    );\n  }\n});\n\nmodule.exports = Editor;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/empty.js",
    "content": "var React = require('react');\n\nvar Empty = React.createClass({\n  render: function () {\n    return (\n      <div className={'box fill w-empty-data' + (this.props.hide ? ' hide' : '')}>Empty</div>\n    );\n  }\n});\n\nmodule.exports = Empty;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/enable-https-btn.js",
    "content": "var React = require('react');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar Icon = require('./icon');\nvar HelpIcon = require('./help-icon');\n\nvar EnableHttpsBtn = React.createClass({\n  showHttpsSettingsDialog: function () {\n    events.trigger('showHttpsSettingsDialog');\n  },\n  render: function () {\n    if (dataCenter.isMultiEnv()) {\n      return null;\n    }\n    if (!dataCenter.isCapture) {\n      return <Icon name=\"lock\" className=\"w-enable-https-btn\" onClick={this.showHttpsSettingsDialog} />;\n    }\n    return <HelpIcon className=\"w-enable-https-help\" docsUrl=\"faq.html#tunnel-to\" />;\n  }\n});\n\nmodule.exports = EnableHttpsBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/enabled-rules.js",
    "content": "var React = require('react');\nvar util = require('./util');\nvar Properties = require('./properties');\nvar Empty = require('./empty');\nvar Dialog = require('./dialog');\nvar CloseBtn = require('./close-btn');\n\nvar EnabledRulesDialog = React.createClass({\n  getInitialState: function () {\n    return { list: [] };\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.enabledRules.isVisible();\n  },\n  handleClickLocate: function (rule) {\n    this.hide();\n    util.handleClickLocate(rule);\n  },\n  show: function (list) {\n    this.refs.enabledRules.show();\n    this.setState({ list: list });\n  },\n  hide: function () {\n    this.refs.enabledRules.hide();\n  },\n  render: function () {\n    var list = this.state.list || [];\n\n    return (\n      <Dialog ref=\"enabledRules\" wstyle=\"w-enabled-rules-dialog\">\n          <div className=\"modal-header\">\n            <h4>Enabled Rules</h4>\n            <CloseBtn />\n          </div>\n          {list.length ? <Properties\n            name=\"Rules\"\n            wrapClass=\"box fill w-enabled-rules\"\n            onClickLocate={this.handleClickLocate}\n            modal={list}\n          /> : <Empty />}\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = EnabledRulesDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/events.js",
    "content": "var $ = require('jquery');\n\nmodule.exports = $({});\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/expand-collapse.js",
    "content": "var React = require('react');\nvar util = require('./util');\n\nvar MIN_LENGTH = 1024;\nvar EXPAND_LENGTH = 1024 * 32;\nvar MAX_LENGTH = EXPAND_LENGTH * 3;\n\nvar ExpandCollapse = React.createClass({\n  getInitialState: function () {\n    return { expandLength: MIN_LENGTH };\n  },\n  componentWillReceiveProps: function (nextProps) {\n    if (\n      nextProps.text !== this.props.text ||\n      this.props.wStyle !== nextProps.wStyle\n    ) {\n      this.state.expandLength = MIN_LENGTH;\n    }\n  },\n  onCollapse: function () {\n    this.setState({ expandLength: MIN_LENGTH });\n  },\n  onExpand: function () {\n    this.setState({ expandLength: this.state.expandLength + EXPAND_LENGTH });\n  },\n  viewAll: function () {\n    util.openEditor(this.props.text);\n  },\n  render: function () {\n    var text = this.props.text;\n    var len = (text && text.length) || 0;\n    var style = this.props.wStyle;\n    if (len < 2100) {\n      return <span style={style}>{text}</span>;\n    }\n    var expandLength = this.state.expandLength;\n    var isCollapse = expandLength >= len;\n    var viewAll = !isCollapse && expandLength > MAX_LENGTH;\n    return (\n      <span style={style}>\n        {isCollapse ? text : text.substring(0, expandLength) + '...'}\n        {viewAll ? (\n          <button onClick={this.viewAll} className=\"w-expand-collapse\">\n            ViewAll\n          </button>\n        ) : isCollapse ? undefined : (\n          <button onClick={this.onExpand} className=\"w-expand-collapse\">\n            Expand\n          </button>\n        )}\n        {expandLength > MIN_LENGTH ? (\n          <button onClick={this.onCollapse} className=\"w-expand-collapse\">\n            Collapse\n          </button>\n        ) : undefined}\n      </span>\n    );\n  }\n});\n\nmodule.exports = ExpandCollapse;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/export-dialog.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar ShareViaUrlBtn = require('./share-via-url-btn');\nvar util = require('./util');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar ExportDialog = React.createClass({\n  getInitialState: function () {\n    return { filename: '' };\n  },\n  show: function (name, data) {\n    var self = this;\n    self.refs.exportDialog.show();\n    setTimeout(function () {\n      var input = findDOMNode(self.refs.input);\n      input.focus();\n      input.select();\n    }, 500);\n    name = name || 'network';\n    self.setState({\n      name: name,\n      title: util.getDialogTitle(name, 'Export'),\n      showOptions: name === 'network',\n      data: data\n    });\n  },\n  getInputValue: function() {\n    return util.formatFilename(findDOMNode(this.refs.input).value.trim());\n  },\n  getFilename: function () {\n    var name = this.state.name;\n    var suffix = name === 'console' || name === 'server' ? '.log' : '.txt';\n    var filename = this.getInputValue();\n    if (filename) {\n      if (!/\\.(txt|json)/i.test(filename)) {\n        filename += suffix;\n      }\n      return filename;\n    }\n    switch (name) {\n    case 'networkSettings':\n      filename = 'network_settings_';\n      break;\n    case 'composer':\n      filename = 'composer_';\n      break;\n    case 'console':\n      filename = 'console_';\n      break;\n    case 'server':\n      filename = 'server_';\n      break;\n    case 'rulesSettings':\n      filename = 'rules_settings_';\n      break;\n    case 'valuesSettings':\n      filename = 'values_settings_';\n      break;\n    case 'mock':\n      filename = 'mock_';\n      break;\n    default:\n      filename = 'network_';\n    }\n    return filename + util.formatDate() + suffix;\n  },\n  hide: function () {\n    this.refs.exportDialog.hide();\n  },\n  export: function(e) {\n    if (e && e.type !== 'click' && e.keyCode !== 13) {\n      return;\n    }\n    var data = this.state.data;\n    if (typeof data === 'function') {\n      data = data();\n    }\n    this.hide();\n    util.download(data, this.getFilename());\n    findDOMNode(this.refs.input).value = '';\n  },\n  filterFilename: function (e) {\n    this.setState({ filename: util.formatFilename(e.target.value) });\n  },\n  onShare: function(err) {\n    if (!err) {\n      this.hide();\n      findDOMNode(this.refs.input).value = '';\n    }\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.exportDialog.isVisible();\n  },\n  render: function () {\n    var state = this.state;\n    var showOptions = state.showOptions;\n\n    return (\n      <Dialog ref=\"exportDialog\" wstyle={'w-ie-dialog' + (showOptions ? ' w-export-network' : '')}>\n        <div className=\"modal-header\">\n          <h4>{state.title}</h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body\">\n          <input\n            ref=\"input\"\n            value={state.filename}\n            onChange={this.filterFilename}\n            onKeyDown={this.export}\n            placeholder=\"Enter filename (optional)\"\n            className=\"form-control\"\n            maxLength=\"64\"\n          />\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          <ShareViaUrlBtn type={state.name} data={state.data} getFilename={this.getInputValue} onComplete={this.onShare} />\n          <button\n            type=\"button\"\n            className=\"btn btn-primary w-fmt-btn\"\n            data-dismiss=\"modal\"\n            onClick={this.export}\n          >\n            Export\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = ExportDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/filter-btn.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar FilterBtn = React.createClass({\n  getInitialState: function () {\n    return {\n      hasFilterText: !!dataCenter.filterIsEnabled()\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('filterChanged', function () {\n      var hasFilterText = !!dataCenter.filterIsEnabled();\n      if (hasFilterText !== self.state.hasFilterText) {\n        self.setState({\n          hasFilterText: !!dataCenter.filterIsEnabled()\n        });\n      }\n    });\n    events.on('shakeSettings', function () {\n      setTimeout(function () {\n        util.shakeElem($(ReactDOM.findDOMNode(self.refs.settings)));\n      }, 100);\n    });\n  },\n  render: function () {\n    var props = this.props;\n    var hide = props.hide;\n    var isNetwork = props.isNetwork;\n    var className = props.backRulesFirst || (isNetwork && this.state.hasFilterText ? ' w-menu-enable' : '');\n    return (\n      <a\n        ref=\"settings\"\n        onClick={props.onClick}\n        className={'w-settings-menu' + className}\n        style={{ display: hide ? 'none' : '' }}\n        draggable=\"false\"\n      >\n        <Icon name=\"cog\" />\n        Settings\n      </a>\n    );\n  }\n});\nmodule.exports = FilterBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/filter-input.js",
    "content": "require('../css/filter-input.css');\nvar util = require('./util');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar storage = require('./storage');\nvar win = require('./win');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_LEN = 128;\nvar TYPES = ['JSON', 'HTML', 'CSS', 'JS', 'Font', 'Img', 'Media', 'WS', 'Tunnel', 'Wasm', 'Mock', 'Rules', 'Import', 'Composer', 'Error', 'Other'];\nvar TITLES = {\n  JS: 'JavaScript',\n  Img: 'Image',\n  WS: 'WebSocket',\n  Wasm: 'WebAssembly',\n  Mock: 'Show mock requests',\n  Error: 'Show error requests',\n  Import: 'Show import sessions',\n  Composer: 'Show composer requests',\n  Rules: 'Show requests matched rules'\n};\n\nvar FilterInput = React.createClass({\n  getInitialState: function () {\n    var hintKey = this.props.hintKey;\n    this.allHintList = [];\n    if (hintKey) {\n      try {\n        var hintList = JSON.parse(storage.get(hintKey));\n        if (Array.isArray(hintList)) {\n          var map = {};\n          this.allHintList = hintList\n            .map(function (key) {\n              return typeof key === 'string' ? key.substring(0, MAX_LEN) : null;\n            })\n            .filter(function (key) {\n              if (!key || map[key]) {\n                return false;\n              }\n              map[key] = 1;\n              return true;\n            })\n            .slice(0, MAX_LEN);\n        }\n      } catch (e) {}\n    }\n    return { hintList: [], filterType: '' };\n  },\n  componentDidMount: function () {\n    var self = this;\n    self.hintElem = $(findDOMNode(this.refs.hints));\n    $(document.body).on('mousedown', function(e) {\n      if (self.state.hintList !== null && !$(e.target).closest('.w-filter-con').length) {\n        self.hideHints();\n      }\n    });\n    var onFilterTypeChange = this.props.onFilterTypeChange;\n    if (typeof onFilterTypeChange === 'function') {\n      onFilterTypeChange(self.state.filterType);\n    }\n  },\n  focus: function() {\n    var input = findDOMNode(this.refs.input);\n    input.select();\n    input.focus();\n  },\n  addHint: function () {\n    var value = this.state.filterText;\n    value = value && value.trim();\n    if (value) {\n      var list = this.allHintList;\n      var index = list.indexOf(value);\n      if (index !== -1) {\n        list.splice(index, 1);\n      }\n      if (list.length > MAX_LEN) {\n        list.splice(0, 1);\n      }\n      list.push(value);\n      try {\n        storage.set(this.props.hintKey, JSON.stringify(list));\n      } catch (e) {}\n    }\n  },\n  filterHints: function (keyword) {\n    keyword = keyword ? keyword.trim() : '';\n    var count = 10;\n    var addonHints = this.props.addonHints || [];\n    if (!keyword) {\n      return addonHints.concat(this.allHintList.slice(-count));\n    }\n    addonHints = addonHints.slice(1);\n    count += addonHints.length;\n    var allHintList = addonHints.concat(this.allHintList);\n    var list = [];\n    var lk = keyword.toLowerCase();\n    var notColon = keyword.indexOf(':') === -1;\n    for (var i = allHintList.length - 1; i >= 0; i--) {\n      var key = allHintList[i];\n      if (key !== keyword && key.toLowerCase().indexOf(lk) !== -1 &&\n      (addonHints.indexOf(key) === -1 || notColon)) {\n        list.unshift(key);\n        if (list.length >= count) {\n          return list;\n        }\n      }\n    }\n    return list;\n  },\n  onFilterChange: function (e) {\n    this.changeInput(e ? e.target.value : '');\n  },\n  changeInput: function (value) {\n    var self = this;\n    self.props.onChange && self.props.onChange(value);\n    var hintKey = self.props.hintKey;\n    hintKey && clearTimeout(self.timer);\n    this.state.filterText = value;\n    self.setState({ hintList: this.filterHints(value) }, function () {\n      if (hintKey) {\n        self.timer = setTimeout(this.addHint, 10000);\n      }\n    });\n  },\n  onClick: function (e) {\n    this.changeInput(e.target.title);\n    this.hideHints();\n  },\n  hideHints: function () {\n    this.setState({ hintList: null });\n    this.addHint();\n  },\n  showHints: function (e) {\n    var self = this;\n    if (!self.props.hintKey) {\n      return;\n    }\n    self.setState({ hintList: this.filterHints(this.state.filterText) }, e && function() {\n      self.hintElem.scrollTop(1000000000);\n    });\n    var top = findDOMNode(self.refs.input).getBoundingClientRect().y - 130;\n    findDOMNode(self.refs.hints).style.setProperty('--h-hints', Math.max(Math.min(top, 360), 200) + 'px');\n  },\n  onFilterKeyDown: function (e) {\n    var elem;\n    if (e.keyCode === 27) {\n      var hintList = this.state.hintList;\n      if (hintList === null) {\n        this.showHints();\n      } else {\n        this.hideHints();\n      }\n    } else if (e.keyCode === 38) {\n      // up\n      elem = this.hintElem.find('.w-active');\n      if (this.state.hintList === null) {\n        this.showHints();\n      }\n      if (elem.length) {\n        elem.removeClass('w-active');\n        elem = elem.prev('li').addClass('w-active');\n      }\n\n      if (!elem.length) {\n        elem = this.hintElem.find('li:last');\n        elem.addClass('w-active');\n      }\n      util.ensureVisible(elem, this.hintElem);\n      e.preventDefault();\n    } else if (e.keyCode === 40) {\n      // down\n      elem = this.hintElem.find('.w-active');\n      if (this.state.hintList === null) {\n        this.showHints();\n      }\n      if (elem.length) {\n        elem.removeClass('w-active');\n        elem = elem.next('li').addClass('w-active');\n      }\n\n      if (!elem.length) {\n        elem = this.hintElem.find('li:first');\n        elem.addClass('w-active');\n      }\n      util.ensureVisible(elem, this.hintElem);\n      e.preventDefault();\n    } else if (e.keyCode === 13) {\n      elem = this.hintElem.find('.w-active');\n      var value = elem.attr('title');\n      if (value) {\n        this.changeInput(value);\n        this.hideHints();\n      }\n    } else if (e.ctrlKey || e.metaKey) {\n      if (e.keyCode == 68) {\n        this.clearFilterText();\n        e.preventDefault();\n        e.stopPropagation();\n      } else if (e.keyCode == 88) {\n        e.stopPropagation();\n      }\n    }\n    if (typeof this.props.onKeyDown === 'function') {\n      this.props.onKeyDown(e);\n    }\n  },\n  clear: function () {\n    var self = this;\n    win.confirm('Confirm to clear history?', function (sure) {\n      if (sure) {\n        storage.set(self.props.hintKey, '');\n        self.allHintList = [];\n        self.hideHints();\n      }\n    });\n  },\n  clearFilterText: function () {\n    this.props.onChange && this.props.onChange('');\n    var hintList = null;\n    if (document.activeElement === findDOMNode(this.refs.input)) {\n      hintList = this.filterHints();\n    }\n    var hasChanged = this.state.filterText;\n    var self = this;\n    this.setState({ filterText: '', hintList: hintList }, function() {\n      if (hasChanged) {\n        self.onFilterChange();\n      }\n    });\n  },\n  handleFilterType: function (e) {\n    var target = e.target;\n    if (target.nodeName !== 'SPAN') {\n      return;\n    }\n    var type = target.textContent;\n    var filterType = type === 'All' ? '' : type;\n    if (filterType === this.state.filterType) {\n      return;\n    }\n    this.setState({ filterType: filterType });\n    this.props.onFilterTypeChange(filterType);\n  },\n  renderTypes: function () {\n    var filterType = this.state.filterType;\n    if (TYPES.indexOf(filterType) === -1) {\n      filterType = '';\n    }\n    return (\n      <div className=\"w-filter-type-bar\" onClick={this.handleFilterType} onMouseDown={util.preventBlur}>\n        <span className={filterType ? undefined : 'w-active'}>All</span>\n        {TYPES.map(function (type) {\n          return <span key={type} title={TITLES[type] || type} className={filterType === type ? 'w-active' : undefined}>{type}</span>;\n        })}\n      </div>\n    );\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var filterText = self.state.filterText || '';\n    var hintKey = props.hintKey;\n    var hintList = self.state.hintList;\n    var addonHints = props.addonHints || [];\n    var showTypes = typeof props.onFilterTypeChange === 'function';\n\n    return (\n      <div className={'w-filter-con' + (showTypes ? ' w-filter-show-types' : '')} style={props.wStyle}>\n        {hintKey ? (\n          <div\n            className=\"w-filter-hint\"\n            style={{ display: hintList && hintList.length ? '' : 'none' }}\n            onMouseDown={util.preventBlur}\n          >\n            <div className=\"w-filter-bar\">\n              <a onClick={this.clear}>\n                <Icon name=\"trash\" />\n                Clear history\n              </a>\n              <CloseBtn onClick={self.hideHints} />\n            </div>\n            <ul ref=\"hints\">\n              {hintList &&\n                hintList.map(function (key) {\n                  var title = key;\n                  if (addonHints.indexOf(key) !== -1) {\n                    title = key.substring(0, key.indexOf(':') + 1);\n                  }\n                  return (\n                    <li key={key} onClick={self.onClick} title={title}>\n                      {key}\n                    </li>\n                  );\n                })}\n            </ul>\n          </div>\n        ) : undefined}\n        {showTypes ? self.renderTypes() : undefined}\n        <input\n          type=\"text\"\n          ref=\"input\"\n          value={filterText}\n          onChange={self.onFilterChange}\n          onKeyDown={self.onFilterKeyDown}\n          onFocus={self.showHints}\n          onDoubleClick={self.showHints}\n          onBlur={self.hideHints}\n          className=\"w-filter-input\"\n          maxLength={MAX_LEN}\n          placeholder={'Type filter text' + (props.placeholder || '')}\n        />\n        <button\n          onMouseDown={util.preventBlur}\n          onClick={self.clearFilterText}\n          style={{ display: self.state.filterText ? 'block' : 'none' }}\n          type=\"button\"\n          className=\"close w-clear-input\"\n          title=\"Ctrl[Command]+D\"\n        >&times;</button>\n      </div>\n    );\n  }\n});\n\nmodule.exports = FilterInput;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/forward-back-btn.js",
    "content": "var React = require('react');\nvar Icon = require('./icon');\n\nvar FbBtn = React.createClass({\n  onForward: function() {\n    var props = this.props;\n    if (!props.disabledForward && props.onForward) {\n      props.onForward();\n    }\n  },\n  onBack: function() {\n    var props = this.props;\n    if (!props.disabledBack && props.onBack) {\n      props.onBack();\n    }\n  },\n  render: function() {\n    var props = this.props;\n    var disabledForward = props.disabledForward;\n    var disabledBack = props.disabledBack;\n\n    return (\n      <div className=\"w-json-bar\">\n        <Icon\n          name=\"menu-left\"\n          className={disabledBack ? 'w-disabled' : ''}\n          title={disabledBack ? '' : 'Back'}\n          onClick={this.onBack}\n        />\n        <Icon\n          name=\"menu-right\"\n          className={disabledForward ? 'w-disabled' : ''}\n          title={disabledForward ? '' : 'Forward'}\n          onClick={this.onForward}\n        />\n      </div>\n    );\n  }\n});\n\nmodule.exports = FbBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/frame-composer.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar message = require('./message');\nvar storage = require('./storage');\nvar win = require('./win');\nvar Icon = require('./icon');\n\nvar MAX_FILE_SIZE = 1024 * 1025;\nvar MAX_LENGTH = 1024 * 64;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar FrameComposer = React.createClass({\n  getInitialState: function () {\n    return {\n      isHexText: !!storage.get('showHexTextFrame'),\n      isCRLF: !!storage.get('useCRLFrame')\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    var framesCtx = this.props.framesCtx;\n    self.dataField = findDOMNode(self.refs.uploadData);\n    self.dataForm = findDOMNode(self.refs.uploadDataForm);\n    framesCtx.on('composeFrame', function (e, frame) {\n      if (frame) {\n        var body;\n        if (self.state.isHexText) {\n          body = util.getHexText(util.getHex(frame));\n        } else {\n          body = util.getBody(frame, true);\n        }\n        self.setTextarea(body);\n        setTimeout(function() {\n          findDOMNode(self.refs.textarea).focus();\n        }, 60);\n      }\n    });\n    framesCtx.on('replayFrame', function (e, frame) {\n      if (!frame) {\n        return;\n      }\n      framesCtx.trigger('enableRecordFrame');\n      self.send(\n        {\n          target: frame.isClient ? 'server' : 'client',\n          type: frame.opcode == 1 ? 'text' : 'bin',\n          base64: frame.base64\n        },\n        function () {\n          framesCtx.trigger('autoRefreshFrames');\n        }\n      );\n    });\n    var text = storage.get('composeFrameData');\n    this.setTextarea(String(text || ''));\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  uploadTextToServer: function () {\n    this.target = 'server';\n    this.dataType = 'text';\n    this.dataField.click();\n  },\n  uploadBinToServer: function () {\n    this.target = 'server';\n    this.dataType = 'bin';\n    this.dataField.click();\n  },\n  uploadTextToClient: function () {\n    this.target = 'client';\n    this.dataType = 'text';\n    this.dataField.click();\n  },\n  uploadBinToClient: function () {\n    this.target = 'client';\n    this.dataType = 'bin';\n    this.dataField.click();\n  },\n  onFormChange: function () {\n    this.uploadForm(new FormData(this.dataForm));\n    this.dataField.value = '';\n  },\n  uploadForm: function (form) {\n    var file = form.get('uploadData');\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Maximum file size: 1MB');\n    }\n    var self = this;\n    var params = {\n      target: self.target,\n      type: self.dataType\n    };\n    util.readFileAsBase64(file, function (base64) {\n      params.base64 = base64;\n      self.send(params);\n      self.dataField.value = '';\n    });\n  },\n  send: function (params, cb) {\n    var data = this.props.data;\n    if (!data) {\n      return;\n    }\n    params.reqId = data.id;\n    this.props.framesCtx.trigger('enableRecordFrame');\n    dataCenter.socket.send(params, function (data, xhr) {\n      if (!data) {\n        return util.showSystemError(xhr);\n      }\n      if (data.ec !== 0) {\n        return message.error('Server temporarily unavailable. Please try again shortly');\n      }\n      cb && cb();\n    });\n  },\n  onSend: function (e) {\n    var value = this.state.text;\n    var self = this;\n    if (!value || self.sendTimer) {\n      return;\n    }\n    var target = e.target;\n    var base64;\n    if (this.state.isHexText) {\n      base64 = util.getBase64FromHexText(value);\n      value = undefined;\n    } else if (this.state.isCRLF) {\n      value = value.replace(/\\r\\n|\\r|\\n/g, '\\r\\n');\n    }\n    var params = {\n      type: target.nodeName === 'A' ? 'bin' : 'text',\n      target: target.getAttribute('data-target') ? 'server' : 'client',\n      text: value,\n      base64: base64\n    };\n    self.setState({});\n    self.sendTimer = setTimeout(function () {\n      self.sendTimer = null;\n      self.setState({});\n    }, 5000);\n    self.send(params, function () {\n      clearTimeout(self.sendTimer);\n      self.sendTimer = null;\n      self.props.framesCtx.trigger('autoRefreshFrames');\n      self.setState({});\n    });\n  },\n  format: function () {\n    var data = util.parseRawJson(this.state.text);\n    if (data) {\n      this.setState({\n        text: JSON.stringify(data, null, '  ')\n      });\n    }\n  },\n  onForamt: function (e) {\n    util.handleFormat(e, this.format);\n    util.handleTab(e);\n  },\n  setTextarea: function (text) {\n    this.setState({ text: text });\n    clearTimeout(this.timer);\n    this.timer = setTimeout(function () {\n      storage.set('composeFrameData', text);\n    }, 600);\n  },\n  onTextareaChange: function (e) {\n    this.setTextarea(e.target.value);\n  },\n  preventDefault: function (e) {\n    e.preventDefault();\n  },\n  onTypeChange: function (e) {\n    var isHexText = e.target.checked;\n    storage.set('showHexTextFrame', isHexText ? 1 : '');\n    this.setState({ isHexText: isHexText });\n  },\n  onCRLFChange: function (e) {\n    var isCRLF = e.target.checked;\n    storage.set('useCRLFrame', isCRLF ? 1 : '');\n    this.setState({ isCRLF: isCRLF });\n  },\n  render: function () {\n    var data = this.props.data || '';\n    util.socketIsClosed(data);\n    var state = this.state;\n    var text = state.text || '';\n    var isHexText = state.isHexText;\n    var isCRLF = state.isCRLF;\n    var closed = data.closed;\n    var isHttps = data.isHttps;\n    var leftStyle = isHttps ? { left: 0 } : undefined;\n    var displayStyle = isHttps ? { display: 'none' } : undefined;\n    var tips = closed ? 'The connection is closed' : undefined;\n    var disabled = closed || this.sendTimer;\n    return (\n      <div\n        onDrop={this.onDrop}\n        className={\n          'fill v-box w-frames-com' +\n          (this.props.hide ? ' hide' : '')\n        }\n      >\n        <div className=\"w-frames-com-action\">\n          <label\n            className={\n              'w-frames-hex-data' + (isHexText ? ' w-frames-checked' : '')\n            }\n          >\n            <input\n              checked={isHexText}\n              onChange={this.onTypeChange}\n              type=\"checkbox\"\n            />\n            HexText\n          </label>\n          <label\n            className={\n              'w-frames-crlf' +\n              (isHexText ? ' hide' : '') +\n              (isCRLF ? ' w-frames-checked' : '')\n            }\n          >\n            <input\n              checked={isCRLF}\n              onChangeCapture={this.onCRLFChange}\n              type=\"checkbox\"\n            />\n            \\r\\n\n          </label>\n          <div className=\"btn-group\">\n            <button\n              disabled={disabled}\n              title={tips}\n              onMouseDown={this.preventDefault}\n              onClick={this.onSend}\n              type=\"button\"\n              className=\"btn btn-default btn-sm\"\n            >\n              <Icon name=\"arrow-left\" />\n              Send To Client\n            </button>\n            <button\n              disabled={disabled}\n              title={tips}\n              type=\"button\"\n              className=\"btn btn-default dropdown-toggle\"\n              data-toggle=\"dropdown\"\n              aria-haspopup=\"true\"\n              aria-expanded=\"false\"\n            >\n              <span className=\"caret\"></span>\n            </button>\n            <ul\n              style={leftStyle}\n              className={'dropdown-menu' + (closed ? ' hide' : '')}\n            >\n              <li style={displayStyle}>\n                <a onClick={this.onSend}>Send Binary Data</a>\n              </li>\n              <li>\n                <a onClick={this.uploadTextToClient}>\n                  {isHttps ? 'Upload ' : 'Upload Text Data'}\n                </a>\n              </li>\n              <li style={displayStyle}>\n                <a onClick={this.uploadBinToClient}>Upload Binary Data</a>\n              </li>\n            </ul>\n          </div>\n          <div className=\"btn-group\">\n            <button\n              disabled={disabled}\n              title={tips}\n              onMouseDown={this.preventDefault}\n              data-target=\"server\"\n              onClick={this.onSend}\n              type=\"button\"\n              className=\"btn btn-default btn-sm\"\n            >\n              <Icon name=\"arrow-right\" />\n              Send To Server\n            </button>\n            <button\n              disabled={disabled}\n              title={tips}\n              type=\"button\"\n              className=\"btn btn-default dropdown-toggle\"\n              data-toggle=\"dropdown\"\n              aria-haspopup=\"true\"\n              aria-expanded=\"false\"\n            >\n              <span className=\"caret\"></span>\n            </button>\n            <ul\n              style={leftStyle}\n              className={'dropdown-menu' + (closed ? ' hide' : '')}\n            >\n              <li style={displayStyle}>\n                <a data-target=\"server\" onClick={this.onSend}>\n                  Send Binary Data\n                </a>\n              </li>\n              <li>\n                <a onClick={this.uploadTextToServer}>\n                  {isHttps ? 'Upload ' : 'Upload Text Data'}\n                </a>\n              </li>\n              <li style={displayStyle}>\n                <a onClick={this.uploadBinToServer}>Upload Binary Data</a>\n              </li>\n            </ul>\n          </div>\n          <button\n            type=\"button\"\n            title=\"Format\"\n            onClick={this.format}\n            className=\"btn btn-default w-format-json-btn\"\n          >\n            Format\n          </button>\n        </div>\n        <textarea\n          ref=\"textarea\"\n          style={{ fontFamily: isHexText ? 'monospace' : undefined }}\n          maxLength={MAX_LENGTH}\n          value={text}\n          onKeyDown={this.onForamt}\n          onChange={this.onTextareaChange}\n          placeholder={'Enter ' + (isHexText ? 'hex ' : '') + 'text'}\n          className=\"fill\"\n        />\n        <form\n          ref=\"uploadDataForm\"\n          method=\"post\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"uploadData\"\n            onChange={this.onFormChange}\n            type=\"file\"\n            name=\"uploadData\"\n          />\n        </form>\n      </div>\n    );\n  }\n});\n\nmodule.exports = FrameComposer;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/frame-data.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar BtnGroup = require('./btn-group');\nvar JSONViewer = require('./json-viewer');\nvar Textarea = require('./textarea');\nvar FrameComposer = require('./frame-composer');\nvar util = require('./util');\nvar Properties = require('./properties');\n\n\nfunction findActive(btn) {\n  return btn.active;\n}\n\nvar FrameClient = React.createClass({\n  getInitialState: function () {\n    return {\n      btns: [\n        { name: 'Overview' },\n        { name: 'TextView' },\n        { name: 'JSONView' },\n        { name: 'HexView' },\n        { name: 'Composer', icon: 'send' }\n      ]\n    };\n  },\n  showTab: function (i) {\n    var btns = this.state.btns;\n    btns.forEach(function (btn) {\n      btn.active = false;\n    });\n    this.selectBtn(btns[i]);\n    this.setState({});\n  },\n  componentDidMount: function () {\n    var self = this;\n    var btns = self.state.btns;\n    var framesCtx = self.props.framesCtx;\n    framesCtx.on('composeFrame', function (e, frame) {\n      if (frame) {\n        self.showTab(4);\n        setTimeout(function() {\n          util.shakeElem($(ReactDOM.findDOMNode(self.refs.tabs)).find('button[data-name=\"Composer\"]'));\n        },100);\n      }\n    });\n    framesCtx.on('toggleFramesInspectors', function () {\n      var btn = self.state.btn;\n      btns.forEach(function (b) {\n        b.active = false;\n      });\n      if (!btn || btn === btns[0]) {\n        self.onClickBtn(btns[1]);\n      } else if (btn === btns[1]) {\n        self.onClickBtn(btns[2]);\n      } else if (btn === btns[2]) {\n        self.onClickBtn(btns[3]);\n      } else {\n        self.onClickBtn(btns[0]);\n      }\n    });\n  },\n  onDragEnter: function (e) {\n    if (e.dataTransfer.types.indexOf('framedataid') != -1) {\n      this.showTab(4);\n      e.preventDefault();\n    }\n  },\n  onDrop: function (e) {\n    var id = e.dataTransfer.getData('frameDataId');\n    id && this.props.framesCtx.trigger('composeFrameId', id);\n  },\n  onClickBtn: function (btn) {\n    this.selectBtn(btn);\n    this.setState({});\n  },\n  selectBtn: function (btn) {\n    btn.active = true;\n    this.state.btn = btn;\n  },\n  render: function () {\n    var state = this.state;\n    var btns = this.state.btns;\n    var btn = state.btn;\n    if (btns.indexOf(btn) === -1) {\n      btn = util.findArray(btns, findActive) || btns[0];\n      this.selectBtn(btn);\n    }\n    var frame = this.props.frame;\n    var text, json, bin, base64, overview;\n    if (frame) {\n      if (!frame.closed) {\n        var len = frame.length;\n        overview = {\n          Date: util.toLocaleString(new Date(parseInt(frame.frameId, 10))),\n          Path: frame.isClient ? 'Client -> Server' : 'Server -> Client',\n          Opcode: frame.opcode,\n          Type: frame.opcode == 1 ? 'Text' : 'Binary',\n          Compressed: frame.compressed ? 'Yes' : 'No',\n          Mask: frame.mask ? 'Yes' : 'No',\n          Length: len >= 0 ? util.formatSize(len, frame.unzipLen) : ''\n        };\n      } else {\n        overview = {\n          Date: util.toLocaleString(new Date(parseInt(frame.frameId, 10)))\n        };\n      }\n      text = util.getBody(frame, true);\n      bin = util.getHex(frame);\n      json = util.getJson(frame, true);\n      base64 = frame.base64;\n    }\n    base64 = base64 || '';\n    return (\n      <div\n        className={\n          'fill v-box w-frames-data' +\n          (this.props.hide ? ' hide' : '')\n        }\n        onDragEnter={this.onDragEnter}\n        onDrop={this.onDrop}\n      >\n        <BtnGroup ref=\"tabs\" onClick={this.onClickBtn} btns={state.btns} />\n        <Properties modal={overview} hide={btn.name !== 'Overview'} />\n        <Textarea\n          className=\"fill\"\n          base64={base64}\n          value={text}\n          hide={btn.name !== 'TextView'}\n        />\n        <JSONViewer data={json} hide={btn.name !== 'JSONView'} />\n        <Textarea\n          className=\"fill n-monospace\"\n          isHexView=\"1\"\n          base64={base64}\n          value={bin}\n          hide={btn.name !== 'HexView'}\n        />\n        <FrameComposer framesCtx={this.props.framesCtx} data={this.props.data} hide={btn.name !== 'Composer'} />\n      </div>\n    );\n  }\n});\n\nmodule.exports = FrameClient;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/frame-list.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar util = require('./util');\nvar FilterInput = require('./filter-input');\nvar DropDown = require('./dropdown');\nvar dataCenter = require('./data-center');\nvar RecordBtn = require('./record-btn');\nvar ContextMenu = require('./context-menu');\nvar Icon = require('./icon');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar SEND_PERATORS = [\n  {\n    value: 0,\n    icon: 'arrow-right',\n    text: 'Send'\n  },\n  {\n    value: 1,\n    icon: 'arrow-right',\n    text: 'Pause'\n  },\n  {\n    value: 2,\n    icon: 'arrow-right',\n    text: 'Ignore'\n  }\n];\nvar RECEIVE_PERATORS = [\n  {\n    value: 0,\n    icon: 'arrow-left',\n    text: 'Receive'\n  },\n  {\n    value: 1,\n    icon: 'arrow-left',\n    text: 'Pause'\n  },\n  {\n    value: 2,\n    icon: 'arrow-left',\n    text: 'Ignore'\n  }\n];\n\nvar contextMenuList = [\n  { name: 'Copy' },\n  { name: 'Replay' },\n  { name: 'Edit' },\n  { name: 'Abort' },\n  { name: 'Clear' }\n];\n\nvar COMPRESSED_ERR = 'invalid compressed data';\nvar isClosed = function(reqData) {\n  if (reqData.closed) {\n    return reqData.lastErr !== COMPRESSED_ERR;\n  }\n  return reqData.err && reqData.err !== COMPRESSED_ERR;\n};\n\nvar FrameList = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  onFilterChange: function (keyword) {\n    var self = this;\n    self.props.modal.search(keyword);\n    clearTimeout(self.filterTimer);\n    self.filterTimer = setTimeout(function () {\n      self.filterTimer = null;\n      self.setState({ keyword: keyword.trim() });\n    }, 500);\n  },\n  componentDidMount: function () {\n    var self = this;\n    var framesCtx = this.props.framesCtx;\n    framesCtx.on('autoRefreshFrames', self.autoRefresh);\n    framesCtx.on('composeFrameId', function (e, id) {\n      var modal = id && self.props.modal;\n      var list = modal && modal.list;\n      if (list) {\n        for (var i = 0, len = list.length; i < len; i++) {\n          var frame = list[i];\n          if (frame && frame.frameId === id) {\n            return framesCtx.trigger('composeFrame', frame);\n          }\n        }\n      }\n    });\n    framesCtx.on('enableRecordFrame', function () {\n      self.refs.recordBtn.enable();\n    });\n  },\n  onDoubleClick: function () {\n    this.props.framesCtx.trigger('toggleFramesInspectors');\n  },\n  componentWillUpdate: function () {\n    this.atBottom = this.shouldScrollToBottom();\n  },\n  componentDidUpdate: function () {\n    if (this.atBottom) {\n      this.autoRefresh();\n    }\n    var reqData = this.props.reqData;\n    if (reqData) {\n      if (reqData.pauseRecordFrames) {\n        this.refs.recordBtn.enable('pause');\n      } else if (reqData.stopRecordFrames) {\n        this.refs.recordBtn.enable('stop');\n      } else {\n        this.refs.recordBtn.enable();\n      }\n    }\n  },\n  replay: function () {\n    var reqData = this.props.reqData;\n    if (!reqData || reqData.closed || reqData.err) {\n      this.autoRefresh();\n      return;\n    }\n    this.props.framesCtx.trigger('replayFrame', this.props.modal.getActive());\n  },\n  stopRefresh: function () {\n    this.container.scrollTop = this.container.scrollTop - 10;\n  },\n  autoRefresh: function () {\n    this.container.scrollTop = 100000000;\n  },\n  clear: function () {\n    this.props.modal.clear();\n    this.setState({});\n    if (this.props.onUpdate) {\n      this.props.onUpdate();\n    }\n  },\n  compose: function () {\n    this.props.framesCtx.trigger('composeFrame', this.props.modal.getActive());\n  },\n  checkActive: function () {\n    var reqData = this.props.reqData;\n    if (!reqData || reqData.closed || reqData.err) {\n      this.autoRefresh();\n      return;\n    }\n    return reqData;\n  },\n  abort: function () {\n    var self = this;\n    var reqData = self.props.reqData;\n    if (!reqData || isClosed(reqData)) {\n      return;\n    }\n    dataCenter.socket.abort(\n      {\n        reqId: reqData.id\n      },\n      function (data, xhr) {\n        if (!data) {\n          util.showSystemError(xhr);\n        } else {\n          delete reqData.lastErr;\n          reqData.closed = true;\n          self.autoRefresh();\n          self.setState({});\n        }\n      }\n    );\n  },\n  changeStatus: function (reqData, option, isSend) {\n    var self = this;\n    var params = {\n      reqId: reqData.id\n    };\n    if (isSend) {\n      params.sendStatus = option.value;\n    } else {\n      params.receiveStatus = option.value;\n    }\n    dataCenter.socket.changeStatus(params, function (data, xhr) {\n      if (!data) {\n        util.showSystemError(xhr);\n      } else {\n        if (isSend) {\n          reqData.sendStatus = option.value;\n        } else {\n          reqData.receiveStatus = option.value;\n        }\n        self.setState({});\n      }\n    });\n  },\n  onSendStatusChange: function (option) {\n    var reqData = this.checkActive();\n    if (!reqData) {\n      return;\n    }\n    this.changeStatus(reqData, option, true);\n  },\n  onReceiveStatusChange: function (option) {\n    var reqData = this.checkActive();\n    if (!reqData) {\n      return;\n    }\n    this.changeStatus(reqData, option);\n  },\n  onClear: function (e) {\n    if (e.ctrlKey || e.metaKey) {\n      e.stopPropagation();\n      if (e.keyCode === 88) {\n        if (!util.hasShortcut('clearNetworkFrames')) {\n          return;\n        }\n        this.clear();\n      } else if (e.keyCode === 13) {\n        e.stopPropagation();\n        if (!util.hasShortcut('replaySelectedFrame')) {\n          return;\n        }\n        this.replay();\n      }\n    }\n  },\n  shouldScrollToBottom: function () {\n    var con = this.container;\n    var ctn = this.content;\n    var modal = this.props.modal;\n    var atBottom = con.scrollTop + con.offsetHeight + 5 > ctn.offsetHeight;\n    if (atBottom) {\n      modal.update();\n    }\n    return atBottom;\n  },\n  setContainer: function (container) {\n    this.container = findDOMNode(container);\n  },\n  setContent: function (content) {\n    this.content = findDOMNode(content);\n  },\n  handleAction: function (type) {\n    if (type === 'top') {\n      this.container.scrollTop = 0;\n      return;\n    }\n    if (type === 'bottom') {\n      return this.autoRefresh();\n    }\n    var refresh = type === 'refresh';\n    var reqData = this.props.reqData;\n    if (reqData) {\n      if (type === 'pause') {\n        reqData.stopRecordFrames = true;\n        reqData.pauseRecordFrames = true;\n        return;\n      }\n      reqData.pauseRecordFrames = false;\n      reqData.stopRecordFrames = !refresh;\n    }\n    if (refresh) {\n      return this.autoRefresh();\n    }\n  },\n  onDragStart: function (e) {\n    var dataId = $(e.target).closest('li').attr('data-id');\n    if (!dataId) {\n      return;\n    }\n    e.dataTransfer.setData('frameDataId', dataId);\n  },\n  onContextMenu: function (e) {\n    e.preventDefault();\n    var frameId = $(e.target).closest('li').attr('data-id');\n    var modal = this.props.modal;\n    var item = modal.getItem(frameId);\n    var hasClosed = !!isClosed(this.props.reqData);\n    this.currentFocusItem = item;\n    contextMenuList[0].disabled = !item;\n    contextMenuList[0].copyText = (item && item.data) || '';\n    contextMenuList[1].disabled = (!item || hasClosed);\n    contextMenuList[2].disabled = !item;\n    contextMenuList[3].disabled = hasClosed;\n    contextMenuList[4].disabled = !modal.list.length;\n    var data = util.getMenuPosition(e, 130, 130);\n    data.list = contextMenuList;\n    this.refs.contextMenu.show(data);\n  },\n  onClickContextMenu: function (action) {\n    var item = this.currentFocusItem;\n    var framesCtx = this.props.framesCtx;\n    this.currentFocusItem = null;\n    switch (action) {\n    case 'Replay':\n      item &&  framesCtx.trigger('replayFrame', item);\n      break;\n    case 'Edit':\n      item && framesCtx.trigger('composeFrame', item);\n      break;\n    case 'Abort':\n      this.abort();\n      break;\n    case 'Clear':\n      this.clear();\n      break;\n    }\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var state = this.state;\n    var reqData = props.reqData || {};\n    var onClickFrame = props.onClickFrame;\n    var modal = self.props.modal;\n    var keyword = state.keyword;\n    var activeItem = modal.getActive();\n    var list = modal.getList();\n    util.socketIsClosed(reqData);\n    return (\n      <div className=\"fill v-box w-frames-list\">\n        <div className=\"w-frames-action\" onMouseDown={util.preventBlur}>\n          <RecordBtn\n            ref=\"recordBtn\"\n            onClick={this.handleAction}\n            disabledRecord={reqData.closed}\n          />\n          <a onClick={self.clear} className=\"w-remove-menu\" draggable=\"false\">\n            <Icon name=\"remove\" />Clear\n          </a>\n          <a\n            onClick={self.replay}\n            className={\n              'w-remove-menu' +\n              (!activeItem || reqData.closed ? ' w-disabled' : '')\n            }\n            draggable=\"false\"\n          >\n            <Icon name=\"repeat\" />Replay\n          </a>\n          <a\n            onClick={self.compose}\n            className={'w-remove-menu' + (activeItem ? '' : ' w-disabled')}\n            draggable=\"false\"\n          >\n            <Icon name=\"send\" />Edit\n          </a>\n          <a\n            onClick={self.abort}\n            className={'w-remove-menu' + (isClosed(reqData) ? ' w-disabled' : '')}\n            draggable=\"false\"\n          >\n            <Icon name=\"ban-circle\" />Abort\n          </a>\n          <DropDown\n            disabled={reqData.closed}\n            value={reqData.sendStatus || 0}\n            onChange={self.onSendStatusChange}\n            options={SEND_PERATORS}\n          />\n          <DropDown\n            disabled={reqData.closed}\n            value={reqData.receiveStatus || 0}\n            onChange={self.onReceiveStatusChange}\n            options={RECEIVE_PERATORS}\n          />\n        </div>\n        <div\n          tabIndex=\"0\"\n          onKeyDown={this.onClear}\n          style={{ background: keyword ? 'var(--b-filtered)' : undefined }}\n          onScroll={self.shouldScrollToBottom}\n          ref={self.setContainer}\n          className=\"fill w-frames-list\"\n          onContextMenu={this.onContextMenu}\n        >\n          <ul ref={self.setContent} onDragStart={self.onDragStart}>\n            {list.map(function (item) {\n              var statusClass = '';\n              if (item.closed || item.err || item.isError) {\n                reqData.closed = reqData.closed || item.closed;\n                reqData.err = item.err || item.data;\n                if (item.closed) {\n                  statusClass = ' w-conn-closed';\n                } else {\n                  statusClass = ' w-conn-error';\n                }\n                item.title =\n                  item.title ||\n                  'Date: ' +\n                    util.toLocaleString(new Date(parseInt(item.frameId, 10)));\n              }\n              if (item.data == null) {\n                item.data = util.getBody(item, true);\n                if (item.data.length > 500) {\n                  item.data = item.data.substring(0, 500) + '...';\n                }\n              }\n              if (!item.title && !item.closed) {\n                item.title =\n                  'Date: ' +\n                  util.toLocaleString(new Date(parseInt(item.frameId, 10))) +\n                  '\\nPath: ' +\n                  (item.isClient ? 'Client -> Server' : 'Server -> Client');\n                if (item.opcode) {\n                  item.title += '\\nOpcode: ' + item.opcode;\n                  item.title +=\n                    '\\nType: ' + (item.opcode == 1 ? 'Text' : 'Binary');\n                }\n                if (item.compressed) {\n                  item.title += '\\nCompressed: ' + (item.compressed ? 'Yes' : 'No');\n                }\n                if (item.mask) {\n                  item.title += '\\nMask: ' + (item.mask ? 'Yes' : 'No');\n                }\n                var length = item.length;\n                if (length >= 0) {\n                  item.title += '\\nLength: ' + util.formatSize(length, item.unzipLen);\n                }\n              }\n              var icon = 'arrow-left';\n              if (item.closed) {\n                icon = 'minus-sign';\n              } else if (item.isClient) {\n                icon = 'arrow-right';\n              }\n              var notDec = item.notDecompressed && item.compressed;\n              return (\n                <li\n                  draggable\n                  key={item.frameId}\n                  data-id={item.frameId}\n                  title={item.title}\n                  style={{ display: item.hide ? 'none' : undefined }}\n                  onClick={function () {\n                    onClickFrame && onClickFrame(item);\n                  }}\n                  onDoubleClick={self.onDoubleClick}\n                  className={\n                    (item.isClient ? 'w-frames-send' : '') +\n                    (item.ignore ? ' w-frames-ignore' : '') +\n                    (item.active ? '  w-frames-selected' : '') +\n                    (item.opcode == 2 ? ' w-frames-bin' : '') +\n                    (notDec ? ' w-not-decompressed' : '') +\n                    statusClass\n                  }\n                >\n                  <Icon name={icon} />\n                  {notDec ? <em>[Not decompressed]</em> : null}\n                  {item.data}\n                </li>\n              );\n            })}\n          </ul>\n        </div>\n        <FilterInput onChange={self.onFilterChange} />\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = FrameList;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/frame-modal.js",
    "content": "var util = require('./util');\nvar MAX_FRAMES_LENGTH = require('./data-center').MAX_FRAMES_LENGTH;\n\nfunction FramesModal() {\n  this.list = [];\n}\n\nvar proto = FramesModal.prototype;\n\nproto.getItem = function(frameId) {\n  if (frameId) {\n    for (var i = 0, len = this.list.length; i < len; i++) {\n      var item = this.list[i];\n      if (item.frameId === frameId) {\n        return item;\n      }\n    }\n  }\n};\n\nvar KW_RE = /^(c|s):(\\S*)$/i;\nproto.search = function (keyword) {\n  keyword = typeof keyword !== 'string' ? '' : keyword.trim();\n  if (!keyword) {\n    this._keyword = '';\n    return;\n  }\n  var k = (this._keyword = {});\n  keyword.split(/\\s+/).forEach(function (key) {\n    var type = '';\n    var not = key[0] === '!';\n    if (not) {\n      key = key.substring(1).trim();\n    }\n    if (KW_RE.test(key)) {\n      type = RegExp.$1.toLowerCase();\n      key = RegExp.$2;\n      if (key[0] === '!') {\n        not = true;\n        key = key.substring(1);\n      }\n    }\n    var list = k[type] || (k[type] = []);\n    if (key && list.length < 3) {\n      list.push({\n        not: not,\n        keyword: key.toLowerCase(),\n        regexp: util.toRegExp(key)\n      });\n    }\n  });\n};\n\nproto.filter = function () {\n  var keyword = this._keyword;\n  var list = this.list;\n  if (!keyword) {\n    list.forEach(function (item) {\n      item.hide = false;\n    });\n    return;\n  }\n  var lowerBody;\n  var isHide = function (item, type) {\n    var list = keyword[type || ''];\n    if (!list) {\n      return false;\n    }\n    if (type === 's') {\n      if (item.isClient) {\n        return true;\n      }\n    } else if (type === 'c') {\n      if (!item.isClient) {\n        return true;\n      }\n    }\n    if (list.length === 0) {\n      return false;\n    }\n    for (var i = 0, len = list.length; i < len; i++) {\n      var key = list[i];\n      if (lowerBody == null) {\n        lowerBody = util.getBody(item, true).toLowerCase();\n      }\n      var flag = key.regexp ? key.regexp.test(lowerBody) : lowerBody.indexOf(key.keyword) !== -1;\n      if (key.not ? !flag : flag) {\n        return false;\n      }\n    }\n    return true;\n  };\n  list.forEach(function (item) {\n    lowerBody = null;\n    item.hide = isHide(item) || isHide(item, 's') || isHide(item, 'c');\n  });\n};\n\nproto.setActive = function (item, active) {\n  this.list.forEach(function (item) {\n    item.active = false;\n  });\n  item.active = active !== false;\n};\n\nfunction getActive(list) {\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (item.active) {\n      return item;\n    }\n  }\n}\n\nfunction updateList(list, len) {\n  var activeItem = getActive(list);\n  list.splice(0, len);\n  if (activeItem && list.indexOf(activeItem) === -1) {\n    list[0] = activeItem;\n  }\n}\n\nproto.getActive = function () {\n  return getActive(this.list);\n};\n\nproto.getList = function () {\n  this.filter();\n  return this.list;\n};\n\nproto.update = function () {\n  var list = this.list;\n  var len = list.length;\n  var overflow = len - MAX_FRAMES_LENGTH;\n  if (overflow > 0) {\n    if (this._keyword) {\n      var i = 0;\n      while (i < len && overflow > 0) {\n        var item = list[i];\n        if (item.hide && !item.active) {\n          --len;\n          --overflow;\n          list.splice(i, 1);\n        } else {\n          ++i;\n        }\n      }\n    }\n    if (overflow > 0) {\n      updateList(list, overflow);\n    }\n  }\n};\n\nproto.clear = function () {\n  this.list.splice(0, this.list.length);\n  return this;\n};\n\nproto.reset = function (list) {\n  if (!list || this.list === list) {\n    return list;\n  }\n  this.list = list;\n  this.filter();\n  return list;\n};\n\nmodule.exports = FramesModal;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/frames.js",
    "content": "require('../css/frames.css');\nvar React = require('react');\nvar util = require('./util');\nvar Divider = require('./divider');\nvar FrameList = require('./frame-list');\nvar FrameData = require('./frame-data');\nvar FrameModal = require('./frame-modal');\nvar dataCenter = require('./data-center');\nvar LazyInit = require('./lazy-init');\nvar $ = require('jquery');\n\nvar Frames = React.createClass({\n  getInitialState: function () {\n    return {\n      modal: new FrameModal(),\n      framesCtx: $({})\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    dataCenter.on('framesUpdate', function () {\n      self.setState({});\n    });\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onClickFrame: function (frame) {\n    var modal = this.state.modal;\n    modal.setActive(frame);\n    this.setState({});\n  },\n  onUpdate: function () {\n    this.setState({});\n  },\n  onDOMReady: function () {\n    this.refs.frameList.autoRefresh();\n  },\n  render: function () {\n    var props = this.props;\n    var modal = this.state.modal;\n    var framesCtx = this.state.framesCtx;\n    var frames = modal.reset(props.frames);\n    var reqData = props.data || '';\n    var curFrame = modal.getActive();\n    var hide = !frames || props.hide;\n    if (curFrame && curFrame.hide) {\n      curFrame = null;\n    }\n    return (\n      <div\n        className={\n          'fill v-box w-frames' + (props.hide ? ' hide' : '')\n        }\n      >\n        <LazyInit inited={!hide}>\n          <Divider\n            hide={hide}\n            vertical=\"true\"\n            rightWidth=\"250\"\n            onDOMReady={this.onDOMReady}\n          >\n            <FrameList\n              ref=\"frameList\"\n              framesCtx={framesCtx}\n              reqData={reqData}\n              modal={modal}\n              onUpdate={this.onUpdate}\n              onClickFrame={this.onClickFrame}\n            />\n            <FrameData framesCtx={framesCtx} data={reqData} frame={curFrame} />\n          </Divider>\n        </LazyInit>\n        <div className={'w-no-frames' + (frames ? ' hide' : '')}>No Frames</div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Frames;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/github-icon.js",
    "content": "var React = require('react');\n\nvar GITHUB_STYLE = {\n  'margin-right': '5px',\n  'margin-left': '-2px',\n  'vertical-align': 'text-bottom'\n};\n\nvar GitHub = React.createClass({\n  render: function() {\n    return (\n      <svg viewBox=\"0 0 30 30\" width=\"17px\" height=\"17px\" style={GITHUB_STYLE} fill=\"currentColor\">\n          <path d=\"M15,3C8.373,3,3,8.373,3,15c0,5.623,3.872,10.328,9.092,11.63C12.036,26.468,12,26.28,12,26.047v-2.051 c-0.487,0-1.303,0-1.508,0c-0.821,0-1.551-0.353-1.905-1.009c-0.393-0.729-0.461-1.844-1.435-2.526 c-0.289-0.227-0.069-0.486,0.264-0.451c0.615,0.174,1.125,0.596,1.605,1.222c0.478,0.627,0.703,0.769,1.596,0.769 c0.433,0,1.081-0.025,1.691-0.121c0.328-0.833,0.895-1.6,1.588-1.962c-3.996-0.411-5.903-2.399-5.903-5.098 c0-1.162,0.495-2.286,1.336-3.233C9.053,10.647,8.706,8.73,9.435,8c1.798,0,2.885,1.166,3.146,1.481C13.477,9.174,14.461,9,15.495,9 c1.036,0,2.024,0.174,2.922,0.483C18.675,9.17,19.763,8,21.565,8c0.732,0.731,0.381,2.656,0.102,3.594 c0.836,0.945,1.328,2.066,1.328,3.226c0,2.697-1.904,4.684-5.894,5.097C18.199,20.49,19,22.1,19,23.313v2.734 c0,0.104-0.023,0.179-0.035,0.268C23.641,24.676,27,20.236,27,15C27,8.373,21.627,3,15,3z\"/>\n      </svg>\n    );\n  }\n});\n\nmodule.exports = GitHub;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/help-icon.js",
    "content": "var React = require('react');\nvar util = require('./util');\n\nvar HelpIcon = React.createClass({\n  shouldComponentUpdate: function (nextProps) {\n    var props = this.props;\n    return props.className !== nextProps.className || props.docsUrl !== nextProps.docsUrl\n    || props.onClick !== nextProps.onClick || props['data-name'] !== nextProps['data-name'];\n  },\n  onClick: function(e) {\n    var props = this.props;\n    if (props.docsUrl) {\n      return window.open(util.getDocUrl(props.docsUrl));\n    }\n    props.onClick(e);\n  },\n  render: function() {\n    var className = this.props.className;\n    var name = this.props['data-name'];\n    return <a data-name={name} className={'glyphicon glyphicon-question-sign w-help-icon' + (className ? ' ' + className : '')} onClick={this.onClick} />;\n  }\n});\n\nmodule.exports = HelpIcon;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/history-data.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar Divider = require('./divider');\nvar ContextMenu = require('./context-menu');\nvar events = require('./events');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar contextMenuList = [\n  { name: 'Copy URL' },\n  { name: 'Copy As cURL' },\n  { name: 'Replay' },\n  { name: 'Replay Times' },\n  { name: 'Export' },\n  { name: 'Edit', icon: 'send' },\n  {\n    name: 'Service',\n    list: [\n      { name: 'Create API Test', action: 'createApiTest' },\n      { name: 'Copy As Script', action: 'copyAsScript' },\n      { name: 'Share Via URL', action: 'Export' }\n    ]\n  }\n];\nvar RULES_KEY = /^\\s*x-whistle-rule-value:/mi;\nvar curItem;\nvar curDisabled;\nvar curRaw;\n\nfunction noData(item) {\n  return !!(item && item.body == null && (item.dataHash || item.base64Hash));\n}\n\nfunction getRaw(item, disabled) {\n  if (curItem === item && curDisabled === disabled) {\n    return curRaw;\n  }\n  var raw = [\n    item.method + ' ' + item.url + ' HTTP/' + (item.useH2 ? '2.0' : '1.1')\n  ];\n  if (disabled) {\n    raw.push('// Loading data, please wait...');\n  } else {\n    item.headers && raw.push(item.headers);\n    raw.push('\\n', item.body || (item.base64 && util.base64Decode(item.base64))) || '';\n  }\n  curRaw = raw.join('\\n');\n  curItem = item;\n  curDisabled = disabled;\n  return curRaw;\n}\n\nvar HistoryData = React.createClass({\n  getInitialState: function() {\n    return {};\n  },\n  shouldComponentUpdate: function (nextProps) {\n    return this.props.show || nextProps.show !== this.props.show;\n  },\n  getItem: function () {\n    var list = this.props.data;\n    for (var i = 0, len = list.length; i < len; i++) {\n      var item = list[i];\n      if (item.selected) {\n        return item;\n      }\n    }\n  },\n  scrollToTop: function() {\n    ReactDOM.findDOMNode(this.refs.list).scrollTop = 0;\n  },\n  copyAsCURL: function(_, item) {\n    item = item || this.getItem();\n    if (!item) {\n      return;\n    }\n    var headers = item.headers;\n    if (typeof headers === 'string') {\n      headers = RULES_KEY.test(headers) ? headers.split(/\\r\\n|\\r|\\n/).filter(function(line) {\n        return !RULES_KEY.test(line);\n      }).join('\\r\\n') : headers;\n      headers = util.parseHeaders(headers);\n    }\n    var text = util.asCURL({\n      url: item.url || '',\n      req: {\n        method: item.method,\n        headers: headers,\n        base64: item.base64 || '',\n        body: item.body || ''\n      }\n    });\n    util.copyText(text, true);\n  },\n  onEdit: function () {\n    this.props.onEdit(this.getItem());\n  },\n  onReplay: function () {\n    this.props.onReplay(this.getItem());\n    this.scrollToTop();\n  },\n  onReplayTimes: function() {\n    this.props.onReplay(this.getItem(), true);\n    this.scrollToTop();\n  },\n  handleClick: function(item) {\n    this.props.data.forEach(function(item) {\n      item.selected = false;\n    });\n    item.selected = true;\n    this.setState({});\n  },\n  exportItem: function(item) {\n    var headers = item.headers;\n    var rules = [];\n    if (typeof headers === 'string' && RULES_KEY.test(headers)) {\n      headers = headers.split(/\\r\\n|\\r|\\n/).filter(function(line) {\n        if (RULES_KEY.test(line)) {\n          line = line.substring(line.indexOf(':') + 1).trim();\n          line = util.decodeURIComponentSafe(line).trim();\n          line && rules.push(line);\n          return false;\n        }\n        return true;\n      }).join('\\r\\n');\n    }\n    events.trigger('showExportDialog', ['composer', {\n      type: 'setComposerData',\n      useH2: item.useH2,\n      rules: rules.join('\\n'),\n      url: item.url,\n      method: item.method,\n      headers: headers,\n      body: item.body,\n      base64: item.base64,\n      isHexText: item.isHexText\n    }]);\n  },\n  export: function() {\n    var groupList = this.props.data && this.props.data._groupList;\n    if (!groupList) {\n      return;\n    }\n    var len = groupList.length;\n    for (var i = 0; i < len; i++) {\n      var item = groupList[i];\n      if (item && item.selected) {\n        this.exportItem(item);\n      }\n    }\n  },\n  onContextMenu: function(e) {\n    var isTitle;\n    var elem = $(e.target).closest('div.w-history-item');\n    if (!elem.length) {\n      elem = $(e.target).closest('div.w-history-title');\n      isTitle = true;\n    }\n    e.preventDefault();\n    if (!elem.length) {\n      return;\n    }\n    var groupList = this.props.data && this.props.data._groupList;\n    var item = groupList && groupList[elem.attr('data-index')];\n    if (!item) {\n      return;\n    }\n    var noService = !dataCenter.whistleId;\n    var disabled = noData(item);\n    this._focusItem = item;\n    contextMenuList[0].name = isTitle ? 'Copy' : 'Copy URL';\n    contextMenuList[0].copyText = elem.attr('title');\n\n    for (var i = 1, len = contextMenuList.length; i < len; i++) {\n      var ctxMenu = contextMenuList[i];\n      ctxMenu.hide = isTitle;\n      ctxMenu.disabled = disabled;\n      if (i === 6) {\n        ctxMenu.hide = noService || isTitle;\n      }\n    }\n    var data = util.getMenuPosition(e, 130, 185 - (isTitle ? 150 : 0) + (noService ? 0 : 30));\n    data.className = 'w-keep-history-data';\n    data.list = contextMenuList;\n    this.refs.contextMenu.show(data);\n  },\n  onClickContextMenu: function (action) {\n    switch (action) {\n    case 'Copy As cURL':\n      return this.copyAsCURL(null, this._focusItem);\n    case 'Export':\n      return this.exportItem(this._focusItem);\n    case 'Replay':\n      this.props.onReplay(this._focusItem);\n      return this.scrollToTop();\n    case 'Replay Times':\n      this.props.onReplay(this._focusItem, true);\n      return this.scrollToTop();\n    case 'Edit':\n      return this.props.onEdit(this._focusItem);\n    case 'createApiTest':\n      return util.showService('createApiTest');\n    case 'copyAsScript':\n      return util.showService('copyAsScript');\n    }\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var show = props.show;\n    var data = props.data || [];\n    var groupList = data._groupList || [];\n    var selectedItem;\n    var disabled;\n    return (\n      <div ref=\"historyDialog\" className={'w-layer box w-com-history-data' + (show ? ' w-show' : '')}>\n        {data.length ? null : <CloseBtn onClick={props.onClose} />}\n        {data.length ?\n        <Divider leftWidth=\"170\">\n          <div ref=\"list\" className=\"w-com-history-list\" onContextMenu={self.onContextMenu}>\n            {groupList.map(function(item, i) {\n              if (item.title) {\n                return <div className=\"w-history-title\" title={item.title} data-index={i}>{item.title}</div>;\n              }\n              if (item.selected) {\n                selectedItem = item;\n                disabled = noData(item);\n              }\n              return (\n                <div className={'w-history-item' + (item.selected ? ' w-selected' : '')}\n                  title={item.url} onClick={function () { self.handleClick(item); }} data-index={i}>\n                  <div>{item.url}</div>\n                  <p>\n                    <i className={'w-req-method-tag w-req-method-tag-' + item.method}>{item.method}</i>\n                    <i className=\"w-req-protocol-tag\">{item.protocol}</i>\n                    {item.body ? <i className=\"w-req-type-tag\">Body</i> : null}\n                  </p>\n                </div>\n              );\n            })}\n          </div>\n          <div className=\"fill v-box w-com-history-ctn\">\n            {selectedItem ? <div className=\"w-com-history-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  disabled={disabled}\n                  onClick={this.copyAsCURL}\n                >\n                  As cURL\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  disabled={disabled}\n                  onClick={this.onReplay}\n                >\n                  Replay\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  disabled={disabled}\n                  onClick={this.onReplayTimes}\n                >\n                  Replay Times\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-info\"\n                  disabled={disabled}\n                  onClick={this.export}\n                >\n                  Export\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-primary\"\n                  disabled={disabled}\n                  onClick={this.onEdit}\n                >\n                  <Icon name=\"send\" />\n                  Edit\n                </button>\n                <CloseBtn onClick={props.onClose} />\n              </div> : null}\n              {selectedItem ? <pre className={disabled ? 'w-show-loading fill' : 'fill'}>\n                {getRaw(selectedItem, disabled)}\n                <div className=\"w-load-tips\">Loading...</div>\n              </pre> : null}\n          </div>\n        </Divider> : <div className=\"w-empty-data\">Empty</div>}\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = HistoryData;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/https-settings.js",
    "content": "var React = require('react');\nvar CertsInfoDialog = require('./certs-info-dialog');\nvar QRCodeImg = require('./qrcode');\nvar dataCenter = require('./data-center');\nvar storage = require('./storage');\nvar util = require('./util');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar events = require('./events');\nvar Icon = require('./icon');\nvar HelpIcon = require('./help-icon');\nvar CloseBtn = require('./close-btn');\n\nfunction getCAType(type) {\n  if (type === 'crt' || type === 'pem') {\n    return type;\n  }\n  return 'cer';\n}\n\nvar HttpsSettings = React.createClass({\n  getInitialState: function() {\n    return {\n      caType: getCAType(storage.get('caType'))\n    };\n  },\n  selectCAType: function(e) {\n    var caType = getCAType(e.target.value);\n    this.setState({ caType: caType });\n    storage.set('caType', caType);\n  },\n  selectCAUrl: function(e) {\n    this.setState({ caFullUrl: e.target.value });\n  },\n  show: function() {\n    $(ReactDOM.findDOMNode(this.refs.rootCADialog)).modal('show');\n  },\n  showCustomCertsInfo: function () {\n    var self = this;\n    if (self.loadingCerts) {\n      return;\n    }\n    self.loadingCerts = true;\n    dataCenter.certs.all(function (data, xhr) {\n      self.loadingCerts = false;\n      if (!data) {\n        util.showSystemError(xhr);\n        return;\n      }\n      self.refs.certsInfoDialog.show(data.certs, data.dir);\n    });\n  },\n  componentDidMount: function() {\n    events.on('showCustomCerts', this.showCustomCertsInfo);\n  },\n  render: function() {\n    var props = this.props;\n    var caHash = props.caHash;\n    var port = props.port;\n    var caUrlList = props.caUrlList;\n    var multiEnv = props.multiEnv;\n    var interceptHttpsConnects = props.interceptHttpsConnects;\n    var enableHttp2 = props.enableHttp2;\n    var onEnableHttps = props.onEnableHttps;\n    var onEnableHttp2 = props.onEnableHttp2;\n    var state = this.state;\n    var caType = state.caType;\n    var caFullUrl = state.caFullUrl;\n    var caUrl = 'cgi-bin/rootca';\n    var caShortUrl = 'http://rootca.pro/';\n\n    if (caType !== 'cer') {\n      caUrl += '?type=' + caType;\n      caShortUrl += caType;\n    }\n\n    return (\n      <div ref=\"rootCADialog\" className=\"modal fade w-https-dialog\">\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <CloseBtn />\n                <div style={{marginBottom: 10}}>\n                  <HelpIcon docsUrl=\"gui/https.html\" />\n                  <a\n                    className=\"w-download-rootca\"\n                    title={caShortUrl}\n                    href={caUrl}\n                    target=\"downloadTargetFrame\"\n                  >\n                    Download RootCA\n                  </a>\n                  <select className=\"w-root-ca-type\" value={caType} onChange={this.selectCAType}>\n                    <option value=\"crt\">rootCA.crt</option>\n                    <option value=\"cer\">rootCA.cer</option>\n                    <option value=\"pem\">rootCA.pem</option>\n                  </select>\n                </div>\n                <div className=\"w-root-ca-url-wrap\">\n                  <select className=\"w-root-ca-url\" value={caFullUrl} onChange={this.selectCAUrl}>\n                    <option value=\"\">{caShortUrl} (PROXY REQUIRED)</option>\n                    {caUrlList.map(function (url) {\n                      url = url[0] === 'h' ? url : 'http://' + url + ':' + port;\n                      url += '/cgi-bin/rootca' + (caType === 'cer' ? '' : '?type=' + caType);\n                      return <option value={url}>{url}</option>;\n                    })}\n                  </select>\n                  <Icon title=\"Copy Root CA URL\" name=\"copy\" className=\"w-copy-text-with-tips\" data-clipboard-text={caFullUrl || caShortUrl} />\n                </div>\n                <a\n                  href={caUrl}\n                  target=\"downloadTargetFrame\"\n                >\n                  <QRCodeImg url={caFullUrl || caShortUrl + caHash} />\n                </a>\n                <div className=\"w-https-settings\">\n                  <p>\n                    <label\n                      title={\n                        multiEnv\n                          ? 'Use \\'pattern enable://capture\\' in rules to enable HTTPS'\n                          : undefined\n                      }\n                    >\n                      <input\n                        disabled={multiEnv}\n                        checked={interceptHttpsConnects}\n                        onChange={onEnableHttps}\n                        type=\"checkbox\"\n                         className=\"w-va-mdl\"\n                      />\n                      <span className=\"w-va-mdl w-mrl-5\">\n                        Enable HTTPS (Capture Tunnel Traffic)\n                      </span>\n                    </label>\n                  </p>\n                  <p>\n                    <label>\n                      <input\n                        checked={dataCenter.supportH2 && enableHttp2}\n                        onChange={onEnableHttp2}\n                        type=\"checkbox\"\n                        className=\"w-va-mdl\"\n                      />\n                    <span className=\"w-va-mdl w-mrl-5\">\n                      Enable HTTP/2\n                    </span>\n                    </label>\n                  </p>\n                  <a\n                    draggable=\"false\"\n                    style={{\n                      color: dataCenter.hasInvalidCerts ? 'var(--c-error)' : undefined\n                    }}\n                    onClick={this.showCustomCertsInfo}\n                  >\n                    Custom Certs Settings\n                  </a>\n                  <CertsInfoDialog ref=\"certsInfoDialog\" />\n                </div>\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  data-dismiss=\"modal\"\n                >\n                  Close\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n    );\n  }\n});\n\nmodule.exports = HttpsSettings;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/icon.js",
    "content": "var React = require('react');\n\nvar Icon = React.createClass({\n  shouldComponentUpdate: function (nextProps) {\n    var props = this.props;\n    return props.name !== nextProps.name || props.className !== nextProps.className\n      || props['data-name'] !== nextProps['data-name']\n      || props['data-clipboard-text'] !== nextProps['data-clipboard-text']\n      || props.title !== nextProps.title\n      || props.onClick !== nextProps.onClick;\n  },\n  render: function() {\n    var props = this.props;\n    return <span\n      className={'glyphicon glyphicon-' + props.name + (props.className ? ' ' + props.className : '')}\n      data-name={props['data-name']}\n      data-clipboard-text={props['data-clipboard-text']}\n      title={props.title}\n      onClick={props.onClick}\n    />;\n  }\n});\n\nmodule.exports = Icon;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/iframe-dialog.js",
    "content": "require('../css/iframe-dialog.css');\nvar React = require('react');\nvar Dialog = require('./dialog');\nvar getBridge = require('./bridge');\nvar dataCenter = require('./data-center');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nfunction onWhistlePluginOptionModalReady(init, win) {\n  if (typeof init === 'function') {\n    init(getBridge(win));\n  }\n}\n\nvar IframeDialog = React.createClass({\n  getInitialState: function() {\n    return {};\n  },\n  show: function(plugin) {\n    var self = this;\n    self._hideDialog = false;\n    self.setState(plugin, function() {\n      self.refs.iframeDialog.show();\n    });\n  },\n  hide: function() {\n    this.refs.iframeDialog.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  render: function() {\n    var state = this.state;\n    var disabled = state.disabled;\n    var name = state.name;\n    var url = state.url;\n    var favicon = state.favicon ? <img src={state.favicon} /> : null;\n    var className = 'w-plugins-tab inline-align-items' + (disabled ? ' w-plugin-tab-disabled' : '');\n\n    window.onWhistlePluginOptionModalReady = onWhistlePluginOptionModalReady;\n\n    return (\n      <Dialog ref=\"iframeDialog\" wstyle=\"w-iframe-dialog\" width={state.width || 'max(calc(100% - 240px), 720px)'}>\n        <div className=\"modal-header\">\n          <h4>\n            <span className={className}>\n              {disabled ? <Icon data-name={name} name=\"ban-circle\" /> : favicon}\n              {name || 'Untitled'}\n            </span>\n          </h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body w-fix-drag\" style={{height: state.height || 'max(calc(100vh - 120px), 600px)'}}>\n        <iframe src={url} onLoad={dataCenter.handleIframeLoad} />\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = IframeDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/iframe.js",
    "content": "require('../css/iframe.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar dataCenter = require('./data-center');\n\nvar IFrame = React.createClass({\n  getWindow: function() {\n    return ReactDOM.findDOMNode(this.refs.iframe).contentWindow;\n  },\n  onload: function(e) {\n    var onLoad = this.props.onLoad;\n    if (typeof onLoad === 'function') {\n      onLoad(e);\n    }\n    dataCenter.handleIframeLoad(e);\n  },\n  render: function() {\n    var props = this.props;\n    var className = props.className;\n    return (\n      <div className={'fill box w-fix-drag w-iframe' + (className ? ' ' + className : '')} style={props.style}>\n        <iframe ref=\"iframe\" onLoad={this.onload} src={props.src} className=\"fill\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = IFrame;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/iframes.js",
    "content": "var util = require('./util');\nvar modal = require('./modal');\nvar getBridge = require('./bridge');\n\nvar MAX_COUNT = 6;\nvar MAX_CACHE_TIME = 1000 * 60 * 3;\nvar TIMEOUT = 1000 * 60;\nvar cache = {};\nvar latestItem;\n\nfunction destroy(item) {\n  if (item === latestItem) {\n    return;\n  }\n  try {\n    document.body.removeChild(item.iframe);\n    item.destroyed = true;\n    item.dialogs.forEach(function (dialog) {\n      dialog.hide(true);\n    });\n    item.dialogs = [];\n    delete cache[item.page];\n  } catch (e) {}\n}\n\nvar detectTimeout = function () {\n  detectTimeout = util.noop;\n  setInterval(function () {\n    var now = Date.now();\n    Object.keys(cache).forEach(function (key) {\n      var item = cache[key];\n      if (now - item.mtime > MAX_CACHE_TIME) {\n        destroy(item);\n      }\n    });\n  }, TIMEOUT);\n};\n\nfunction getItem(win) {\n  try {\n    var page = win.location.pathname.substring(1) + win.location.search;\n    var index = page.lastIndexOf('?');\n    if (index !== -1) {\n      ++index;\n      var len = parseInt(page.substring(index), 10);\n      if (len > 0) {\n        page = page.substring(index - len, index);\n      }\n    }\n    return cache[page];\n  } catch (e) {}\n}\n\nfunction onPluginContextMenuReady(win) {\n  var item = getItem(win);\n  if (!item || item.emit) {\n    return;\n  }\n  function emit(data) {\n    try {\n      if (typeof win.onWhistleContextMenuClick === 'function') {\n        win.onWhistleContextMenuClick(data);\n      }\n    } catch (e) {}\n  }\n  try {\n    var bridge = getBridge(win);\n    bridge.createModal = function (options) {\n      var dialog = modal.create(options);\n      var hide = dialog.hide;\n      dialog.hide = function (_destroy) {\n        if (_destroy) {\n          var index = item.dialogs.indexOf(dialog);\n          index !== -1 && item.dialogs.splice(index, 1);\n        }\n        hide(_destroy);\n      };\n      item.dialogs.push(dialog);\n      return dialog;\n    };\n    win.initWhistleBridge(bridge);\n  } catch (e) {}\n  item.list.forEach(emit);\n  item.emit = emit;\n  item.list = null;\n}\n\nfunction removeOldest() {\n  var keys = Object.keys(cache);\n  var len = keys.length;\n  if (len < MAX_COUNT) {\n    return;\n  }\n  var oldest = cache[keys[0]];\n  for (var i = 1; i < len; i++) {\n    var item = cache[keys[i]];\n    if (oldest.mtime > item.mtime) {\n      oldest = item;\n    }\n  }\n  destroy(oldest);\n}\n\nexports.fork = function (page, options) {\n  page += '???_WHISTLE_PLUGIN_EXT_CONTEXT_MENU_' + options.port + '???';\n  var item = (latestItem = cache[page]);\n  if (item) {\n    item.mtime = Date.now();\n    if (item.emit) {\n      item.emit(options);\n    } else {\n      item.list.push(options);\n      if (item.list.length > 10) {\n        item.list = item.list.slice(-10);\n      }\n    }\n    return;\n  }\n  removeOldest();\n  detectTimeout();\n  window.onPluginContextMenuReady = onPluginContextMenuReady;\n  var iframe = document.createElement('iframe');\n  iframe.style.display = 'none';\n  cache[page] =\n    latestItem =\n    item =\n    {\n      page: page,\n      list: [options],\n      mtime: Date.now(),\n      iframe: iframe,\n      dialogs: []\n    };\n  document.body.appendChild(iframe);\n  iframe.src = page + page.length;\n  setTimeout(function () {\n    !item.emit && destroy(item);\n  }, TIMEOUT);\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/image-view.js",
    "content": "require('../css/image-view.css');\nvar React = require('react');\nvar util = require('./util');\n\nvar isClient = util.getQuery().mode === 'client';\nvar hasWebView = function() {\n  return isClient && window.WebView && window.WebView.name === 'WebViewElement';\n};\n\nvar ImageView = React.createClass({\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  preview: function () {\n    util.openPreview(this.props.data);\n  },\n  getPreviewUrl: function() {\n    var data = !this.props.imgSrc && hasWebView() && this.props.data;\n    if (!data || !data.res.base64) {\n      return;\n    }\n    if (this._curData !== data) {\n      this._curData = data;\n      this._previewUrl = util.getPreviewUrl(data);\n    }\n    return this._previewUrl;\n  },\n  getPreviewElem: function(previewUrl) {\n    if (previewUrl) {\n      return <webview src={previewUrl} className=\"fill\" />;\n    }\n    var props = this.props;\n    if (props.imgSrc) {\n      return <img src={props.imgSrc} />;\n    }\n    if (props.data) {\n      return <a className=\"w-image-link\" onClick={this.preview}>Preview page in new window</a>;\n    }\n  },\n  render: function () {\n    var props = this.props;\n    var previewUrl = this.getPreviewUrl(props.data);\n    var isImg = props.imgSrc && !previewUrl;\n\n    return (\n      <div\n        className={'fill w-image-view' + (previewUrl ? ' w-image-webview' : '') +\n          (props.hide ? ' hide' : '') + (isImg ? ' w-image-bg' : '')}\n      >\n        {previewUrl || props.imgSrc ? <div className=\"w-textarea-bar\">\n          <a onClick={this.preview}>Open in new window</a>\n        </div> : undefined}\n        {this.getPreviewElem(previewUrl)}\n      </div>\n    );\n  }\n});\n\nmodule.exports = ImageView;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/import-dialog.js",
    "content": "require('../css/import-dialog.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar events = require('./events');\nvar message = require('./message');\nvar EditorDialog = require('./editor-dialog');\nvar parseCurl = require('./parse-curl');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\n\nfunction getAccept(name) {\n  if (name === 'network') {\n    return '.txt,.json,.saz,.har';\n  }\n  if (name === 'console' || name === 'server') {\n    return '.log';\n  }\n  if (name === 'composer') {\n    return '.txt,.json,.har';\n  }\n  return '.txt,.json';\n}\n\nfunction parseJson(text) {\n  if (text[0] !== '{' && text[0] !== '[') {\n    return;\n  }\n  try {\n    return JSON.parse(text);\n  } catch(e) {}\n}\n\nvar ImportDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  show: function (name) {\n    var self = this;\n    self.refs.importDialog.show();\n    setTimeout(function () {\n      var input = findDOMNode(self.refs.input);\n      input.focus();\n      input.select();\n    }, 500);\n    name = name || 'network';\n    self.setState({\n      name: name,\n      accept: getAccept(name),\n      importCURL: name === 'composer',\n      title: util.getDialogTitle(name)\n    });\n  },\n  hide: function () {\n    this.refs.importDialog.hide();\n  },\n  showService: function () {\n    util.showService(this.state.name + '/history');\n  },\n  importRemoteUrl: function (e) {\n    if (e && e.type !== 'click' && e.keyCode !== 13) {\n      return;\n    }\n    var self = this;\n    var input = findDOMNode(self.refs.input);\n    var url = input.value.trim();\n    if (!url) {\n      message.error('The url or file path is required');\n      input.value = '';\n      input.focus();\n      return;\n    }\n    dataCenter.getRemoteData(url, function (_, data) {\n      if (data) {\n        self.hide();\n        events.trigger(self.state.name + 'ImportData', [data]);\n      }\n    });\n  },\n  handleFile: function(file) {\n    if (file) {\n      this.hide();\n      events.trigger(this.state.name + 'ImportFile', [file]);\n    }\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('importFile', function (_, file) {\n      self.handleFile(file);\n    });\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.importDialog.isVisible();\n  },\n  uploadFile: function() {\n    var fileInput = findDOMNode(this.refs.importFile);\n    this.handleFile(fileInput.files[0]);\n    findDOMNode(this.refs.importFile).value = '';\n  },\n  selectFile: function() {\n    findDOMNode(this.refs.importFile).click();\n  },\n  importCURL: function(text) {\n    text = text.trim();\n    if (!text) {\n      message.error('The text is required');\n      return false;\n    }\n    try {\n      var result = parseJson(text) || parseCurl(text);\n      if (!result || !result.url) {\n        message.error('Not cURL text');\n        return false;\n      }\n      result.isHexText = false;\n      result.headers = util.objectToString(result.headers);\n      events.trigger('setComposerData', [result]);\n    } catch (e) {\n      message.error(e.message);\n      return false;\n    }\n\n  },\n  showImportCURL: function() {\n    this.refs.editorDialog.show();\n  },\n  render: function () {\n    var state = this.state;\n\n    return (\n      <Dialog ref=\"importDialog\" wstyle=\"w-ie-dialog w-import-dialog\">\n        <div className=\"modal-header\">\n          <h4>{state.title}</h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body\">\n          <input\n            ref=\"input\"\n            maxLength=\"2048\"\n            onKeyDown={this.importRemoteUrl}\n            placeholder=\"Enter url or file path\"\n          />\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          {\n            state.importCURL ? <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n            onClick={this.showImportCURL}\n          >\n            <Icon name=\"file\" />\n            Import cURL\n          </button> : null\n          }\n          {dataCenter.whistleId ? <button\n            type=\"button\"\n            className=\"btn btn-warning\"\n            data-dismiss=\"modal\"\n            onClick={this.showService}\n          >\n            <Icon name=\"cloud\" />\n            Import From Service\n          </button> : null}\n          <button\n            type=\"button\"\n            className=\"btn btn-info\"\n            onClick={this.selectFile}\n          >\n            <Icon name=\"folder-open\" />\n            Upload\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-primary w-fmt-btn\"\n            onMouseDown={util.preventBlur}\n            onClick={this.importRemoteUrl}\n          >\n            Import\n          </button>\n        </div>\n        <form\n          ref=\"importFileForm\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"importFile\"\n            onChange={this.uploadFile}\n            name=\"fileInput\"\n            type=\"file\"\n            accept={state.accept}\n          />\n        </form>\n        <EditorDialog ref=\"editorDialog\" title=\"Import cURL\" hideFormat=\"1\" placeholder=\"Enter cURL text\"\n          textEditor onConfirm={this.importCURL} />\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = ImportDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/index.js",
    "content": "require('./base-css.js');\nrequire('../css/index.css');\nrequire('../css/theme.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar Clipboard = require('clipboard');\nvar EditorDialog = require('./editor-dialog');\nvar List = require('./list');\nvar ListModal = require('./list-modal');\nvar Network = require('./network');\nvar About = require('./about');\nvar Online = require('./online');\nvar MenuItem = require('./menu-item');\nvar RecordBtn = require('./record-btn');\nvar EditorSettings = require('./editor-settings');\nvar NetworkSettings = require('./network-settings');\nvar Plugins = require('./plugins');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar protocols = require('./protocols');\nvar events = require('./events');\nvar storage = require('./storage');\nvar Dialog = require('./dialog');\nvar ListDialog = require('./list-dialog');\nvar FilterBtn = require('./filter-btn');\nvar message = require('./message');\nvar UpdateAllBtn = require('./update-all-btn');\nvar ContextMenu = require('./context-menu');\nvar RulesDialog = require('./rules-dialog');\nvar SyncDialog = require('./sync-dialog');\nvar LargeDialog = require('./large-dialog');\nvar JSONDialog = require('./json-dialog');\nvar MockDialog = require('./mock-dialog');\nvar IframeDialog = require('./iframe-dialog');\nvar win = require('./win');\nvar ServiceBtn = require('./service-btn');\nvar SaveToServiceBtn = require('./share-via-url-btn');\nvar ImportDialog = require('./import-dialog');\nvar ExportDialog = require('./export-dialog');\nvar HttpsSettings = require('./https-settings');\nvar ServiceDialog = require('./service-dialog');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar TEMP_LINK_RE = /^(?:[\\w-]+:\\/\\/)?temp(?:\\/([\\da-z]{64}|blank))?(?:\\.[\\w-]+)?$/;\nvar FILE_PATH_RE = /^(?:[\\w-]+:\\/\\/)?((?:[a-z]:[\\\\/]|\\/).+)$/i;\nvar DEFAULT = 'Default';\nvar MAX_PLUGINS_TABS = 7;\nvar MAX_FILE_SIZE = 1024 * 1024 * 128;\nvar MAX_OBJECT_SIZE = 1024 * 1024 * 36;\nvar MAX_LOG_SIZE = 1024 * 1024 * 2;\nvar MAX_REPLAY_COUNT = 100;\nvar LINK_SELECTOR = '.cm-js-type, .cm-js-http-url, .cm-string, .cm-js-at';\nvar LINK_RE = /^\"(https?:)?(\\/\\/[^/]\\S+)\"$/i;\nvar AT_LINK_RE = /^@(https?:)?(\\/\\/[^/]\\S+)$/i;\nvar OPTIONS_WITH_SELECTED = [\n  'removeSelected',\n  'exportWhistleFile'\n];\nvar HIDE_STYLE = { display: 'none' };\nvar VER_RE = /^(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)(?:-[\\w-]+)?$/;\nvar search = window.location.search;\nvar query = util.getQuery();\nvar isClient = query.mode === 'client';\nvar clientVersion = isClient && util.isElectron && VER_RE.test(query.v) ? query.v : '';\nvar hideMenus = !!(query.hideMenus || query.hideMenu);\nvar hideLeftMenu;\nvar showTreeView;\nvar dataUrl;\nvar TABS = ['Network', 'Rules', 'Values', 'Plugins'];\nvar TEXT_SUFFIX_RE = /[\\w-]\\.(?:txt|csv|tsv|json|xml|yaml|yml|ini|conf|log|html|htm|css|js|py|java|c|cpp|h|sh|php|sql|md|markdown|rtf|tex|bib|vcf)$/i;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nfunction getHideStyle(hide) {\n  return hide ? HIDE_STYLE: null;\n}\n\nfunction getString(url) {\n  return typeof url === 'string' ? url.trim() : '';\n}\n\nfunction isTextFile(url) {\n  if (!TEXT_SUFFIX_RE.test(url)) {\n    return false;\n  }\n  var PATH_RE = dataCenter.isWin ? /^(?:[\\w-]+:\\/\\/)?[a-z]:[\\\\/]/i : /^(?:[\\w-]+:\\/\\/)?\\//i;\n  return PATH_RE.test(url);\n}\n\nwindow.setWhistleDataUrl = function(url) {\n  url = getString(url);\n  if (url) {\n    if (dataCenter.handleDataUrl) {\n      dataCenter.handleDataUrl(url);\n    } else {\n      dataUrl = url;\n    }\n    return true;\n  }\n  return false;\n};\n\nwindow.showWhistleMessage = function(options) {\n  message[options.level || options.type || 'info'](options.text || options.msg || options.message);\n};\n\nwindow.showWhistleWebUI = function(name) {\n  if (TABS.indexOf(name) !== -1) {\n    events.trigger('show' + name);\n  }\n};\n\nif (/[&#?]showTreeView=(0|false|1|true)(?:&|$|#)/.test(search)) {\n  showTreeView = RegExp.$1 === '1' || RegExp.$1 === 'true';\n}\n\nif (/[&#?]hideLeft(?:Bar|Menu)=(0|false|1|true)(?:&|$|#)/.test(search)) {\n  hideLeftMenu = RegExp.$1 === '1' || RegExp.$1 === 'true';\n} else if (/[&#?]showLeft(?:Bar|Menu)=(0|false|1|true)(?:&|$|#)/.test(search)) {\n  hideLeftMenu = RegExp.$1 === '0' || RegExp.$1 === 'false';\n}\n\nvar TOP_BAR_MENUS = [\n  {\n    name: 'Scroll To Top',\n    action: 'top'\n  },\n  {\n    name: 'Scroll To Selected',\n    action: 'selected'\n  },\n  {\n    name: 'Scroll To Bottom',\n    action: 'bottom'\n  }\n];\n\nvar LEFT_BAR_MENUS = [\n  {\n    name: 'Clear',\n    icon: 'remove'\n  },\n  {\n    name: 'Save',\n    icon: 'save-file'\n  },\n  {\n    name: 'Tree View',\n    multiple: true\n  },\n  {\n    name: 'Rules',\n    multiple: true\n  },\n  {\n    name: 'Plugins',\n    multiple: true\n  }\n];\n\nvar RULES_ACTIONS = [\n  {\n    name: 'Import',\n    icon: 'import',\n    id: 'importRules',\n    title: 'Ctrl[Command] + I'\n  },\n  {\n    name: 'Export',\n    icon: 'export',\n    id: 'exportRules'\n  }\n];\nvar VALUES_ACTIONS = [\n  {\n    name: 'Import',\n    icon: 'import',\n    id: 'importValues',\n    title: 'Ctrl[Command] + I'\n  },\n  {\n    name: 'Export',\n    icon: 'export',\n    id: 'exportValues'\n  }\n];\n\nvar ABORT_OPTIONS = [\n  {\n    name: 'Abort',\n    icon: 'ban-circle',\n    id: 'abort'\n  }\n];\n\nfunction getJsonForm(data, name) {\n  var form = new FormData();\n  var file = new File([JSON.stringify(data)], 'data.json', { type: 'application/json' });\n  form.append(name || 'rules', file);\n  return form;\n}\n\nfunction readFileJson(file, cb) {\n  if (util.isString(file)) {\n    if (file.length > MAX_OBJECT_SIZE) {\n      win.alert('File exceeds maximum size limit');\n      return cb();\n    }\n    return cb(parseJSON(file));\n  }\n  if (!file || !/\\.(txt|json|har)$/i.test(file.name)) {\n    win.alert('Supported file formats: .txt, .json, .har');\n    return cb();\n  }\n\n  if (file.size > MAX_OBJECT_SIZE) {\n    win.alert('File exceeds maximum size limit');\n    return cb();\n  }\n  util.readFileAsText(file, function(text) {\n    cb(parseJSON(text));\n  });\n}\n\nfunction handleImportData(file, cb, type) {\n  readFileJson(file, function(data) {\n    if (!data || util.handleImportData(data, type)) {\n      return cb();\n    }\n    cb(data);\n  });\n}\n\nfunction getPageName(options) {\n  var hash = location.hash.substring(1);\n  if (hash) {\n    hash = hash.replace(/[?#].*$/, '');\n  } else {\n    hash = location.href.replace(/[?#].*$/, '').replace(/.*\\//, '');\n  }\n  if (options.networkMode) {\n    return 'network';\n  }\n  if (options.rulesMode && options.pluginsMode) {\n    return 'plugins';\n  }\n  if (options.rulesOnlyMode) {\n    return hash === 'values' ? 'values' : 'rules';\n  }\n  if (options.rulesMode) {\n    return hash === 'network' ? 'rules' : hash;\n  }\n\n  if (options.pluginsMode) {\n    return hash !== 'plugins' ? 'network' : hash;\n  }\n  if (isClient && !hash) {\n    hash = storage.get('pageName');\n    return TABS.indexOf(hash) !== -1 ? hash : 'network';\n  }\n  return hash;\n}\n\nfunction parseJSON(text) {\n  try {\n    var obj = JSON.parse(text);\n    return obj && typeof obj === 'object' ? obj : null;\n  } catch (e) {\n    message.error(e.message);\n  }\n}\n\nfunction compareSelectedNames(src, target) {\n  var srcLen = src.length;\n  var i;\n  for (i = 0; i < srcLen; i++) {\n    if ($.inArray(src[i], target) === -1) {\n      return false;\n    }\n  }\n  var targetLen = target.length;\n  if (srcLen !== targetLen) {\n    for (i = 0; i < targetLen; i++) {\n      if ($.inArray(target[i], src) === -1) {\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nfunction getKey(url) {\n  if (url.indexOf('{') == 0) {\n    var index = url.lastIndexOf('}');\n    return index > 1 && url.substring(1, index);\n  }\n\n  return false;\n}\n\nfunction getValue(url) {\n  if (url.indexOf('(') == 0) {\n    var index = url.lastIndexOf(')');\n    return (index != -1 && url.substring(1, index)) || '';\n  }\n\n  return false;\n}\n\nfunction appendList(list, _list) {\n  if (!_list.length) {\n    return;\n  }\n  for (var i = 0, len = list.length; i < len; i++) {\n    if (util.isGroup(list[i])) {\n      _list.unshift(i, 0);\n      list.splice.apply(list, _list);\n      return;\n    }\n  }\n  list.push.apply(list, _list);\n}\n\nfunction updateData(list, data, modal) {\n  var changedList = modal.getChangedList();\n  if (!changedList.length) {\n    return;\n  }\n  var hasChanged;\n  var _list = [];\n  var activeItem;\n  changedList.forEach(function(item) {\n    var name = item.name;\n    var curItem = data[name];\n    if (!curItem) {\n      data[name] = item;\n      _list.push(name);\n      if (item.active) {\n        activeItem = item;\n      }\n    } else if (curItem.value != item.value) {\n      hasChanged = true;\n      data[name] = item;\n    }\n  });\n  appendList(list, _list);\n  if (activeItem) {\n    list.forEach(function(name) {\n      data[name].active = false;\n    });\n    activeItem.active = true;\n  }\n  return hasChanged;\n}\n\nfunction getCAType(type) {\n  if (type === 'crt' || type === 'pem') {\n    return type;\n  }\n  return 'cer';\n}\n\nvar Index = React.createClass({\n  getInitialState: function () {\n    var self = this;\n    var modal = self.props.modal;\n    var rules = modal.rules;\n    var values = modal.values;\n    var server = modal.server;\n    var caUrlList = [];\n    var caHash = util.getCAHash(server, caUrlList);\n    var state = {\n      filename: '',\n      replayCount: 1,\n      tabs: [],\n      caType: getCAType(storage.get('caType')),\n      caHash: caHash,\n      caUrlList: caUrlList,\n      allowMultipleChoice: modal.rules.allowMultipleChoice,\n      backRulesFirst: modal.rules.backRulesFirst,\n      networkMode: !!server.networkMode,\n      rulesMode: !!server.rulesMode,\n      pluginsMode: !!server.pluginsMode,\n      rulesOnlyMode: !!server.rulesOnlyMode,\n      ndr: server.ndr,\n      ndp: server.ndp,\n      drb: server.drb,\n      drm: server.drm,\n      port: server.port,\n      whistleId: server.whistleId,\n      version: modal.version\n    };\n    if (hideLeftMenu !== false) {\n      hideLeftMenu = hideLeftMenu || server.hideLeftMenu;\n    }\n    var pageName = getPageName(state);\n    if (!pageName || pageName.indexOf('rules') != -1) {\n      state.hasRules = true;\n      state.name = 'rules';\n    } else if (pageName.indexOf('values') != -1) {\n      state.hasValues = true;\n      state.name = 'values';\n    } else if (pageName.indexOf('plugins') != -1) {\n      state.hasPlugins = true;\n      state.name = 'plugins';\n    } else {\n      state.hasNetwork = true;\n      state.name = 'network';\n    }\n\n    var rulesList = [];\n    var rulesOptions = [];\n    var rulesData = {};\n    var valuesList = [];\n    var valuesOptions = [];\n    var valuesData = {};\n\n    var rulesTheme = storage.get('rulesTheme');\n    var valuesTheme = storage.get('valuesTheme');\n    var rulesFontSize = storage.get('rulesFontSize');\n    var valuesFontSize = storage.get('valuesFontSize');\n    var showRulesLineNumbers = storage.get('showRulesLineNumbers');\n    var showValuesLineNumbers = storage.get('showValuesLineNumbers');\n    var autoRulesLineWrapping = storage.get('autoRulesLineWrapping');\n    var autoValuesLineWrapping = storage.get('autoValuesLineWrapping');\n    var selectedName;\n\n    if (rules) {\n      selectedName = storage.get('activeRules') || rules.current;\n      var selected = !rules.defaultRulesIsDisabled;\n      if (!rulesTheme) {\n        rulesTheme = rules.theme;\n      }\n      if (!rulesFontSize) {\n        rulesFontSize = rules.fontSize;\n      }\n      if (!showRulesLineNumbers) {\n        showRulesLineNumbers = rules.showLineNumbers ? 'true' : 'false';\n      }\n      rulesList.push(DEFAULT);\n      rulesData.Default = {\n        name: DEFAULT,\n        fixed: true,\n        value: rules.defaultRules,\n        selected: selected,\n        isDefault: true,\n        active: selectedName === DEFAULT\n      };\n\n      rulesOptions.push(rulesData.Default);\n\n      rules.list.forEach(function (item) {\n        rulesList.push(item.name);\n        item = rulesData[item.name] = {\n          name: item.name,\n          value: item.data,\n          selected: item.selected,\n          active: selectedName === item.name\n        };\n        rulesOptions.push(item);\n      });\n    }\n\n    if (values) {\n      selectedName = storage.get('activeValues') || values.current;\n      if (!valuesTheme) {\n        valuesTheme = values.theme;\n      }\n      if (!valuesFontSize) {\n        valuesFontSize = values.fontSize;\n      }\n      if (!showValuesLineNumbers) {\n        showValuesLineNumbers = values.showLineNumbers ? 'true' : 'false';\n      }\n      values.list.forEach(function (item) {\n        valuesList.push(item.name);\n        valuesData[item.name] = {\n          name: item.name,\n          value: item.data,\n          active: selectedName === item.name\n        };\n        valuesOptions.push({\n          name: item.name,\n          icon: 'edit'\n        });\n      });\n    }\n    var rulesModal = new ListModal(rulesList, rulesData);\n    var valuesModal = new ListModal(valuesList, valuesData);\n    var networkModal = dataCenter.networkModal;\n    dataCenter.setValuesModal(valuesModal);\n    dataCenter.rulesModal = rulesModal;\n    dataCenter.exportSessions = function(sessions, opts, name) {\n      var type;\n      if (typeof opts === 'string') {\n        type = opts;\n      } else if (opts) {\n        type = opts.type;\n        name = opts.name || name;\n      }\n      if (type === 'saz' || type === 'fiddler') {\n        type = 'Fiddler';\n      }\n      if (typeof name !== 'string') {\n        name = '';\n      }\n      self.exportSessions(type, name, sessions);\n    };\n    state.rulesTheme = rulesTheme;\n    state.valuesTheme = valuesTheme;\n    state.rulesFontSize = rulesFontSize;\n    state.valuesFontSize = valuesFontSize;\n    state.showRulesLineNumbers = showRulesLineNumbers === 'true';\n    state.showValuesLineNumbers = showValuesLineNumbers === 'true';\n    state.autoRulesLineWrapping = !!autoRulesLineWrapping;\n    state.foldGutter = storage.get('foldGutter') !== '';\n    state.autoValuesLineWrapping = !!autoValuesLineWrapping;\n    state.plugins = modal.plugins;\n    state.disabledPlugins = modal.disabledPlugins;\n    state.disabledAllRules = modal.disabledAllRules;\n    state.disabledAllPlugins = modal.disabledAllPlugins;\n    state.interceptHttpsConnects = modal.interceptHttpsConnects;\n    state.enableHttp2 = modal.enableHttp2;\n    state.rules = rulesModal;\n    state.network = networkModal;\n    state.rulesOptions = rulesOptions;\n    state.pluginsOptions = self.createPluginsOptions(modal.plugins);\n    dataCenter.valuesModal = state.values = valuesModal;\n    state.valuesOptions = valuesOptions;\n    dataCenter.syncData = self.syncData;\n    dataCenter.syncRules = self.syncRules;\n    dataCenter.syncValues = self.syncValues;\n\n    self.initPluginTabs(state, modal.plugins);\n    if (rulesModal.exists(dataCenter.activeRulesName)) {\n      self.setRulesActive(dataCenter.activeRulesName, rulesModal);\n    }\n    if (valuesModal.exists(dataCenter.activeValuesName)) {\n      self.setValuesActive(dataCenter.activeValuesName, valuesModal);\n    }\n\n    state.networkOptions = [\n      {\n        name: 'Remove All',\n        icon: 'remove',\n        id: 'removeAll',\n        disabled: true,\n        title: 'Ctrl[Command] + X'\n      },\n      {\n        name: 'Remove Selected',\n        id: 'removeSelected',\n        disabled: true,\n        title: 'Ctrl[Command] + D'\n      },\n      {\n        name: 'Remove Unselected',\n        id: 'removeUnselected',\n        disabled: true,\n        title: 'Ctrl[Command] + Shift + D'\n      },\n      {\n        name: 'Import',\n        icon: 'import',\n        id: 'importSessions',\n        title: 'Ctrl[Command] + I'\n      },\n      {\n        name: 'Export',\n        icon: 'export',\n        id: 'exportWhistleFile',\n        disabled: true,\n        title: 'Ctrl[Command] + S'\n      },\n      {\n        name: 'Show Tree View',\n        icon: 'tree-conifer',\n        id: 'toggleView'\n      }\n    ];\n    state.helpOptions = [\n      {\n        name: 'Website',\n        href: util.getDocUrl(),\n        icon: 'link'\n      },\n      {\n        name: 'GitHub',\n        href: 'https://github.com/avwo/whistle',\n        icon: 'github'\n      },\n      {\n        name: 'Update',\n        href: util.UPDATE_URL,\n        icon: 'refresh'\n      },\n      {\n        name: 'Issue',\n        href: 'https://github.com/avwo/whistle/issues/new',\n        icon: 'record'\n      }\n    ];\n    protocols.setPlugins(state);\n    state.exportFileType = storage.get('exportFileType');\n    var showLeftMenu = storage.get('showLeftMenu');\n    state.showLeftMenu = showLeftMenu == null ? true : showLeftMenu;\n    util.triggerPageChange(state.name);\n    if (showTreeView || showTreeView === false) {\n      networkModal.setTreeView(showTreeView, true);\n    }\n    events.on('importSessionsFromUrl', function (_, url) {\n      self.importSessionsFromUrl(url);\n    });\n    return self.updateMenuView(state);\n  },\n  initPluginTabs: function(state, plugins) {\n    plugins = plugins || {};\n    var tabs = state.tabs;\n    var activeTabs;\n    var activeName;\n    try {\n      activeTabs = JSON.parse(storage.get('activePluginTabList'));\n      activeName = storage.get('activePluginTabName');\n    } catch (e) {}\n    if (!Array.isArray(activeTabs)) {\n      return;\n    }\n    var map = {};\n    Object.keys(plugins)\n      .forEach(function (name) {\n        var plugin = plugins[name];\n        name = name.slice(0, -1);\n        if (activeTabs.indexOf(name) === -1) {\n          return;\n        }\n        if (activeName === name) {\n          state.active = name;\n        }\n        map[name] = {\n          name: name,\n          url: plugin.pluginHomepage || 'plugin.' + name + '/'\n        };\n      });\n    activeTabs.forEach(function(name) {\n      name = name && map[name];\n      name && tabs.push(name);\n    });\n  },\n  getListByName: function (name, type) {\n    var list = this.state[name].list;\n    var data = this.state[name].data;\n    return {\n      type: type,\n      url: location.href,\n      list: list.map(function (name) {\n        var item = data[name];\n        return {\n          name: name,\n          value: (item && item.value) || ''\n        };\n      })\n    };\n  },\n  triggerRulesChange: function (type) {\n    util.triggerListChange('rules', this.getListByName('rules', type));\n  },\n  triggerValuesChange: function (type) {\n    util.triggerListChange('values', this.getListByName('values', type));\n  },\n  syncData: function(plugin, cb) {\n    var state = this.state;\n    this.refs.syncDialog.show(plugin, state.rules, state.values, cb);\n  },\n  syncRules: function(plugin) {\n    var self = this;\n    self.syncData(plugin, function() {\n      self.refs.syncDialog.syncRules(plugin);\n    });\n  },\n  syncValues: function(plugin) {\n    var self = this;\n    self.syncData(plugin, function() {\n      self.refs.syncDialog.syncValues(plugin);\n    });\n  },\n  showKVDialog: function(data, isValues) {\n    if (data) {\n      this.refs.syncDialog.showKVDialog(data, this.state.rules, this.state.values, isValues);\n    }\n  },\n  createPluginsOptions: function (plugins) {\n    plugins = plugins || {};\n    var pluginsOptions = [\n      {\n        name: 'Home'\n      }\n    ];\n\n    Object.keys(plugins)\n      .sort(function (a, b) {\n        var p1 = plugins[a];\n        var p2 = plugins[b];\n        return (\n          util.compare(p1.priority, p2.priority) ||\n          util.compare(p2.mtime, p1.mtime) ||\n          (a > b ? 1 : -1)\n        );\n      })\n      .forEach(function (name) {\n        var plugin = plugins[name];\n        pluginsOptions.push({\n          name: name.slice(0, -1),\n          icon: 'checkbox',\n          mtime: plugin.mtime,\n          homepage: plugin.homepage,\n          latest: plugin.latest,\n          hideLongProtocol: plugin.hideLongProtocol,\n          hideShortProtocol: plugin.hideShortProtocol,\n          path: plugin.path,\n          pluginVars: plugin.pluginVars\n        });\n      });\n    return pluginsOptions;\n  },\n  reloadRules: function (data, quite) {\n    var self = this;\n    var selectedName = storage.get('activeRules', true) || data.current;\n    var rulesList = [];\n    var rulesData = {};\n    rulesList.push(DEFAULT);\n    rulesData.Default = {\n      name: DEFAULT,\n      fixed: true,\n      value: data.defaultRules,\n      selected: !data.defaultRulesIsDisabled,\n      isDefault: true,\n      active: selectedName === DEFAULT\n    };\n    data.list.forEach(function (item) {\n      rulesList.push(item.name);\n      item = rulesData[item.name] = {\n        name: item.name,\n        value: item.data,\n        selected: item.selected,\n        active: selectedName === item.name\n      };\n    });\n    var changed = quite && updateData(rulesList, rulesData, self.state.rules);\n    self.state.rules.reset(rulesList, rulesData);\n    self.setState({});\n    return changed;\n  },\n  reloadValues: function (data, quite) {\n    var self = this;\n    var selectedName = storage.get('activeValues', true) || data.current;\n    var valuesList = [];\n    var valuesData = {};\n    data.list.forEach(function (item) {\n      valuesList.push(item.name);\n      valuesData[item.name] = {\n        name: item.name,\n        value: item.data,\n        active: selectedName === item.name\n      };\n    });\n    var changed = quite && updateData(valuesList, valuesData, self.state.values);\n    self.state.values.reset(valuesList, valuesData);\n    self.setState({});\n    return changed;\n  },\n  reloadDataQuite: function() {\n    this.reloadData(true);\n  },\n  reloadData: function (quite) {\n    var self = this;\n    var dialog = $('.w-reload-data-tips').closest('.w-confirm-reload-dialog');\n    var name = dialog.find('.w-reload-data-tips').attr('data-name');\n    var isRules = name === 'rules';\n    quite = quite === true;\n    var handleResponse = function (data, xhr) {\n      if (!data) {\n        !quite && util.showSystemError(xhr, true);\n        return setTimeout(function() {\n          events.trigger(isRules ? 'rulesChanged' : 'valuesChanged', true);\n        }, 2000);\n      }\n      if (isRules) {\n        if (self.reloadRules(data, quite)) {\n          events.trigger('rulesChanged', true);\n        }\n        self.triggerRulesChange('reload');\n      } else {\n        if (self.reloadValues(data, quite)) {\n          events.trigger('valuesChanged', true);\n        }\n        self.triggerValuesChange('reload');\n      }\n    };\n    if (isRules) {\n      dataCenter.rules.list(handleResponse);\n      events.trigger('reloadRulesRecycleBin');\n    } else {\n      dataCenter.values.list(handleResponse);\n      events.trigger('reloadValuesRecycleBin');\n    }\n  },\n  showReloadRules: function (force) {\n    if (this.rulesChanged && this.state.name === 'rules') {\n      this.rulesChanged = false;\n      var hasChanged = this.state.rules.hasChanged();\n      this.showReloadDialog(\n        'Rules changed. Reload now?',\n        hasChanged,\n        force\n      );\n    }\n  },\n  showReloadValues: function (force) {\n    if (this.valuesChanged && this.state.name === 'values') {\n      this.valuesChanged = false;\n      var hasChanged = this.state.values.hasChanged();\n      this.showReloadDialog(\n        'Values changed. Reload now?',\n        hasChanged,\n        force\n      );\n    }\n  },\n  componentDidUpdate: function () {\n    this.showReloadRules();\n    this.showReloadValues();\n  },\n  showReloadDialog: function (msg, existsUnsaved, force) {\n    var dialog = this.refs.confirmReload;\n    clearTimeout(this.reloadTimer);\n    var tips = $('.w-reload-data-tips');\n    tips.attr('data-name', this.state.name);\n    if (!force && !dialog.isVisible()) {\n      this.reloadTimer = setTimeout(this.reloadDataQuite, 1000);\n      return;\n    }\n    dialog.show();\n    if (existsUnsaved) {\n      msg +=\n        '<p class=\"w-confim-reload-note\">Warning: Unsaved changes will be lost</p>';\n    }\n    tips.html(msg);\n  },\n  showTab: function() {\n    var pageName = getPageName(this.state);\n    if (!pageName || pageName.indexOf('rules') != -1) {\n      this.showRules();\n    } else if (pageName.indexOf('values') != -1) {\n      this.showValues();\n    } else if (pageName.indexOf('plugins') != -1) {\n      this.showPlugins();\n    } else {\n      this.showNetwork();\n    }\n    storage.set('pageName', pageName || '');\n  },\n  switchTab: function(isBack) {\n    var self = this;\n    var name = self.state.name;\n    var tabs = [];\n    if (!self.hideNetwork) {\n      tabs.push('network');\n    }\n    if (!self.hideRules) {\n      tabs.push('rules');\n    }\n    if (!self.hideValues) {\n      tabs.push('values');\n    }\n    if (!self.hidePlugins) {\n      tabs.push('plugins');\n    }\n    var index = tabs.indexOf(name);\n    var len = tabs.length;\n    if (isBack) {\n      index = index - 1;\n      if (index < 0) {\n        index = len - 1;\n      }\n    } else {\n      index = index + 1;\n      if (index >= len) {\n        index = 0;\n      }\n    }\n    switch (tabs[index]) {\n    case 'network':\n      self.showNetwork();\n      break;\n    case 'rules':\n      self.showRules();\n      break;\n    case 'values':\n      self.showValues();\n      break;\n    case 'plugins':\n      self.showPlugins();\n      break;\n    }\n  },\n  componentDidMount: function () {\n    var self = this;\n    var clipboard = new Clipboard('.w-copy-text');\n    clipboard.on('error', function (e) {\n      win.alert('Copy failed');\n    });\n    clipboard = new Clipboard('.w-copy-text-with-tips');\n    clipboard.on('error', function (e) {\n      message.error('Copy failed');\n    });\n    clipboard.on('success', function (e) {\n      message.success('Copied clipboard');\n    });\n    var preventDefault = function (e) {\n      e.preventDefault();\n    };\n    events.on('showRulesDialog', function(_, data) {\n      if (data && !self.isHideRules()) {\n        self.refs.rulesDialog.show(data.rules, data.values);\n      }\n    });\n    events.on('changeRecordState', function (_, type) {\n      self.setState({ record: type }, self.updateList);\n    });\n    events.on('showHttpsSettingsDialog', self.showHttpsSettingsDialog);\n\n    if (isClient) {\n      var findEditor = function(keyword, prev) {\n        events.editorMatchedCount = 0;\n        events.trigger(prev ? 'findEditorPrev' : 'findEditorNext', keyword);\n        return events.editorMatchedCount;\n      };\n      window.__findWhistleCodeMirrorEditor_ = findEditor;\n    }\n\n    var composerDidMount;\n    var composerData;\n\n    events.one('networkDidMount', function() {\n      composerData && events.trigger('showComposerTab');\n    });\n\n    events.one('composerDidMount', function() {\n      composerDidMount = true;\n      if (composerData) {\n        events.trigger('_setComposerData', composerData);\n        composerData = null;\n      }\n    });\n\n    events.on('showPluginOptionTab', function(_, plugin) {\n      plugin && self.showPluginTab(util.getSimplePluginName(plugin));\n    });\n\n    events.on('disablePlugin', function(_, plugin, disabled) {\n      self.setPluginState(util.getSimplePluginName(plugin), disabled);\n    });\n\n    events.on('setComposerData', function(_, data) {\n      if (!data || self.state.rulesMode) {\n        return;\n      }\n      win.confirm('Do you confirm the changes to the composer\\'s data?', function(sure) {\n        if (sure) {\n          if (composerDidMount) {\n            events.trigger('_setComposerData', data);\n          } else {\n            composerData = data;\n          }\n        }\n      });\n    });\n\n    events.on('showPluginOption', function(_, plugin) {\n      if (!plugin) {\n        return;\n      }\n      var name = util.getSimplePluginName(plugin);\n      var url =  plugin.pluginHomepage || 'plugin.' + name + '/';\n      if ((plugin.pluginHomepage || plugin.openExternal) && !plugin.openInPlugins && !plugin.openInModal) {\n        return window.open(url);\n      }\n      var modal = plugin.openInModal || '';\n      if (modal && !plugin.pluginHomepage) {\n        url += '?openInModal=5b6af7b9884e1165';\n      }\n      self.refs.iframeDialog.show({\n        favicon: util.getPluginIcon(plugin),\n        name: name,\n        url: url,\n        homepage: plugin.homepage,\n        disabled: util.pluginIsDisabled(self.state, name),\n        width: modal.width,\n        height: modal.height\n      });\n    });\n    events.on('hidePluginOption', function() {\n      self.refs.iframeDialog.hide();\n    });\n\n    events.on('download', function(_, data) {\n      self.download(data);\n    });\n    events.on('showMockDialog', function(_, data) {\n      if (data) {\n        self.refs.mockDialog.show(data.item, data.type);\n      }\n    });\n    events.on('enableRecord', function () {\n      self.enableRecord();\n    });\n    events.on('showJsonViewDialog', function(_, data, keyPath) {\n      self.refs.jsonDialog.show(data, keyPath);\n    });\n    events.on('rulesChanged', function (_, force) {\n      self.rulesChanged = true;\n      self.showReloadRules(force === true);\n    });\n    events.on('switchTreeView', function () {\n      self.toggleTreeView();\n    });\n    events.on('updateGlobal', function () {\n      self.setState({});\n    });\n    events.on('valuesChanged', function (_, force) {\n      self.valuesChanged = true;\n      self.showReloadValues(force === true);\n    });\n    events.on('showNetwork', function () {\n      self.showNetwork();\n    });\n    events.on('showRules', function (_, name) {\n      self.showRules();\n      if (name && self.state.rules.exists(name)) {\n        events.trigger('expandRulesGroup', name);\n        self.setRulesActive(name);\n      }\n    });\n    events.on('showValues', function () {\n      self.showValues();\n    });\n    events.on('showPlugins', function (_, name) {\n      if (name && typeof name === 'string') {\n        self.setState({ active: 'Home' });\n        setTimeout(function() {\n          events.trigger('highlightPlugin', name);\n        }, 600);\n      }\n      self.showPlugins();\n    });\n    events.on('disableAllPlugins', self.disableAllPlugins);\n    events.on('disableAllRules', self.disableAllRules);\n    events.on('activeRules', function () {\n      var rulesModal = dataCenter.rulesModal;\n      if (rulesModal.exists(dataCenter.activeRulesName)) {\n        self.setRulesActive(dataCenter.activeRulesName, rulesModal);\n        self.setState({});\n      }\n    });\n\n    events.on('activeValues', function () {\n      var valuesModal = dataCenter.valuesModal;\n      if (valuesModal.exists(dataCenter.activeValuesName)) {\n        self.setValuesActive(dataCenter.activeValuesName, valuesModal);\n        self.setState({});\n      }\n    });\n    var editorWin;\n    events.on('openEditor', function(_, text) {\n      if (storage.get('viewAllInNewWindow') === '1') {\n        return util.openInNewWin(text || '');\n      }\n      try {\n        if (editorWin && typeof editorWin.setValue === 'function') {\n          window.getTextFromWhistle_ = null;\n          self.refs.editorWin.show();\n          return editorWin.setValue(text);\n        }\n        window._initWhistleTextEditor_ = function(win) {\n          editorWin = win;\n          editorWin.setValue(text);\n        };\n        self.refs.editorWin.show('editor.html');\n      } catch (e) {}\n    });\n    events.on('openInNewWin', function() {\n      try {\n        util.openInNewWin(editorWin.getEditorValue() || '');\n        self.refs.editorWin.hide();\n      } catch (e) {}\n    });\n\n    var updateTimer;\n    events.on('updateUIThrottle', function() {\n      if (updateTimer) {\n        return;\n      }\n      updateTimer = setTimeout(function() {\n        updateTimer = null;\n        self.setState({});\n      }, 200);\n    });\n\n    events.on('addNewRulesFile', function(_, data) {\n      var filename = data.filename;\n      var modal = self.state.rules;\n      var item = modal.add(filename, data.data);\n      modal.setChanged(filename, false);\n      self.setRulesActive(filename);\n      self.setState({ activeRules: item });\n      if (!data.update) {\n        self.triggerRulesChange('create');\n      }\n    });\n    events.on('addNewValuesFile', function(_, data) {\n      var filename = data.filename;\n      var modal = self.state.values;\n      var item = modal.add(filename, data.data);\n      modal.setChanged(filename, false);\n      if (data.update) {\n        self.setState({});\n      } else {\n        self.setValuesActive(filename);\n        self.setState({ activeValues: item });\n        self.triggerValuesChange('create');\n      }\n    });\n\n    events.on('recoverRules', function (_, data) {\n      var modal = self.state.rules;\n      var filename = data.filename;\n      var handleRecover = function (sure) {\n        if (!sure) {\n          return;\n        }\n        dataCenter.rules.add(\n          {\n            name: filename,\n            value: data.data,\n            recycleFilename: data.name\n          },\n          function (result, xhr) {\n            if (result && result.ec === 0) {\n              var item = modal.add(filename, data.data);\n              self.setRulesActive(filename);\n              self.setState({ activeRules: item });\n              self.triggerRulesChange('create');\n              events.trigger('rulesRecycleList', result);\n              events.trigger('focusRulesList');\n            } else {\n              util.showSystemError(xhr);\n            }\n          }\n        );\n      };\n      if (!modal.exists(filename)) {\n        return handleRecover(true);\n      }\n      win.confirm(\n        'The name \\'' + filename + '\\' is already in use. Overwrite?',\n        handleRecover\n      );\n    });\n\n    events.on('recoverValues', function (_, data) {\n      var modal = self.state.values;\n      var filename = data.filename;\n      var handleRecover = function (sure) {\n        if (!sure) {\n          return;\n        }\n        dataCenter.values.add(\n          {\n            name: filename,\n            value: data.data,\n            recycleFilename: data.name\n          },\n          function (result, xhr) {\n            if (result && result.ec === 0) {\n              var item = modal.add(filename, data.data);\n              self.setValuesActive(filename);\n              self.setState({ activeValues: item });\n              self.triggerValuesChange('create');\n              events.trigger('valuesRecycleList', result);\n            } else {\n              util.showSystemError(xhr);\n            }\n          }\n        );\n      };\n      if (!modal.exists(filename)) {\n        return handleRecover(true);\n      }\n      win.confirm(\n        'The name \\'' + filename + '\\' is already in use. Overwrite?',\n        handleRecover\n      );\n    });\n    events.on('networkImportFile', function (_, file) {\n      self.uploadSessionsForm(file);\n    });\n    events.on('networkImportData', function (_, data) {\n      self.importAnySessions(data);\n    });\n    events.on('rulesImportFile', function (_, file) {\n      handleImportData(file, self.handleImportRules);\n    });\n    events.on('rulesImportData', function (_, data) {\n      self.handleImportRules(data);\n    });\n    events.on('valuesImportFile', function (_, file) {\n      handleImportData(file, self.handleImportValues);\n    });\n    events.on('valuesImportData', function (_, data) {\n      self.handleImportValues(data);\n    });\n    events.on('networkSettingsImportFile composerImportFile rulesSettingsImportFile valuesSettingsImportFile', function (e, file) {\n      handleImportData(file, util.noop, e.type);\n    });\n    events.on('networkSettingsImportData composerImportData rulesSettingsImportFile valuesSettingsImportFile', function (_, data) {\n      util.handleImportData(data);\n    });\n    events.on('setRulesSettings', function (_, data) {\n      if (!data) {\n        return;\n      }\n      win.confirm('Do you confirm the changes to the rules settings?', function(sure) {\n        if (sure) {\n          self.setState({\n            rulesTheme: data.theme,\n            rulesFontSize: data.fontSize,\n            showRulesLineNumbers: data.lineNumbers,\n            autoRulesLineWrapping: data.autoLineWrapping\n          });\n          storage.set('rulesTheme', getString(data.theme).substring(0, 30));\n          storage.set('rulesFontSize', getString(data.fontSize).substring(0, 30));\n          storage.set('showRulesLineNumbers', !!data.lineNumbers);\n          storage.set('autoRulesLineWrapping', data.autoLineWrapping ? '1' : '');\n          self.setMultipleCohice(data.allowMultipleChoice);\n          self.setBackRulesFirst(data.backRulesFirst);\n        }\n      });\n    });\n    events.on('setValuesSettings', function (_, data) {\n      if (!data) {\n        return;\n      }\n      win.confirm('Do you confirm the changes to the values settings?', function(sure) {\n        if (sure) {\n          self.setState({\n            valuesTheme: data.theme,\n            valuesFontSize: data.fontSize,\n            showValuesLineNumbers: data.lineNumbers,\n            autoValuesLineWrapping: data.autoLineWrapping,\n            foldGutter: data.foldGutter\n          });\n          storage.set('valuesTheme', getString(data.theme).substring(0, 30));\n          storage.set('valuesFontSize', getString(data.fontSize).substring(0, 10));\n          storage.set('showValuesLineNumbers', !!data.lineNumbers);\n          storage.set('autoValuesLineWrapping', data.autoLineWrapping ? '1' : '');\n          storage.set('foldGutter', data.foldGutter ? '1' : '');\n        }\n      });\n    });\n\n    $(document)\n      .on('dragleave', preventDefault)\n      .on('dragenter', preventDefault)\n      .on('dragover', preventDefault)\n      .on('drop', function (e) {\n        e.preventDefault();\n        var files = e.originalEvent.dataTransfer.files;\n        var file = files && files[0];\n        if (!file) {\n          return;\n        }\n        var target = e.target;\n        if (target.nodeName === 'TEXTAREA') {\n          e.preventDefault();\n          target.readOnly = true;\n          setTimeout(function() {\n            target.readOnly = false;\n          }, 0);\n        }\n        target = $(target);\n        var iframe = target.closest('.w-fix-drag').find('iframe')[0];\n        if (iframe) {\n          try {\n            var win = iframe.contentWindow;\n            if (win && typeof win.onWhistleFileDrop === 'function') {\n              return win.onWhistleFileDrop(file);\n            }\n          } catch (e) {\n            console.error(e); // eslint-disable-line\n          }\n        }\n        if ($('.w-show-upload-temp-file.in').length) {\n          return events.trigger('uploadTempFile', file);\n        }\n        if ($('.w-import-dialog.in').length) {\n          return events.trigger('importFile', file);\n        }\n        var name = self.state.name;\n        var filename = file.name;\n        if (name === 'network') {\n          if (target.closest('.w-frames-com').length) {\n            return;\n          }\n          if (/\\.log$/i.test(filename)) {\n            if (file.size > MAX_LOG_SIZE) {\n              return win.alert('Maximum file size: 2MB');\n            }\n            util.readFileAsText(file, function (logs) {\n              logs = util.parseLogs(logs);\n              if (!logs) {\n                return;\n              }\n              if (dataCenter.uploadLogs !== null) {\n                dataCenter.uploadLogs = logs;\n              }\n              events.trigger('showLog');\n              events.trigger('uploadLogs', { logs: logs });\n            });\n            return;\n          }\n          return self.uploadSessionsForm(file);\n        }\n        handleImportData(file, function(json) {\n          if (json) {\n            if (name === 'rules') {\n              self.handleImportRules(json);\n            } else if (name === 'values') {\n              self.handleImportValues(json);\n            }\n          }\n        });\n      })\n      .on('keyup', function (e) {\n        if ((e.metaKey || e.ctrlKey) && e.keyCode === 82) {\n          !isClient && e.preventDefault();\n        } else if (e.keyCode === 191) {\n          var name = self.state.name;\n          var nodeName = document.activeElement && document.activeElement.nodeName;\n          if (nodeName !== 'INPUT' && nodeName !== 'TEXTAREA' && !$('.modal.in').length) {\n            if (name === 'network') {\n              if (!util.hasShortcut('focusNetworkSearchBox')) {\n                return;\n              }\n              events.trigger('focusNetworkFilterInput');\n            } else if (name === 'rules') {\n              if (!util.hasShortcut('focusRulesSearchBox')) {\n                return;\n              }\n              events.trigger('focusRulesFilterInput');\n            } else if (name === 'values') {\n              if (!util.hasShortcut('focusValuesSearchBox')) {\n                return;\n              }\n              events.trigger('focusValuesFilterInput');\n            }\n          }\n        }\n      })\n      .on('contextmenu', '.w-textarea-bar', function(e) {\n        e.preventDefault();\n      });\n    var removeItem = function (e) {\n      var target = e.target;\n      if (\n        target.nodeName == 'A' &&\n        $(target).parent().hasClass('w-list-data')\n      ) {\n        self.state.name == 'rules' ? self.removeRules() : self.removeValues();\n      }\n      e.preventDefault();\n    };\n\n    $(window)\n      .on('hashchange', self.showTab)\n      .on('keyup', function (e) {\n        if (e.keyCode == 27) {\n          self.setMenuOptionsState();\n          var dialog = $('.modal');\n          if (typeof dialog.modal == 'function') {\n            dialog.modal('hide');\n          }\n        }\n      })\n      .on('keydown', function (e) {\n        var name = self.state.name;\n        var code = e.keyCode;\n        code == 46 && removeItem(e);\n        if (!e.ctrlKey && !e.metaKey) {\n          if (code === 112) {\n            e.preventDefault();\n            window.open(util.getDocUrl('gui/' + name + '.html'));\n          } else if (code === 116) {\n            e.preventDefault();\n          }\n          return;\n        }\n        var isBack = code === 37;\n        if (isBack || code === 39) {\n          if (!util.hasShortcut(isBack ? 'switchTabReverse' : 'switchTab')) {\n            return;\n          }\n          self.switchTab(isBack);\n          return e.preventDefault();\n        }\n        if (code === 79) {\n          if (name === 'network') {\n            if (!util.hasShortcut('toggleNetworkState')) {\n              return;\n            }\n            events.trigger('toggleNetworkState');\n          } else if (name === 'rules') {\n            if (!util.hasShortcut('toggleRules')) {\n              return;\n            }\n            self.confirmDisableAllRules();\n          } else if (name === 'plugins') {\n            if (!util.hasShortcut('togglePlugins')) {\n              return;\n            }\n            self.confirmDisableAllPlugins();\n          }\n          e.preventDefault();\n        } else if (code === 76) {\n          if (name === 'network') {\n            if (!util.hasShortcut('toggleNetworkPanelLayout')) {\n              return;\n            }\n            events.trigger('toggleNetworkDock');\n          } else if (name === 'rules') {\n            if (!util.hasShortcut('toggleRulesNum')) {\n              return;\n            }\n            events.trigger('toggleRulesLineNumbers');\n          } else if (name === 'values') {\n            if (!util.hasShortcut('toggleValuesNum')) {\n              return;\n            }\n            events.trigger('toggleValuesLineNumbers');\n          }\n          e.preventDefault();\n        } else if (code === 82) {\n          !isClient && e.preventDefault();\n        } else if (code === 77) {\n          self.toggleLeftMenu();\n          e.preventDefault();\n        } else if (code === 66) {\n          if (!util.hasShortcut('switchNetworkView')) {\n            return;\n          }\n          self.toggleTreeView();\n          e.preventDefault();\n          events.trigger('toggleTreeViewByAccessKey');\n          return;\n        }\n        var isNetwork = name === 'network';\n        if (isNetwork && code == 88) {\n          if (\n            !util.isFocusEditor() &&\n            !$(e.target).closest('.w-frames-list').length &&\n            util.hasShortcut('clearNetworkSessions')\n          ) {\n            self.clear();\n          }\n          return;\n        }\n        if (code == 68) {\n          if (!util.hasShortcut(isNetwork ? 'removeNetworkSessions' : name === 'rules' ? 'removeRules' : 'removeValues')) {\n            return;\n          }\n          return removeItem(e);\n        }\n        var modal = self.state.network;\n        if (isNetwork && (code === 83 || code === 69)) {\n          if (code === 83) {\n            if (!util.hasShortcut('saveNetwork')) {\n              return;\n            }\n            e.preventDefault();\n            util.noModal() && events.trigger('saveSessions');\n            return;\n          }\n          e.preventDefault();\n          if (!util.noModal()) {\n            if (\n              $(findDOMNode(self.refs.chooseFileType)).is(':visible')\n            ) {\n              self.exportBySave();\n            }\n            return;\n          }\n          var nodeName = e.target.nodeName;\n          if (nodeName === 'INPUT' || nodeName === 'TEXTAREA') {\n            return;\n          }\n          var hasSelected = modal.hasSelected();\n          if (hasSelected) {\n            $(findDOMNode(self.refs.chooseFileType)).modal('show');\n            setTimeout(function () {\n              var input = findDOMNode(self.refs.sessionsName);\n              input.focus();\n              input.select();\n            }, 500);\n          }\n          return;\n        }\n        if (code === 69) {\n          if (!util.hasShortcut(isNetwork ? 'exportNetwork' : name === 'rules' ? 'exportRules' : 'exportValues')) {\n            return;\n          }\n          e.preventDefault();\n          return util.noModal() && self.exportData();\n        }\n        if (code === 190) {\n          if (!util.hasShortcut(isNetwork ? 'openNetworkSettings' : name === 'rules' ? 'openRulesSettings' : 'openValuesSettings')) {\n            return;\n          }\n          self.showSettings();\n          return e.preventDefault();\n        }\n        var isService = code === 74;\n        if (isService || code === 73) {\n          if (util.noModal()) {\n            if (isService) {\n              if (!dataCenter.whistleId || !util.hasShortcut('openService')) {\n                return;\n              }\n              self.showService();\n            } else if (isNetwork || name === 'rules' || name === 'values') {\n              if (!util.hasShortcut(isNetwork ? 'importNetwork' : name === 'rules' ? 'importRules' : 'importValues')) {\n                return;\n              }\n              self.importData();\n            } else if (name === 'plugins') {\n              if (!util.hasShortcut('openInstallPlugins')) {\n                return;\n              }\n              events.trigger('installPlugins');\n            }\n          }\n          e.preventDefault();\n        }\n      });\n\n    function getKey(url) {\n      if (!(url = url && url.trim())) {\n        return;\n      }\n\n      var index = url.indexOf('://') + 3;\n      url = index != -1 ? url.substring(index) : url;\n      if (url.indexOf('{') !== 0) {\n        return;\n      }\n\n      index = url.lastIndexOf('}');\n      return index > 1 ? url.substring(1, index) : null;\n    }\n\n    var isEditor = function () {\n      var name = self.state.name;\n      return name === 'rules' || name === 'values';\n    };\n\n    $(document.body)\n      .on('mouseenter', LINK_SELECTOR, function (e) {\n        if (!isEditor() || !(e.ctrlKey || e.metaKey)) {\n          return;\n        }\n        var elem = $(this);\n        var text;\n        if (\n          elem.hasClass('cm-js-http-url') ||\n          elem.hasClass('cm-string') ||\n          elem.hasClass('cm-js-at') ||\n          TEMP_LINK_RE.test(text = elem.text()) ||\n          isTextFile(text) ||\n          getKey(text)\n        ) {\n          elem.addClass('w-is-link');\n        }\n      })\n      .on('mouseleave', LINK_SELECTOR, function (e) {\n        $(this).removeClass('w-is-link');\n      })\n      .on('mousedown', LINK_SELECTOR, function (e) {\n        if (!isEditor() || !(e.ctrlKey || e.metaKey)) {\n          return;\n        }\n        var elem = $(this);\n        var text = elem.text();\n        if (elem.hasClass('cm-js-at')) {\n          if (AT_LINK_RE.test(text)) {\n            window.open((RegExp.$1 || 'http:') + RegExp.$2);\n          }\n          return;\n        }\n        if (elem.hasClass('cm-string')) {\n          if (LINK_RE.test(text)) {\n            window.open((RegExp.$1 || 'http:') + RegExp.$2);\n          }\n          return;\n        }\n        if (elem.hasClass('cm-js-http-url')) {\n          if (!/^https?:\\/\\//i.test(text)) {\n            text = 'http:' + (text[0] === '/' ? '' : '//') + text;\n          }\n          window.open(text);\n          return;\n        }\n        if (TEMP_LINK_RE.test(text) || (isTextFile(text) && FILE_PATH_RE.test(text))) {\n          var tempFile = RegExp.$1;\n          return events.trigger('showEditorDialog', [{\n            ruleName: self.getActiveRuleName(),\n            tempFile: tempFile\n          }, elem]);\n        } else {\n          var name = getKey(text);\n          if (name) {\n            var activeRule = self.state.rules.getActive();\n            var rulesText = activeRule && activeRule.value;\n            var values = {};\n            util.resolveInlineValues(rulesText, values);\n            if (values[name] == null) {\n              return events.trigger('showEditorDialog', { name: name });\n            }\n            return win.confirm('The value of \\'' + name + '\\' is stored in this rule file and cannot be synced if edited via the Values\\'s editor. Continue?', function (sure) {\n              if (sure) {\n                events.trigger('showEditorDialog', { name: name });\n              }\n            });\n          }\n        }\n      });\n\n    if (self.state.name == 'network') {\n      self.startLoadData(true);\n    }\n    dataCenter.on('settings', function (data) {\n      var state = self.state;\n      var server = data.server;\n      var hasChanged = state.whistleId !== server.whistleId;\n      if (hasChanged) {\n        state.whistleId = server.whistleId;\n      }\n      var caUrlList = [];\n      var caHash = util.getCAHash(server, caUrlList);\n      if (caHash !== state.caHash) {\n        state.caHash = caHash;\n        state.caUrlList = caUrlList;\n        hasChanged = true;\n      }\n      if (\n        state.interceptHttpsConnects !== data.interceptHttpsConnects ||\n        state.enableHttp2 !== data.enableHttp2 ||\n        state.disabledAllRules !== data.disabledAllRules ||\n        state.allowMultipleChoice !== data.allowMultipleChoice ||\n        state.disabledAllPlugins !== data.disabledAllPlugins ||\n        state.backRulesFirst !== data.backRulesFirst ||\n        state.ndp != server.ndp ||\n        state.ndr != server.ndr ||\n        state.drb != server.drb ||\n        state.drm != server.drm ||\n        state.port != server.port\n      ) {\n        state.interceptHttpsConnects = data.interceptHttpsConnects;\n        state.enableHttp2 = data.enableHttp2;\n        state.disabledAllRules = data.disabledAllRules;\n        state.allowMultipleChoice = data.allowMultipleChoice;\n        state.backRulesFirst = data.backRulesFirst;\n        state.disabledAllPlugins = data.disabledAllPlugins;\n        state.ndp = server.ndp;\n        state.ndr = server.ndr;\n        state.drb = server.drb;\n        state.drm = server.drm;\n        state.port = server.port;\n        protocols.setPlugins(state);\n        var list = LEFT_BAR_MENUS;\n        list[3].checked = !state.disabledAllRules;\n        list[4].checked = !state.disabledAllPlugins;\n        self.refs.contextMenu.update();\n        return self.setState({});\n      }\n      if (hasChanged) {\n        self.setState({});\n      }\n    });\n    dataCenter.on('rules', function (data) {\n      var modal = self.state.rules;\n      var newSelectedNames = data.list;\n      if (\n        !data.defaultRulesIsDisabled &&\n        newSelectedNames.indexOf('Default') === -1\n      ) {\n        newSelectedNames.unshift('Default');\n      }\n      var selectedNames = modal.getSelectedNames();\n      if (compareSelectedNames(selectedNames, newSelectedNames)) {\n        return;\n      }\n      self.reselectRules(data, true);\n      self.setState({});\n    });\n    dataCenter.on('serverInfo', function (data) {\n      self.serverInfo = data;\n    });\n\n    events.on('autoRefreshNetwork', function () {\n      !self.state.network.isTreeView && self.autoRefresh && self.autoRefresh();\n    });\n\n    var getFocusItemList = function (curItem) {\n      if (Array.isArray(curItem)) {\n        return curItem;\n      }\n      if (!curItem || curItem.selected) {\n        return;\n      }\n      return [curItem];\n    };\n\n    events.on('updateUI', function () {\n      self.setState({});\n    });\n\n    events.on('replaySessions', function (e, curItem, shiftKey) {\n      var modal = self.state.network;\n      var list = getFocusItemList(curItem) || modal.getSelectedList();\n      var len = list && list.length;\n      if (shiftKey && len === 1) {\n        self.replayList = list;\n        self.refs.setReplayCount.show();\n        setTimeout(function () {\n          var input = findDOMNode(self.refs.replayCount);\n          input.select();\n          input.focus();\n        }, 300);\n        return;\n      }\n      self.replay(e, list);\n    });\n    events.on('filterSessions', self.showSettings);\n    events.on('exportSessions', function (e, curItem, filename) {\n      self.exportData(e, getFocusItemList(curItem), filename);\n    });\n    events.on('abortRequest', function (e, curItem) {\n      self.abort(getFocusItemList(curItem));\n    });\n    events.on('removeIt', function (e, item) {\n      var modal = self.state.network;\n      if (item && modal) {\n        modal.remove(item);\n        self.setState({});\n      }\n    });\n    events.on('removeOthers', function (e, item) {\n      var modal = self.state.network;\n      if (item && modal) {\n        if (item.selected) {\n          modal.removeUnselectedItems();\n        } else {\n          modal.removeOthers(item);\n        }\n        self.setState({});\n      }\n    });\n    events.on('clearAll', self.clear);\n    events.on('removeSelected', function () {\n      var modal = self.state.network;\n      if (modal) {\n        modal.removeSelectedItems();\n        self.setState({});\n      }\n    });\n    events.on('removeUnselected', function () {\n      var modal = self.state.network;\n      if (modal) {\n        modal.removeUnselectedItems();\n        self.setState({});\n      }\n    });\n    events.on('removeUnmarked', function () {\n      var modal = self.state.network;\n      if (modal) {\n        modal.removeUnmarkedItems();\n        self.setState({});\n      }\n    });\n    events.on('saveRules', function (e, item) {\n      if (item.changed || !item.selected) {\n        var list = self.state.rules.getChangedGroupList(item);\n        list.forEach(self.selectRules);\n      } else {\n        self.unselectRules(item);\n      }\n    });\n    events.on('saveValues', function (e, item) {\n      var list = self.state.values.getChangedGroupList(item);\n      list.forEach(self.saveValues);\n    });\n    events.on('renameRules', function (e, item) {\n      self.showEditRules(item);\n    });\n    events.on('renameValues', function (e, item) {\n      self.showEditValues(item);\n    });\n    events.on('deleteRules', function (e, item) {\n      setTimeout(function () {\n        self.removeRules(item);\n      }, 0);\n    });\n    events.on('deleteValues', function (e, item) {\n      setTimeout(function () {\n        self.removeValues(item);\n      }, 0);\n    });\n    events.on('createRules', self.showCreateRules);\n    events.on('createValues', self.showCreateValues);\n    events.on('showImportDialog', function (_, name) {\n      self.refs.importDialog.show(name || self.state.name);\n    });\n    events.on('showExportDialog', function (_, name, data) {\n      self.refs.exportDialog.show(name || self.state.name, data);\n    });\n    events.on('exportData', self.exportData);\n    events.on('handleImportRules', function(_, data) {\n      self.handleImportRules(data);\n    });\n    events.on('handleImportValues', function(_, data) {\n      self.handleImportValues(data);\n    });\n    events.on('uploadRules', function (_, data) {\n      var form = getJsonForm(data);\n      form.append('replaceAll', '1');\n      dataCenter.upload.importRules(form, function (data, xhr) {\n        if (!data) {\n          util.showSystemError(xhr);\n        } else if (data.ec === 0) {\n          self.reloadRules(data);\n          message.success('Rules imported successfully');\n        } else {\n          win.alert(data.em);\n        }\n      });\n    });\n    events.on('uploadValues', function (_, data) {\n      var form = getJsonForm(data, 'values');\n      form.append('replaceAll', '1');\n      dataCenter.upload.importValues(form, function (data, xhr) {\n        if (!data) {\n          util.showSystemError(xhr);\n        }\n        if (data.ec === 0) {\n          self.reloadValues(data);\n          message.success('Values imported successfully');\n        } else {\n          win.alert(data.em);\n        }\n      });\n    });\n    var timeout;\n    var hidden = document.hidden;\n    var isAtBottom; // 记录 visibilitychange 之前的状态\n    $(document).on('visibilitychange', function () {\n      clearTimeout(timeout);\n      var isNetwork = self.state.name === 'network';\n      if (document.hidden || !isNetwork) {\n        if (isNetwork && hidden !== document.hidden) {\n          hidden = true;\n          isAtBottom = self.scrollerAtBottom && self.scrollerAtBottom();\n        }\n        return;\n      }\n      hidden = false;\n      timeout = setTimeout(function () {\n        var atBottom = isAtBottom || self.scrollerAtBottom && self.scrollerAtBottom();\n        isAtBottom = false;\n        self.setState({}, function () {\n          atBottom && self.autoRefresh();\n        });\n      }, 100);\n    });\n\n    setTimeout(function () {\n      dataCenter.checkUpdate(function (data) {\n        if (data && data.showUpdate) {\n          self.setState(\n            {\n              version: data.version,\n              latestVersion: data.latestVersion\n            },\n            function () {\n              $(findDOMNode(self.refs.showUpdateTipsDialog)).modal(\n                'show'\n              );\n            }\n          );\n        }\n      });\n    }, 10000);\n\n    dataCenter.getLogIdList = this.getLogIdListFromRules;\n    dataCenter.importAnySessions = self.importAnySessions;\n    dataCenter.on('plugins', function (data) {\n      var pluginsOptions = self.createPluginsOptions(data.plugins);\n      var oldPluginsOptions = self.state.pluginsOptions;\n      var oldDisabledPlugins = self.state.disabledPlugins;\n      var disabledAllPlugins = self.state.disabledAllPlugins;\n      var disabledPlugins = data.disabledPlugins;\n      if (\n        disabledAllPlugins == data.disabledAllPlugins &&\n        pluginsOptions.length == oldPluginsOptions.length\n      ) {\n        var hasUpdate;\n        for (var i = 0, len = pluginsOptions.length; i < len; i++) {\n          var plugin = pluginsOptions[i];\n          var oldPlugin = oldPluginsOptions[i];\n          if (\n            plugin.name != oldPlugin.name ||\n            plugin.latest !== oldPlugin.latest ||\n            plugin.mtime != oldPlugin.mtime || // 判断时间即可\n            oldDisabledPlugins[plugin.name] != disabledPlugins[plugin.name] ||\n            plugin.hideLongProtocol != oldPlugin.hideLongProtocol ||\n            plugin.hideShortProtocol != oldPlugin.hideShortProtocol ||\n            plugin.path != oldPlugin.path\n          ) {\n            hasUpdate = true;\n            break;\n          }\n        }\n        if (!hasUpdate) {\n          return;\n        }\n      }\n      var oldPlugins = self.state.plugins;\n      if (oldPlugins && data.plugins) {\n        Object.keys(data.plugins).forEach(function(name) {\n          var oldP = oldPlugins[name];\n          if (oldP) {\n            var p = data.plugins[name];\n            p.selectedRulesHistory = oldP.selectedRulesHistory;\n            p.selectedValuesHistory = oldP.selectedValuesHistory;\n          }\n        });\n      }\n      var pluginsState = {\n        plugins: data.plugins,\n        disabledPlugins: data.disabledPlugins,\n        pluginsOptions: pluginsOptions,\n        disabledAllPlugins: data.disabledAllPlugins\n      };\n      protocols.setPlugins(pluginsState);\n      self.setState(pluginsState);\n    });\n    try {\n      var onReady = window.parent.onWhistleReady;\n      if (typeof onReady === 'function') {\n        var selectItem = function(item) {\n          var modal = item && self.state.network;\n          var index = modal && modal.getList().indexOf(item);\n          if (index >= 0) {\n            events.trigger('selectedIndex', index);\n          }\n        };\n        var selectIndex = function (index) {\n          events.trigger('selectedIndex', index);\n        };\n        onReady({\n          url: location.href,\n          pageId: dataCenter.getPageId(),\n          compose: dataCenter.compose,\n          createComposeInterrupt: dataCenter.createComposeInterrupt,\n          importSessions: self.importAnySessions,\n          importHarSessions: self.importHarSessions,\n          clearSessions: self.clear,\n          selectIndex: selectIndex,\n          selectItem: selectItem,\n          setActive: function(item) {\n            if (item >= 0) {\n              selectIndex(item);\n            } else {\n              selectItem(item);\n            }\n          }\n        });\n      }\n    } catch (e) {}\n    self.handleDataUrl(dataUrl || util.getDataUrl());\n    dataCenter.handleDataUrl = self.handleDataUrl;\n    dataUrl = null;\n\n    var INTERVAL = 6000;\n    var curNetworkSettings;\n    var curRulesSettings;\n    var curValuesSettings;\n    var curWhistleId;\n    var saveSettings = function () {\n      if (!dataCenter.whistleId) {\n        curNetworkSettings = curRulesSettings = curValuesSettings = null;\n        return setTimeout(saveSettings, INTERVAL);\n      }\n      if (curWhistleId !== dataCenter.whistleId) {\n        curNetworkSettings = curRulesSettings = curValuesSettings = null;\n        curWhistleId = dataCenter.whistleId;\n      }\n      var networkSettings = JSON.stringify(self.refs.networkSettings.getSettings());\n      var rulesSettings = JSON.stringify(self.getRulesSettings());\n      var valuesSettings = JSON.stringify(self.getValuesSettings());\n      var data;\n      if (curNetworkSettings !== networkSettings) {\n        data = { networkSettings: networkSettings };\n      }\n      if (curRulesSettings !== rulesSettings) {\n        data = data || {};\n        data.rulesSettings = rulesSettings;\n      }\n      if (curValuesSettings !== valuesSettings) {\n        data = data || {};\n        data.valuesSettings = valuesSettings;\n      }\n      if (!data) {\n        return setTimeout(saveSettings, INTERVAL);\n      }\n      data.type = 'settings';\n      dataCenter.saveToService(data, function (result) {\n        setTimeout(saveSettings, INTERVAL);\n        if (result && result.ec === 0) {\n          curValuesSettings = valuesSettings;\n          curRulesSettings = rulesSettings;\n          curNetworkSettings = networkSettings;\n        }\n      });\n    };\n    setTimeout(saveSettings, INTERVAL);\n  },\n  shouldComponentUpdate: function (_, nextSate) {\n    var name = this.state.name;\n    if (name === 'network' && nextSate.name !== name) {\n      this._isAtBottom = this.scrollerAtBottom && this.scrollerAtBottom();\n    }\n    return true;\n  },\n  handleDataUrl: function(url) {\n    url = getString(url);\n    if (!url) {\n      return;\n    }\n    var self = this;\n    dataCenter.getRemoteData(url, function(err, data) {\n      if (!err) {\n        self.importAnySessions(data);\n      }\n    });\n  },\n  importAnySessions: function (data) {\n    if (data && !util.handleImportData(data)) {\n      var isArr = Array.isArray(data);\n      if (!isArr && !Array.isArray(data.log && data.log.entries)) {\n        isArr = true;\n        data = [data];\n      }\n      if (Array.isArray(data)) {\n        dataCenter.addNetworkList(data);\n      } else {\n        this.importHarSessions(data);\n      }\n    }\n  },\n  donotShowAgain: function () {\n    dataCenter.donotShowAgain();\n  },\n  hideUpdateTipsDialog: function () {\n    $(findDOMNode(this.refs.showUpdateTipsDialog)).modal('hide');\n  },\n  getAllRulesText: function () {\n    var text = ' ' + this.getAllRulesValue();\n    return text.replace(/#[^\\r\\n]*[\\r\\n]/g, '\\n');\n  },\n  getLogIdListFromRules: function () {\n    var text = this.getAllRulesText();\n    if (\n      (text = text.match(\n        /\\slog:\\/\\/(?:\\{[^\\s]{1,36}\\}|[^/\\\\{}()<>\\s]{1,36})\\s/g\n      ))\n    ) {\n      var flags = {};\n      text = text\n        .map(function (logId) {\n          logId = util.removeProtocol(logId.trim());\n          if (logId[0] === '{') {\n            logId = logId.slice(1, -1);\n          }\n          return logId;\n        })\n        .filter(function (logId) {\n          if (!logId) {\n            return false;\n          }\n          if (!flags[logId]) {\n            flags[logId] = 1;\n            return true;\n          }\n          return false;\n        });\n    }\n    return text;\n  },\n  getWeinreFromRules: function () {\n    var values = this.state.values;\n    var text = this.getAllRulesText();\n    if ((text = text.match(/(?:^|\\s)weinre:\\/\\/[^\\s#]+(?:$|\\s)/gm))) {\n      var flags = {};\n      text = text\n        .map(function (weinre) {\n          weinre = util.removeProtocol(weinre.trim());\n          var value = getValue(weinre);\n          if (value !== false) {\n            return value;\n          }\n          var key = getKey(weinre);\n          if (key !== false) {\n            key = values.get(key);\n            return key && key.value;\n          }\n\n          return weinre;\n        })\n        .filter(function (weinre) {\n          if (!weinre) {\n            return false;\n          }\n          if (!flags[weinre]) {\n            flags[weinre] = 1;\n            return true;\n          }\n          return false;\n        });\n    }\n\n    return text;\n  },\n  getValuesFromRules: function () {\n    var text = ' ' + this.getAllRulesValue();\n    if ((text = text.match(/\\s(?:[\\w-]+:\\/\\/)?\\{[^\\s#]+\\}/g))) {\n      text = text\n        .map(function (key) {\n          return getKey(util.removeProtocol(key.trim()));\n        })\n        .filter(function (key) {\n          return !!key;\n        });\n    }\n    return text;\n  },\n  getAllRulesValue: function () {\n    var result = [];\n    var activeList = [];\n    var selectedList = [];\n    var modal = this.state.rules;\n    modal.list.forEach(function (name) {\n      var item = modal.get(name);\n      var value = item.value || '';\n      if (item.active) {\n        activeList.push(value);\n      } else if (item.selected) {\n        selectedList.push(value);\n      } else {\n        result.push(value);\n      }\n    });\n    modal = this.state.values;\n    modal.list.forEach(function (name) {\n      if (/\\.rules$/.test(name)) {\n        result.push(modal.get(name).value);\n      }\n    });\n\n    return activeList.concat(selectedList).concat(result).join('\\r\\n');\n  },\n  preventBlur: function (e) {\n    e.target.nodeName != 'INPUT' && e.preventDefault();\n  },\n  startLoadData: function (init) {\n    var self = this;\n    if (self._updateNetwork) {\n      if (init) {\n        self._updateNetwork();\n      } else {\n        setTimeout(self._updateNetwork, 30);\n      }\n      return;\n    }\n    var scrollTimeout;\n    var baseDom = $('.w-req-data-list .ReactVirtualized__Grid:first').scroll(\n      function () {\n        var modal = self.state.network;\n        scrollTimeout && clearTimeout(scrollTimeout);\n        scrollTimeout = null;\n        if (atBottom()) {\n          scrollTimeout = setTimeout(function () {\n            update(modal, true);\n          }, 1000);\n        }\n      }\n    );\n\n    var timeout;\n    var con = baseDom[0];\n    this.container = baseDom;\n    dataCenter.on('data', update);\n\n    function update(modal, _atBottom) {\n      modal = modal || self.state.network;\n      clearTimeout(timeout);\n      timeout = null;\n      if (self.state.name != 'network') {\n        return;\n      }\n      _atBottom = _atBottom || atBottom();\n      if (modal.update(_atBottom) && _atBottom) {\n        timeout = setTimeout(update, 3000);\n      }\n      if (document.hidden) {\n        return;\n      }\n      self.setState({}, function () {\n        _atBottom && scrollToBottom();\n      });\n    }\n\n    function scrollToBottom(force) {\n      if (force || !self.state.network.isTreeView) {\n        con.scrollTop = 10000000;\n      }\n    }\n\n    $(document).on('dblclick', '.w-network-menu-list', function (e) {\n      if ($(e.target).hasClass('w-network-menu-list')) {\n        if (con.scrollTop < 1) {\n          scrollToBottom(true);\n        } else {\n          con.scrollTop = 0;\n        }\n      }\n    });\n\n    self._updateNetwork = update;\n    self.autoRefresh = scrollToBottom;\n    self.scrollerAtBottom = atBottom;\n\n    function atBottom(force) {\n      var body = baseDom.find('.ReactVirtualized__Grid__innerScrollContainer')[0];\n      if (!body) {\n        force && events.trigger('toggleBackToBottomBtn', false);\n        return true;\n      }\n      var height = con.offsetHeight + 5;\n      var ctnHeight = body.offsetHeight;\n      var isBottom = con.scrollTop + height > ctnHeight;\n      events.trigger('toggleBackToBottomBtn', !isBottom && ctnHeight >= height);\n      return isBottom;\n    }\n\n    events.on('checkAtBottom', atBottom);\n  },\n  showPlugins: function (e) {\n    if (this.state.name != 'plugins') {\n      this.setMenuOptionsState();\n      this.hidePluginsOptions();\n    } else if (e && !this.state.showLeftMenu) {\n      this.showPluginsOptions();\n    }\n    this.setState({\n      hasPlugins: true,\n      name: 'plugins'\n    });\n    util.changePageName('plugins');\n  },\n  handleAction: function (type) {\n    if (type === 'top') {\n      this.container[0].scrollTop = 0;\n      return;\n    }\n    if (type === 'bottom') {\n      return this.autoRefresh(true);\n    }\n    if (type === 'pause') {\n      events.trigger('changeRecordState', type);\n      return dataCenter.pauseNetworkRecord();\n    }\n    var refresh = type === 'refresh';\n    if (refresh) {\n      events.trigger('changeRecordState');\n    } else {\n      events.trigger('changeRecordState', 'stop');\n    }\n    dataCenter.stopNetworkRecord(!refresh);\n    if (refresh) {\n      return this.autoRefresh();\n    }\n  },\n  showNetwork: function (e) {\n    var self = this;\n    if (self.state.name == 'network') {\n      e && !self.state.showLeftMenu && self.showNetworkOptions();\n      return;\n    }\n    self.setMenuOptionsState();\n    self.setState(\n      {\n        hasNetwork: true,\n        name: 'network'\n      },\n      function () {\n        self.startLoadData();\n        if (self._isAtBottom) {\n          self._isAtBottom = false;\n          self.autoRefresh && self.autoRefresh();\n        }\n      }\n    );\n    util.changePageName('network');\n  },\n  handleNetwork: function (item, e) {\n    var modal = this.state.network;\n    if (item.id == 'removeAll') {\n      this.clear();\n    } else if (item.id == 'removeSelected') {\n      modal.removeSelectedItems();\n    } else if (item.id == 'removeUnselected') {\n      modal.removeUnselectedItems();\n    } else if (item.id == 'exportWhistleFile') {\n      this.exportData();\n    } else if (item.id === 'toggleView') {\n      this.toggleTreeView();\n    } else if (item.id === 'importSessions') {\n      this.importData();\n    }\n    this.hideNetworkOptions();\n  },\n  importData: function () {\n    this.refs.importDialog.show(this.state.name);\n  },\n  getRulesSettings: function() {\n    var state = this.state;\n    return {\n      type: 'setRulesSettings',\n      theme: state.rulesTheme || 'cobalt',\n      fontSize: state.rulesFontSize || '14px',\n      lineNumbers: !!state.showRulesLineNumbers,\n      autoLineWrapping: !!state.autoRulesLineWrapping,\n      allowMultipleChoice: !!state.allowMultipleChoice,\n      backRulesFirst: !!state.backRulesFirst\n    };\n  },\n  getValuesSettings: function() {\n    var state = this.state;\n    return {\n      type: 'setValuesSettings',\n      theme: state.valuesTheme || 'cobalt',\n      fontSize: state.rulesFontSize || '14px',\n      lineNumbers: !!state.showValuesLineNumbers,\n      autoLineWrapping: !!state.autoValuesLineWrapping,\n      foldGutter: !!state.foldGutter\n    };\n  },\n  importRulesSettings: function() {\n    this.refs.importDialog.show('rulesSettings');\n  },\n  exportRulesSettings: function() {\n    this.refs.exportDialog.show('rulesSettings', this.getRulesSettings());\n  },\n  importValuesSettings: function() {\n    this.refs.importDialog.show('valuesSettings');\n  },\n  exportValuesSettings: function() {\n    this.refs.exportDialog.show('valuesSettings', this.getValuesSettings());\n  },\n  getInputValue: function () {\n    return util.formatFilename(findDOMNode(this.refs.sessionsName).value.trim());\n  },\n  filterFilename: function (e) {\n    this.setState({ filename: util.formatFilename(e.target.value) });\n  },\n  exportData: function (e, curItem, filename) {\n    switch (this.state.name) {\n    case 'network':\n      var modal = this.state.network;\n      var hasSelected = Array.isArray(curItem) || modal.hasSelected();\n      this.currentFoucsItem = curItem;\n      if (hasSelected) {\n        $(findDOMNode(this.refs.chooseFileType)).modal('show');\n        var input = findDOMNode(this.refs.sessionsName);\n        if (filename && typeof filename === 'string') {\n          input.value = filename;\n        }\n        setTimeout(function () {\n          input.focus();\n          input.select();\n        }, 500);\n      } else {\n        message.info('Please select one or more sessions first');\n      }\n      break;\n    case 'rules':\n      this.showAndActiveRules({ id: 'exportRules' });\n      break;\n    case 'values':\n      this.showAndActiveValues({ id: 'exportValues' });\n      break;\n    }\n  },\n  showService: function () {\n    util.showService(this.state.name);\n  },\n  importSessionsFromUrl: function (url) {\n    var self = this;\n    url && dataCenter.getRemoteData(url, function (err, data) {\n      if (!err) {\n        self.importAnySessions(data);\n      }\n    });\n  },\n  handleImportRules: function(data) {\n    if (data && !util.handleImportData(data)) {\n      this.showKVDialog(data);\n    }\n  },\n  handleImportValues: function(data) {\n    if (data && !util.handleImportData(data)) {\n      this.showKVDialog(data, true);\n    }\n  },\n  showAndActiveRules: function (item, e) {\n    if (this.state.name === 'rules') {\n      switch (item.id) {\n      case 'exportRules':\n        this.refs.selectRulesDialog.show();\n        break;\n      case 'importRules':\n        this.importData();\n        break;\n      }\n    } else {\n      this.setRulesActive(item.name);\n      this.showRules();\n    }\n    this.hideRulesOptions();\n  },\n  showRules: function (e) {\n    if (this.state.name != 'rules') {\n      this.setMenuOptionsState();\n      this.hideRulesOptions();\n    } else if (e && !this.state.showLeftMenu) {\n      this.showRulesOptions(e);\n    }\n    this.setState({\n      hasRules: true,\n      name: 'rules'\n    });\n    util.changePageName('rules');\n  },\n  showAndActiveValues: function (item, e) {\n    var self = this;\n    if (self.state.name === 'values' && item.id) {\n      switch (item.id) {\n      case 'exportValues':\n        self.refs.selectValuesDialog.show();\n        break;\n      case 'importValues':\n        this.importData();\n        break;\n      }\n    } else {\n      var modal = self.state.values;\n      var name = item.name;\n\n      if (!modal.exists(name)) {\n        dataCenter.values.add({ name: name }, function (data, xhr) {\n          if (data && data.ec === 0) {\n            var item = modal.add(name);\n            self.setValuesActive(name);\n            self.setState({\n              activeValues: item\n            });\n            events.trigger('focusValuesList');\n          } else {\n            util.showSystemError(xhr);\n          }\n        });\n      } else {\n        self.setValuesActive(name);\n      }\n\n      this.showValues();\n    }\n    self.hideValuesOptions();\n  },\n  addValue: function () {},\n  showValues: function (e) {\n    if (this.state.name != 'values') {\n      this.setMenuOptionsState();\n      this.hideValuesOptions();\n    } else if (e && !this.state.showLeftMenu) {\n      this.showValuesOptions(e);\n    }\n    this.setState({\n      hasValues: true,\n      name: 'values'\n    });\n    util.changePageName('values');\n  },\n  showNetworkOptions: function () {\n    if (this.state.name == 'network') {\n      this.setState({\n        showNetworkOptions: true\n      });\n    }\n  },\n  hideNetworkOptions: function () {\n    this.setState({\n      showAbortOptions: false,\n      showNetworkOptions: false\n    });\n  },\n  showAbortOptions: function () {\n    var modal = this.state.network;\n    var list = modal.getSelectedList();\n    ABORT_OPTIONS[0].disabled = !list || !list.filter(util.canAbort).length;\n    this.setState({\n      showAbortOptions: true\n    });\n  },\n  showCreateOptions: function () {\n    this.setState({\n      showCreateOptions: true\n    });\n  },\n  hideCreateOptions: function () {\n    this.setState({\n      showCreateOptions: false\n    });\n  },\n  hideAbortOptions: function () {\n    this.setState({\n      showAbortOptions: false\n    });\n  },\n  showHelpOptions: function () {\n    this.setState({\n      showHelpOptions: true\n    });\n  },\n  hideHelpOptions: function () {\n    this.setState({\n      showHelpOptions: false\n    });\n  },\n  showHasNewVersion: function (hasNewVersion) {\n    this.setState({\n      hasNewVersion: hasNewVersion\n    });\n  },\n  showRulesOptions: function (e) {\n    var self = this;\n    var rules = self.state.rules;\n    var data = rules.data;\n    var rulesOptions;\n    var rulesList = rules.list;\n    if (self.state.name === 'rules') {\n      var len = rulesList.length;\n      RULES_ACTIONS[0].disabled = len < 2;\n      RULES_ACTIONS[1].disabled = len < 1;\n      rulesOptions = RULES_ACTIONS;\n    } else {\n      rulesOptions = [];\n      rulesList.forEach(function (name) {\n        rulesOptions.push(data[name]);\n      });\n    }\n    self.setState({\n      rulesOptions: rulesOptions,\n      showRulesOptions: true\n    });\n  },\n  hideRulesOptions: function () {\n    this.setState({\n      showRulesOptions: false\n    });\n  },\n  showValuesOptions: function (e) {\n    var self = this;\n    var valuesOptions;\n    var valuesList = this.state.values.list;\n    if (self.state.name === 'values') {\n      var len = valuesList.length;\n      VALUES_ACTIONS[0].disabled = len < 2;\n      VALUES_ACTIONS[1].disabled = len < 1;\n      valuesOptions = VALUES_ACTIONS;\n    } else {\n      valuesOptions = [];\n      var list = self.getValuesFromRules() || [];\n      list = util.unique(valuesList.concat(list));\n      var newValues = [];\n      list.forEach(function (name) {\n        var exists = valuesList.indexOf(name) != -1;\n        var item = {\n          name: name,\n          icon: exists ? 'edit' : 'plus'\n        };\n        exists ? valuesOptions.push(item) : newValues.push(item);\n      });\n      valuesOptions = newValues.concat(valuesOptions);\n    }\n    self.setState({\n      valuesOptions: valuesOptions,\n      showValuesOptions: true\n    });\n  },\n  hideValuesOptions: function () {\n    this.setState({\n      showValuesOptions: false\n    });\n  },\n  showAndActivePlugins: function (option) {\n    this.hidePluginsOptions();\n    this.showPlugins();\n    this.showPluginTab(option.name);\n  },\n  showPluginTab: function (name) {\n    var active = 'Home';\n    var tabs = this.state.tabs || [];\n    if (name && name != active) {\n      for (var i = 0, len = tabs.length; i < len; i++) {\n        if (tabs[i].name == name) {\n          active = name;\n          name = null;\n          break;\n        }\n      }\n    }\n    var plugin = name && this.state.plugins[name + ':'];\n    if (plugin) {\n      if (tabs.length >= MAX_PLUGINS_TABS) {\n        win.alert('Maximum ' + MAX_PLUGINS_TABS + ' tabs allowed');\n        return this.showPlugins();\n      }\n      active = name;\n      if (plugin.pluginHomepage && !plugin.openInPlugins) {\n        return window.open(plugin.pluginHomepage);\n      }\n      tabs.push({\n        name: name,\n        url: plugin.pluginHomepage || 'plugin.' + name + '/'\n      });\n    }\n\n    this.setState({\n      active: active,\n      tabs: tabs\n    });\n    this.updatePluginTabInfo(tabs, active);\n  },\n  updatePluginTabInfo: function(tabs, active) {\n    tabs = tabs.map(function(tab) {\n      return tab.name;\n    });\n    storage.set('activePluginTabList', JSON.stringify(tabs));\n    active && storage.set('activePluginTabName', active);\n  },\n  activePluginTab: function (e) {\n    this.showPluginTab($(e.target).attr('data-name'));\n  },\n  closePluginTab: function (e) {\n    var name = $(e.target).attr('data-name');\n    var tabs = this.state.tabs || [];\n    for (var i = 0, len = tabs.length; i < len; i++) {\n      if (tabs[i].name == name) {\n        tabs.splice(i, 1);\n        var active = this.state.active;\n        if (active == name) {\n          var plugin = tabs[i] || tabs[i - 1];\n          this.state.active = plugin ? plugin.name : null;\n        }\n        this.setState({\n          tabs: tabs\n        });\n        this.updatePluginTabInfo(tabs);\n        return;\n      }\n    }\n  },\n  showPluginsOptions: function (e) {\n    this.setState({\n      showPluginsOptions: true\n    });\n  },\n  hidePluginsOptions: function () {\n    this.setState({\n      showPluginsOptions: false\n    });\n  },\n  showWeinreOptionsQuick: function (e) {\n    var list = this.getWeinreFromRules();\n    if (!list || !list.length) {\n      this.showAnonymousWeinre();\n      return;\n    }\n    $(e.target).closest('div').addClass('w-menu-wrapper-show');\n    util.shakeElem($(findDOMNode(this.refs.weinreMenuItem)));\n  },\n  showWeinreOptions: function (e) {\n    var self = this;\n    var list = (self.state.weinreOptions = self.getWeinreFromRules() || []);\n    self.state.weinreOptions = util.unique(list).map(function (name) {\n      return {\n        name: name,\n        icon: 'console'\n      };\n    });\n    self.setState({\n      showWeinreOptions: true\n    });\n  },\n  hideWeinreOptions: function () {\n    this.setState({\n      showWeinreOptions: false\n    });\n  },\n  setMenuOptionsState: function (name, callback) {\n    var state = {\n      showCreateRules: false,\n      showCreateValues: false,\n      showEditRules: false,\n      showEditValues: false,\n      showCreateOptions: false\n    };\n    if (name) {\n      state[name] = true;\n    }\n    this.setState(state, callback);\n  },\n  hideRulesInput: function () {\n    this.setState({ showCreateRules: false });\n  },\n  hideValuesInput: function () {\n    this.setState({ showCreateValues: false });\n  },\n  hideRenameRuleInput: function () {\n    this.setState({ showEditRules: false });\n  },\n  hideRenameValueInput: function () {\n    this.setState({ showEditValues: false });\n  },\n  showCreateRules: function (_, group, focusItem) {\n    var createRulesInput = findDOMNode(this.refs.createRulesInput);\n    this._curFocusRulesGroup = group;\n    this._curFocusRulesItem = focusItem;\n    this.setState(\n      {\n        showCreateRules: true\n      },\n      function () {\n        createRulesInput.focus();\n      }\n    );\n  },\n  showCreateValues: function (_, group, focusItem) {\n    var createValuesInput = findDOMNode(this.refs.createValuesInput);\n    this._curFocusValuesGroup = group;\n    this._curFocusValuesItem = focusItem;\n    this.setState(\n      {\n        showCreateValues: true\n      },\n      function () {\n        createValuesInput.focus();\n      }\n    );\n  },\n  showHttpsSettingsDialog: function () {\n    this.refs.httpsSettings.show();\n  },\n  interceptHttpsConnects: function (e) {\n    var self = this;\n    var checked = e.target.checked;\n    dataCenter.interceptHttpsConnects(\n      { interceptHttpsConnects: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.state.interceptHttpsConnects = checked;\n          dataCenter.isCapture = checked ? 1 : 0;\n          events.trigger('reqTabsChange');\n          events.trigger('resTabsChange');\n        } else {\n          util.showSystemError(xhr);\n        }\n        self.setState({});\n      }\n    );\n  },\n  enableHttp2: function (e) {\n    var self = this;\n    if (!dataCenter.supportH2) {\n      win.confirm(\n        'HTTP/2 requires Node.js LTS version v16+. Please upgrade',\n        function (sure) {\n          sure && window.open('https://nodejs.org/');\n          self.setState({});\n        }\n      );\n      return;\n    }\n    var checked = e.target.checked;\n    dataCenter.enableHttp2(\n      { enableHttp2: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.state.enableHttp2 = checked;\n        } else {\n          util.showSystemError(xhr);\n        }\n        self.setState({});\n      }\n    );\n  },\n  createRules: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var self = this;\n    var target = findDOMNode(self.refs.createRulesInput);\n    var name = target.value.trim();\n    if (!name) {\n      message.error('The name is required');\n      return;\n    }\n    var modal = self.state.rules;\n    var type = e && e.target.getAttribute('data-type');\n    var isGroup;\n    if (type === 'group') {\n      isGroup = true;\n      name = '\\r' + name;\n    }\n    if (modal.exists(name)) {\n      message.error('The name \\'' + name + '\\' is already in use');\n      return;\n    }\n    var addToTop = type === 'top' ? 1 : '';\n    var groupItem = self._curFocusRulesGroup;\n    var focusItem = self._curFocusRulesItem;\n    var params = { name: name, addToTop: addToTop };\n    if (isGroup) {\n      var focusName = focusItem && focusItem.name;\n      if (focusName) {\n        if (focusName === 'Default') {\n          focusName = self.state.rules.list[1];\n        }\n        params.focusName = focusName;\n      }\n    } else if (groupItem) {\n      params.groupName = groupItem.name;\n    }\n    dataCenter.rules.add(params, function (data, xhr) {\n      if (data && data.ec === 0) {\n        var item = modal[addToTop ? 'unshift' : 'add'](name);\n        target.value = '';\n        target.blur();\n        var toName = params.focusName;\n        if (toName) {\n          modal.moveTo(name, toName);\n        } else {\n          modal.moveToGroup(name, params.groupName, addToTop);\n        }\n        if (isGroup) {\n          if (item) {\n            item._isNewGroup = true;\n          }\n        } else {\n          self.setRulesActive(name);\n        }\n        params.groupName && events.trigger('expandRulesGroup', params.groupName);\n        self.setState(isGroup ? {} : {\n          activeRules: item\n        });\n        self.triggerRulesChange('create');\n      } else {\n        util.showSystemError(xhr);\n      }\n    }\n    );\n  },\n  createValues: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var self = this;\n    var target = findDOMNode(self.refs.createValuesInput);\n    var name = target.value.trim();\n    if (!name) {\n      message.error('The name is required');\n      return;\n    }\n\n    if (/\\s/.test(name)) {\n      message.error('Spaces are not allowed in the name');\n      return;\n    }\n\n    if (/#/.test(name)) {\n      message.error('Special character \\'#\\' is not allowed in the name');\n      return;\n    }\n\n    var modal = self.state.values;\n    var type = e && e.target.getAttribute('data-type');\n    var isGroup;\n    if (type === 'group') {\n      isGroup = true;\n      name = '\\r' + name;\n    }\n    if (modal.exists(name)) {\n      message.error('The name \\'' + name + '\\' is already in use');\n      return;\n    }\n    var groupItem = self._curFocusValuesGroup;\n    var focusItem = self._curFocusValuesItem;\n    var params = { name: name };\n    if (isGroup) {\n      if (focusItem) {\n        params.focusName = focusItem.name;\n      }\n    } else if (groupItem) {\n      params.groupName = groupItem.name;\n    }\n    dataCenter.values.add(params, function (data, xhr) {\n      if (data && data.ec === 0) {\n        var item = modal.add(name);\n        target.value = '';\n        target.blur();\n        var toName = params.focusName;\n        if (toName) {\n          modal.moveTo(name, toName);\n        } else {\n          modal.moveToGroup(name, params.groupName);\n        }\n        if (isGroup) {\n          if (item) {\n            item._isNewGroup = true;\n          }\n        } else {\n          self.setValuesActive(name);\n        }\n        params.groupName && events.trigger('expandValuesGroup', params.groupName);\n        self.setState(isGroup ? {} : {\n          activeValues: item\n        });\n        self.triggerValuesChange('create');\n      } else {\n        util.showSystemError(xhr);\n      }\n    });\n  },\n  showEditRules: function (item) {\n    this.currentFocusRules = item;\n    var modal = this.state.rules;\n    var activeItem = item || modal.getActive();\n    if (!activeItem || activeItem.isDefault) {\n      return;\n    }\n    var editRulesInput = findDOMNode(this.refs.editRulesInput);\n    editRulesInput.value = activeItem.name;\n    this.setState(\n      {\n        showEditRules: true,\n        selectedRule: activeItem\n      },\n      function () {\n        editRulesInput.select();\n        editRulesInput.focus();\n      }\n    );\n  },\n  showEditValuesByDBClick: function (item) {\n    !item.changed && this.showEditValues();\n  },\n  showEditValues: function (item) {\n    this.currentFocusValues = item;\n    var modal = this.state.values;\n    var activeItem = item || modal.getActive();\n    if (!activeItem || activeItem.isDefault) {\n      return;\n    }\n\n    var editValuesInput = findDOMNode(this.refs.editValuesInput);\n    editValuesInput.value = activeItem.name;\n    this.setState(\n      {\n        showEditValues: true,\n        selectedValue: activeItem\n      },\n      function () {\n        editValuesInput.select();\n        editValuesInput.focus();\n      }\n    );\n  },\n  editRules: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var self = this;\n    var modal = self.state.rules;\n    var activeItem = this.currentFocusRules || modal.getActive();\n    if (!activeItem) {\n      return;\n    }\n    var target = findDOMNode(self.refs.editRulesInput);\n    var isGroup = util.isGroup(activeItem.name);\n    var name = (isGroup ? '\\r' : '') + target.value.trim();\n    if (!name) {\n      message.error('The name is required');\n      return;\n    }\n\n    if (modal.exists(name)) {\n      message.error('The name \\'' + name + '\\' is already in use');\n      return;\n    }\n    var curName = activeItem.name;\n    dataCenter.rules.rename(\n      { name: curName, newName: name },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          modal.rename(curName, name);\n          target.value = '';\n          target.blur();\n          !isGroup && self.setRulesActive(name);\n          events.trigger('rulesNameChanged', [curName, name]);\n          self.setState({ activeRules: modal.getActive() });\n          self.triggerRulesChange('rename');\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n  },\n  editValues: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var self = this;\n    var modal = self.state.values;\n    var activeItem = this.currentFocusValues || modal.getActive();\n    if (!activeItem) {\n      return;\n    }\n    var target = findDOMNode(self.refs.editValuesInput);\n    var isGroup = util.isGroup(activeItem.name);\n    var name = (isGroup ? '\\r' : '') + target.value.trim();\n    if (!name) {\n      message.error('The name is required');\n      return;\n    }\n\n    if (modal.exists(name)) {\n      message.error('The name \\'' + name + '\\' is already in use');\n      return;\n    }\n    var curName = activeItem.name;\n    dataCenter.values.rename(\n      { name: curName, newName: name },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          modal.rename(curName, name);\n          target.value = '';\n          target.blur();\n          !isGroup && self.setValuesActive(name);\n          events.trigger('valuesNameChanged', [curName, name]);\n          self.setState({ activeValues: modal.getActive() });\n          self.triggerValuesChange('rename');\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n  },\n  getActiveRuleName: function() {\n    var modal = this.state.rules;\n    var activeItem = modal.getActive();\n    return activeItem ? activeItem.name : '';\n  },\n  showAnonymousWeinre: function () {\n    this.openWeinre();\n  },\n  showWeinre: function (options) {\n    this.openWeinre(options.name);\n  },\n  openWeinre: function (name) {\n    window.open('weinre/client/#' + (name || 'anonymous'));\n    this.setState({\n      showWeinreOptions: false\n    });\n  },\n  onClickRulesOption: function (item) {\n    item.selected ? this.unselectRules(item) : this.selectRules(item);\n  },\n  selectRules: function (item) {\n    if (util.isGroup(item.name)) {\n      return;\n    }\n    var self = this;\n    dataCenter.rules[item.isDefault ? 'enableDefault' : 'select'](\n      item,\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.reselectRules(data);\n          self.state.rules.setChanged(item.name, false);\n          self.setState({});\n          self.triggerRulesChange('save');\n          if (data.changed) {\n            events.trigger('rulesChanged');\n          }\n          if (self.state.disabledAllRules) {\n            win.confirm(\n              'Rules are currently disabled. Enable them now?',\n              function (sure) {\n                if (sure) {\n                  dataCenter.rules.disableAllRules(\n                    { disabledAllRules: 0 },\n                    function (data, xhr) {\n                      if (data && data.ec === 0) {\n                        self.state.disabledAllRules = false;\n                        self.setState({});\n                      } else {\n                        util.showSystemError(xhr);\n                      }\n                    }\n                  );\n                }\n              }\n            );\n          }\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n    return false;\n  },\n  selectRulesByOptions: function (e) {\n    var item = this.state.rules.data[$(e.target).attr('data-name')];\n    this[e.target.checked ? 'selectRules' : 'unselectRules'](item);\n  },\n  unselectRules: function (item) {\n    var self = this;\n    dataCenter.rules[item.isDefault ? 'disableDefault' : 'unselect'](\n      item,\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.reselectRules(data);\n          self.triggerRulesChange('unselect');\n          self.setState({});\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n    return false;\n  },\n  reselectRules: function (data, autoUpdate) {\n    var self = this;\n    self.state.rules.clearAllSelected();\n    self.setSelected(\n      self.state.rules,\n      'Default',\n      !data.defaultRulesIsDisabled,\n      autoUpdate\n    );\n    data.list.forEach(function (name) {\n      self.setSelected(self.state.rules, name, true, autoUpdate);\n    });\n  },\n  saveValues: function (item) {\n    if (!item.changed || util.isGroup(item.name)) {\n      return;\n    }\n    var self = this;\n    dataCenter.values.add(item, function (data, xhr) {\n      if (data && data.ec === 0) {\n        self.setSelected(self.state.values, item.name);\n        self.triggerValuesChange('save');\n      } else {\n        util.showSystemError(xhr);\n      }\n    });\n    return false;\n  },\n  setSelected: function (modal, name, selected, autoUpdate) {\n    if (modal.setSelected(name, selected)) {\n      if (!autoUpdate) {\n        modal.setChanged(name, false);\n      }\n      this.setState({\n        curSelectedName: name\n      });\n    }\n  },\n  replayCountChange: function (e) {\n    var count = e.target.value.replace(/^\\s*0*|[^\\d]+/, '');\n    var replayCount = count.slice(0, 3);\n    if (replayCount > MAX_REPLAY_COUNT) {\n      replayCount = MAX_REPLAY_COUNT;\n    }\n    this.setState({ replayCount: replayCount });\n  },\n  clickReplay: function (e) {\n    if (e.shiftKey) {\n      events.trigger('replaySessions', [null, e.shiftKey]);\n    } else {\n      this.replay(e);\n    }\n  },\n  replay: function (e, list, count) {\n    var modal = this.state.network;\n    list = Array.isArray(list) ? list : modal.getSelectedList();\n    if (!list || !list.length) {\n      return;\n    }\n    this.enableRecord();\n    var replayReq = function (item, repeatCount) {\n      var req = item.req;\n      dataCenter.compose({\n        repeatCount: repeatCount,\n        useH2: item.useH2 ? 1 : '',\n        url: item.url,\n        headers: util.getOriginalReqHeaders(item),\n        method: req.method,\n        base64: req.base64\n      });\n    };\n    var map;\n    if (count > 1) {\n      replayReq(list[0], Math.min(count, MAX_REPLAY_COUNT));\n    } else {\n      map = {};\n      list.slice(0, MAX_REPLAY_COUNT).forEach(function (item) {\n        map[item.id] = 1;\n        replayReq(item);\n      });\n    }\n    if (modal.isTreeView) {\n      var dataId = dataCenter.lastSelectedDataId;\n      if (!dataId) {\n        return;\n      }\n      if (!map) {\n        return events.trigger('replayTreeView', [dataId, count]);\n      }\n      var node = dataId && modal.getTreeNode(dataId);\n      node = node && node.parent;\n      if (!node) {\n        return;\n      }\n      count = 0;\n      node.children.forEach(function (item) {\n        item = item.data;\n        if (item && map[item.id]) {\n          ++count;\n        }\n      });\n      events.trigger('replayTreeView', [dataId, count]);\n    } else if (this.autoRefresh) {\n      this.autoRefresh();\n    }\n  },\n  enableRecord: function () {\n    this.refs.recordBtn.enable();\n    events.trigger('changeRecordState');\n  },\n  composer: function () {\n    events.trigger('composer');\n  },\n  clear: function () {\n    var modal = this.state.network;\n    this.setState({\n      network: modal.clear()\n    });\n  },\n  removeRulesBatch: function(list) {\n    var self = this;\n    dataCenter.rules.remove({ list: list }, function (data, xhr) {\n      if (data && data.ec === 0) {\n        var nextItem;\n        var modal = self.state.rules;\n        list.forEach(function(name) {\n          var item = modal.data[name] || '';\n          if (item.active) {\n            nextItem = modal.getSibling(name);\n            nextItem && self.setRulesActive(nextItem.name);\n          }\n          modal.remove(name);\n        });\n        nextItem && events.trigger('expandRulesGroup', nextItem.name);\n        self.setState(nextItem ? { activeRules: nextItem } : {});\n        self.triggerRulesChange('remove');\n        events.trigger('focusRulesList');\n      } else {\n        util.showSystemError(xhr);\n      }\n    });\n    this.refs.deleteRulesDialog.hide();\n  },\n  removeValuesBatch: function(list) {\n    var self = this;\n    dataCenter.values.remove({ list: list }, function (data, xhr) {\n      if (data && data.ec === 0) {\n        var nextItem;\n        var modal = self.state.values;\n        list.forEach(function(name) {\n          var item = modal.data[name] || '';\n          if (item.active) {\n            nextItem = modal.getSibling(name);\n            nextItem && self.setValuesActive(nextItem.name);\n          }\n          modal.remove(name);\n        });\n        nextItem && events.trigger('expandValuesGroup', nextItem.name);\n        self.setState(nextItem ? { activeValues: nextItem } : {});\n        self.triggerValuesChange('remove');\n        events.trigger('focusValuesList');\n      } else {\n        util.showSystemError(xhr);\n      }\n    });\n    this.refs.deleteValuesDialog.hide();\n  },\n  removeRules: function (item) {\n    var modal = this.state.rules;\n    var activeItem = item || modal.getActive();\n    if (activeItem && !activeItem.isDefault) {\n      this.refs.deleteRulesDialog.show(activeItem.name);\n    }\n  },\n  removeValues: function (item) {\n    var modal = this.state.values;\n    var activeItem = item || modal.getActive();\n    if (activeItem && !activeItem.isDefault) {\n      this.refs.deleteValuesDialog.show(activeItem.name);\n    }\n  },\n  setRulesActive: function (name, modal) {\n    modal = modal || this.state.rules;\n    storage.set('activeRules', name);\n    modal.setActive(name);\n  },\n  setValuesActive: function (name, modal) {\n    modal = modal || this.state.values;\n    storage.set('activeValues', name);\n    modal.setActive(name);\n  },\n  showRulesSettings: function () {\n    $(findDOMNode(this.refs.rulesSettingsDialog)).modal('show');\n  },\n  showValuesSettings: function () {\n    $(findDOMNode(this.refs.valuesSettingsDialog)).modal('show');\n  },\n  toggleLeftMenu: function () {\n    var showLeftMenu = !this.state.showLeftMenu;\n    this.setState({\n      showLeftMenu: showLeftMenu\n    });\n    storage.set('showLeftMenu', showLeftMenu ? 1 : '');\n    events.trigger('editorResize');\n  },\n  handleCreate: function () {\n    this.state.name == 'rules'\n      ? this.showCreateRules()\n      : this.showCreateValues();\n  },\n  saveRulesOrValues: function () {\n    var self = this;\n    var state = self.state;\n    var list;\n    var isRules = state.name == 'rules';\n    if (isRules) {\n      list = state.rules.getChangedList();\n      var active = state.rules.getActive();\n      if (active && !active.selected && list.indexOf(active) === -1) {\n        list.push(active);\n      }\n      if (list.length) {\n        list.forEach(function (item) {\n          self.selectRules(item);\n        });\n        self.setState({});\n      }\n    } else {\n      list = state.values.getChangedList();\n      if (list.length) {\n        list.forEach(function (item) {\n          self.saveValues(item);\n        });\n        self.setState({});\n      }\n    }\n  },\n  onClickMenu: function (e) {\n    var target = $(e.target).closest('a');\n    var self = this;\n    var state = self.state;\n    var isRules = state.name == 'rules';\n    if (target.hasClass('w-edit-menu')) {\n      isRules ? self.showEditRules() : self.showEditValues();\n    } else if (target.hasClass('w-delete-menu')) {\n      isRules ? self.removeRules() : self.removeValues();\n    } else if (target.hasClass('w-save-menu')) {\n      self.saveRulesOrValues();\n    }\n  },\n  showSettings: function () {\n    var pageName = this.state.name;\n    if (pageName === 'rules') {\n      this.showRulesSettings();\n      return;\n    }\n    if (pageName === 'values') {\n      this.showValuesSettings();\n      return;\n    }\n    if (pageName === 'network') {\n      this.refs.networkSettings.showDialog();\n    }\n  },\n  activeRules: function (item) {\n    storage.set('activeRules', item.name);\n    this.setState({ activeRules: item });\n  },\n  activeValues: function (item) {\n    storage.set('activeValues', item.name);\n    this.setState({ activeValues: item });\n  },\n  onRulesThemeChange: function (e) {\n    var theme = e.target.value;\n    storage.set('rulesTheme', theme);\n    this.setState({\n      rulesTheme: theme\n    });\n  },\n  onValuesThemeChange: function (e) {\n    var theme = e.target.value;\n    storage.set('valuesTheme', theme);\n    this.setState({\n      valuesTheme: theme\n    });\n  },\n  onRulesFontSizeChange: function (e) {\n    var fontSize = e.target.value;\n    storage.set('rulesFontSize', fontSize);\n    this.setState({\n      rulesFontSize: fontSize\n    });\n  },\n  onValuesFontSizeChange: function (e) {\n    var fontSize = e.target.value;\n    storage.set('valuesFontSize', fontSize);\n    this.setState({\n      valuesFontSize: fontSize\n    });\n  },\n  onRulesLineNumberChange: function (e) {\n    var checked = e.target.checked;\n    storage.set('showRulesLineNumbers', checked);\n    this.setState({\n      showRulesLineNumbers: checked\n    });\n  },\n  onValuesLineNumberChange: function (e) {\n    var checked = e.target.checked;\n    storage.set('showValuesLineNumbers', checked);\n    this.setState({\n      showValuesLineNumbers: checked\n    });\n  },\n  showFoldGutter: function (e) {\n    var checked = e.target.checked;\n    storage.set('foldGutter', checked ? '1' : '');\n    this.setState({ foldGutter: checked });\n  },\n  onRulesLineWrappingChange: function (e) {\n    var checked = e.target.checked;\n    storage.set('autoRulesLineWrapping', checked ? 1 : '');\n    this.setState({\n      autoRulesLineWrapping: checked\n    });\n  },\n  onValuesLineWrappingChange: function (e) {\n    var checked = e.target.checked;\n    storage.set('autoValuesLineWrapping', checked ? 1 : '');\n    this.setState({\n      autoValuesLineWrapping: checked\n    });\n  },\n  confirmDisableAllRules: function (e) {\n    var self = this;\n    var state = self.state;\n    var dialog;\n    if (state.disabledAllRules || (!e && (dialog = $('.w-win-dialog[data-confirm-flag=rules]')).is(':visible'))) {\n      self.disableAllRules();\n      dialog && dialog.modal('hide');\n    } else {\n      win.confirm('Do you confirm disabling all rules?', function (sure) {\n        sure && self.disableAllRules();\n      }, false, 'rules');\n    }\n    e && e.preventDefault();\n  },\n  confirmDisableAllPlugins: function (e) {\n    var self = this;\n    var state = self.state;\n    var dialog;\n    if (state.disabledAllPlugins || (!e && (dialog = $('.w-win-dialog[data-confirm-flag=plugins]')).is(':visible'))) {\n      self.disableAllPlugins();\n      dialog && dialog.modal('hide');\n    } else {\n      win.confirm('Do you confirm disabling all plugins?', function (sure) {\n        sure && self.disableAllPlugins();\n      }, false, 'plugins');\n    }\n    e && e.preventDefault();\n  },\n  disableAllRules: function (e, callback) {\n    var self = this;\n    var state = self.state;\n    var checked = !state.disabledAllRules;\n    dataCenter.rules.disableAllRules(\n      { disabledAllRules: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          state.disabledAllRules = checked;\n          self.setState({});\n          if (typeof callback === 'function') {\n            callback(checked);\n          }\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n    e && e.preventDefault();\n  },\n  disableAllPlugins: function (e, callback) {\n    var self = this;\n    var state = self.state;\n    var checked = !state.disabledAllPlugins;\n    dataCenter.plugins.disableAllPlugins(\n      { disabledAllPlugins: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          state.disabledAllPlugins = checked;\n          protocols.setPlugins(state);\n          self.setState({});\n          if (typeof callback === 'function') {\n            callback(checked);\n          }\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n    e && e.preventDefault();\n  },\n  setPluginState: function(name, disabled) {\n    var self = this;\n    if (self.state.ndp) {\n      return message.info('Plugin disabling is restricted');\n    }\n    dataCenter.plugins.disablePlugin(\n      {\n        name: name,\n        disabled: disabled ? 1 : 0\n      },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.state.disabledPlugins = data.data;\n          dataCenter.setDisabledPlugins(data.data);\n          protocols.setPlugins(self.state);\n          self.setState({});\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n  },\n  disablePlugin: function (e) {\n    var target = e.target;\n    this.setPluginState($(target).attr('data-name'), !target.checked);\n  },\n  abort: function (list) {\n    if (!Array.isArray(list)) {\n      var modal = this.state.network;\n      list = modal.getSelectedList();\n    }\n    if (list) {\n      list = list.map(function (item) {\n        if (util.canAbort(item)) {\n          return item.id;\n        }\n      });\n      if (list.length) {\n        dataCenter.abort({ list: list.join() });\n      }\n    }\n    this.hideAbortOptions();\n  },\n  allowMultipleChoice: function (e) {\n    this.setMultipleCohice(e.target.checked);\n  },\n  setMultipleCohice: function (checked) {\n    var self = this;\n    dataCenter.rules.allowMultipleChoice(\n      { allowMultipleChoice: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.setState({\n            allowMultipleChoice: checked\n          });\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n  },\n  enableBackRulesFirst: function (e) {\n    this.setBackRulesFirst(e.target.checked);\n  },\n  setBackRulesFirst: function (checked) {\n    var self = this;\n    dataCenter.rules.enableBackRulesFirst(\n      { backRulesFirst: checked ? 1 : 0 },\n      function (data, xhr) {\n        if (data && data.ec === 0) {\n          self.setState({\n            backRulesFirst: checked\n          });\n          dataCenter.backRulesFirst = checked;\n        } else {\n          util.showSystemError(xhr);\n        }\n      }\n    );\n  },\n  installPlugins: function () {\n    events.trigger('installPlugins');\n  },\n  chooseFileType: function (e) {\n    var value = e.target.value;\n    storage.set('exportFileType', value);\n    this.setState({\n      exportFileType: value\n    });\n  },\n  importHarSessions: function (result) {\n    if (!result || typeof result !== 'object') {\n      return;\n    }\n    var entries = result.log.entries;\n    var sessions = [];\n    entries.forEach(function (entry) {\n      entry = util.harToSession(entry);\n      entry && sessions.push(entry);\n    });\n    dataCenter.addNetworkList(sessions);\n  },\n  uploadSessionsForm: function (data) {\n    if (!(data instanceof FormData)) {\n      var form = new FormData();\n      form.append('importSessions', data);\n      data = form;\n    }\n    var file = data.get('importSessions');\n    if (!file || !/\\.(txt|json|saz|har)$/i.test(file.name)) {\n      return win.alert('Supported file formats: .txt, .json, .saz, .har');\n    }\n\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Maximum file size: 64MB');\n    }\n    var isText = /\\.(?:txt|json)$/i.test(file.name);\n    if (isText || /\\.har$/i.test(file.name)) {\n      var self = this;\n      util.readFileAsText(file, function (result) {\n        try {\n          result = JSON.parse(result);\n          if (isText) {\n            dataCenter.importAnySessions(result);\n          } else {\n            self.importHarSessions(result);\n          }\n        } catch (e) {\n          win.alert('Invalid JSON format');\n        }\n      });\n      return;\n    }\n    dataCenter.upload.importSessions(data, dataCenter.addNetworkList);\n  },\n  getExportSessions: function() {\n    var modal = this.state.network;\n    var sessions = this.currentFoucsItem;\n    this.currentFoucsItem = null;\n    if (!sessions || !$(findDOMNode(this.refs.chooseFileType)).is(':visible')) {\n      sessions = modal.getSelectedList();\n    }\n    return sessions;\n  },\n  exportSessions: function (type, name, sessions) {\n    sessions = sessions || this.getExportSessions();\n    if (!sessions || !sessions.length) {\n      return;\n    }\n    var form = findDOMNode(this.refs.exportSessionsForm);\n    findDOMNode(this.refs.exportFilename).value = name || '';\n    findDOMNode(this.refs.exportFileType).value = type;\n    if (type === 'har') {\n      sessions = {\n        log: {\n          version: '1.2',\n          creator: {\n            name: 'Whistle',\n            version: this.state.version,\n            comment: ''\n          },\n          browser: {\n            name: 'Whistle',\n            version: this.state.version\n          },\n          pages: [],\n          entries: sessions.map(util.toHar),\n          comment: ''\n        }\n      };\n    }\n    findDOMNode(this.refs.sessions).value = JSON.stringify(\n      sessions,\n      null,\n      '  '\n    );\n    form.submit();\n  },\n  hideChooseFileTypeDialog: function(failed) {\n    if (!failed) {\n      $(findDOMNode(this.refs.chooseFileType)).modal('hide');\n      findDOMNode(this.refs.sessionsName).value = '';\n    }\n  },\n  exportBySave: function (e) {\n    if (e && e.type !== 'click' && e.keyCode !== 13) {\n      return;\n    }\n    var input = findDOMNode(this.refs.sessionsName);\n    var name = input.value.trim();\n    input.value = '';\n    this.exportSessions(this.state.exportFileType, name);\n    this.hideChooseFileTypeDialog();\n  },\n  replayRepeat: function (e) {\n    if (e && e.type !== 'click' && e.keyCode !== 13) {\n      return;\n    }\n    this.refs.setReplayCount.hide();\n    this.replay('', this.replayList, this.state.replayCount);\n    events.trigger('focusNetworkList');\n  },\n  showAboutDialog: function (e) {\n    if (this.state.hasNewVersion) {\n      this.refs.aboutDialog.showAboutInfo();\n      e.preventDefault();\n    }\n  },\n  onTopContextMenu: function(e) {\n    if (this.getTabName() !== 'network' || $(e.target).closest('.w-menu-item').length) {\n      return;\n    }\n    e.preventDefault();\n    var data = util.getMenuPosition(e, 110, 100);\n    data.list = TOP_BAR_MENUS;\n    this.refs.topContextMenu.show(data);\n  },\n  onContextMenu: function (e) {\n    var count = 0;\n    var list = LEFT_BAR_MENUS;\n    if (list[2].hide) {\n      ++count;\n    }\n    if (list[3].hide) {\n      ++count;\n    }\n    if (list[4].hide) {\n      ++count;\n    }\n    if (count < 3) {\n      var data = util.getMenuPosition(e, 110, 100 - count * 30);\n      var state = this.state;\n      data.list = list;\n      list[2].checked = !!state.network.isTreeView;\n      list[3].checked = !state.disabledAllRules;\n      list[4].checked = !state.disabledAllPlugins;\n      var target = $(e.target);\n      list[0].hide = true;\n      list[1].hide = true;\n      if (target.closest('.w-network-menu').length) {\n        list[0].hide = false;\n      } else if (target.closest('.w-save-menu').length) {\n        list[1].hide = false;\n        if (target.closest('.w-rules-menu').length) {\n          list[1].disabled = !state.rules.hasChanged();\n        } else {\n          list[1].disabled = !state.values.hasChanged();\n        }\n      }\n      this.refs.contextMenu.show(data);\n    }\n    e.preventDefault();\n  },\n  onClickTopMenu: function(action) {\n    switch(action) {\n    case 'top':\n      if (this.container) {\n        this.container[0].scrollTop = 0;\n      }\n      break;\n    case 'selected':\n      events.trigger('ensureSelectedItemVisible');\n      break;\n    case 'bottom':\n      if (this.container) {\n        this.container[0].scrollTop = 10000000;\n      }\n      break;\n    }\n  },\n  onClickContextMenu: function (action) {\n    var self = this;\n    var state = self.state;\n    var list = LEFT_BAR_MENUS;\n    switch (action) {\n    case 'Tree View':\n      list[2].checked = !state.network.isTreeView;\n      setTimeout(self.toggleTreeView, 0);\n      break;\n    case 'Rules':\n      self.disableAllRules(null, function (disabled) {\n        list[3].checked = !disabled;\n        self.setState({});\n      });\n      break;\n    case 'Plugins':\n      self.disableAllPlugins(null, function (disabled) {\n        list[4].checked = !disabled;\n        self.setState({});\n      });\n      break;\n    case 'Clear':\n      self.clear();\n      return;\n    case 'Save':\n      self.saveRulesOrValues();\n      return;\n    }\n    this.refs.contextMenu.show({});\n  },\n  forceShowLeftMenu: function () {\n    var self = this;\n    clearTimeout(self.hideTimer);\n    clearTimeout(self.showTimer);\n    self.showTimer = setTimeout(function () {\n      self.setState({ forceShowLeftMenu: true });\n    }, 200);\n  },\n  forceHideLeftMenu: function () {\n    var self = this;\n    clearTimeout(self.hideTimer);\n    clearTimeout(self.showTimer);\n    self.hideTimer = setTimeout(function () {\n      self.setState({ forceShowLeftMenu: false });\n    }, 500);\n  },\n  updateMenuView: function(state) {\n    var opt = state.networkOptions[state.networkOptions.length - 1];\n    if (state.network.isTreeView) {\n      opt.icon = 'globe';\n      opt.name = 'Show List View';\n    } else {\n      opt.icon = 'tree-conifer';\n      opt.name = 'Show Tree View';\n    }\n    return state;\n  },\n  toggleTreeView: function () {\n    var self = this;\n    var modal = self.state.network;\n    modal.setTreeView(!modal.isTreeView);\n    self.updateMenuView(self.state);\n    self.setState({}, function () {\n      if (!modal.isTreeView) {\n        self.autoRefresh && self.autoRefresh();\n      }\n    });\n  },\n  toggleTreeViewByIcon: function () {\n    if (this.getTabName() == 'network') {\n      this.toggleTreeView();\n    }\n  },\n  download: function(data) {\n    if (!data || !(util.isString(data.content) ||\n      util.isString(data.value) || util.isString(data.base64))) {\n      return;\n    }\n    var base64 = util.getString(data.base64);\n    findDOMNode(this.refs.filename).value = util.getString(data.name);\n    findDOMNode(this.refs.dataType).value = base64 ? 'rawBase64' : '';\n    findDOMNode(this.refs.content).value = base64 || util.getString(data.value|| data.content);\n    findDOMNode(this.refs.downloadForm).submit();\n  },\n  getTabName: function () {\n    var state = this.state;\n    var rulesMode = state.rulesMode;\n    var pluginsMode = state.pluginsMode;\n    var name = state.name;\n    if (state.networkMode) {\n      name = 'network';\n    } else if (state.rulesOnlyMode) {\n      name = name === 'values' ? 'values' : 'rules';\n    } else if (rulesMode && pluginsMode) {\n      name = 'plugins';\n    } else if (rulesMode) {\n      name = name === 'network' ? 'rules' : name;\n    } else if (pluginsMode) {\n      name = name !== 'plugins' ? 'network' : name;\n    }\n    return name || 'network';\n  },\n  isHideRules: function() {\n    return this.state.networkMode || this.state.pluginsMode;\n  },\n  onClickHelpMenu: function(option, e) {\n    if (option.name === 'Update' && dataCenter.showLatestClientVersion()) {\n      e.preventDefault();\n    }\n  },\n  render: function () {\n    var state = this.state;\n    var networkMode = state.networkMode;\n    var rulesMode = state.rulesMode;\n    var rulesOnlyMode = state.rulesOnlyMode;\n    var pluginsMode = state.pluginsMode;\n    var multiEnv = dataCenter.isMultiEnv();\n    var name = this.getTabName();\n    var isAccount = name == 'account';\n    var isNetwork = name == 'network';\n    var isRules = name == 'rules';\n    var isValues = name == 'values';\n    var isPlugins = name == 'plugins';\n    var isEditor = isRules || isValues;\n    var editMenuStyle = getHideStyle(!isEditor);\n    var importMenuStyle = getHideStyle(isPlugins || isAccount);\n    var disabledEditBtn = true;\n    var disabledDeleteBtn = true;\n    var rulesTheme = state.rulesTheme || 'cobalt';\n    var valuesTheme = state.valuesTheme || 'cobalt';\n    var rulesFontSize = state.rulesFontSize || '14px';\n    var valuesFontSize = state.valuesFontSize || '14px';\n    var showRulesLineNumbers = state.showRulesLineNumbers || false;\n    var showValuesLineNumbers = state.showValuesLineNumbers || false;\n    var autoRulesLineWrapping = state.autoRulesLineWrapping;\n    var autoValuesLineWrapping = state.autoValuesLineWrapping;\n    var rulesOptions = state.rulesOptions;\n    var pluginsOptions = state.pluginsOptions;\n    var uncheckedRules = {};\n    var showNetworkOptions = state.showNetworkOptions;\n    var showRulesOptions = state.showRulesOptions;\n    var showValuesOptions = state.showValuesOptions;\n    var showPluginsOptions = state.showPluginsOptions;\n    var showWeinreOptions = state.showWeinreOptions;\n    var showHelpOptions = state.showHelpOptions;\n    var modal = state.network;\n    var isTreeView = modal.isTreeView;\n    var networkType = (isTreeView ? 'tree-conifer' : 'globe') + (state.record ? ' w-disabled' : '');\n    if (rulesOptions[0].name === DEFAULT) {\n      rulesOptions.forEach(function (item, i) {\n        item.icon = !i || !multiEnv ? 'checkbox' : 'edit';\n        if (!item.selected) {\n          uncheckedRules[item.name] = 1;\n        }\n      });\n    }\n\n    var i, data;\n    if (isRules) {\n      data = state.rules.data;\n      for (i in data) {\n        if (data[i].active) {\n          disabledEditBtn = disabledDeleteBtn = data[i].isDefault;\n          break;\n        }\n      }\n    } else if (isValues) {\n      data = state.values.data;\n      for (i in data) {\n        if (data[i].active) {\n          disabledEditBtn = disabledDeleteBtn = false;\n          break;\n        }\n      }\n    }\n    modal.rulesModal = state.rules;\n    state.rules.editorTheme = {\n      theme: rulesTheme,\n      fontSize: rulesFontSize,\n      lineNumbers: showRulesLineNumbers\n    };\n    var networkOptions = state.networkOptions;\n    var hasUnselected = modal.hasUnselected();\n    if (modal.hasSelected()) {\n      networkOptions.forEach(function (option) {\n        option.disabled = false;\n        if (option.id === 'removeUnselected') {\n          option.disabled = !hasUnselected;\n        }\n      });\n    } else {\n      networkOptions.forEach(function (option) {\n        if (OPTIONS_WITH_SELECTED.indexOf(option.id) !== -1) {\n          option.disabled = true;\n        } else if (option.id === 'removeUnselected') {\n          option.disabled = !hasUnselected;\n        }\n      });\n      networkOptions[0].disabled = !hasUnselected;\n    }\n    var mustHideLeftMenu = hideLeftMenu && !state.forceShowLeftMenu;\n    var pluginsOnlyMode = pluginsMode && rulesMode;\n    var showLeftMenu = (networkMode || state.showLeftMenu) && !pluginsOnlyMode;\n    var disabledAllPlugins = state.disabledAllPlugins;\n    var disabledAllRules = state.disabledAllRules;\n    var forceShowLeftMenu, forceHideLeftMenu;\n    var pluginsStyle = getHideStyle(rulesOnlyMode || pluginsOnlyMode || networkMode);\n    if (showLeftMenu && hideLeftMenu) {\n      forceShowLeftMenu = this.forceShowLeftMenu;\n      forceHideLeftMenu = this.forceHideLeftMenu;\n    }\n    LEFT_BAR_MENUS[2].hide = rulesMode;\n    LEFT_BAR_MENUS[3].hide = pluginsMode;\n    LEFT_BAR_MENUS[4].hide = rulesOnlyMode;\n\n    var caType = state.caType || 'crt';\n    var caHash = state.caHash;\n    var caUrl = 'cgi-bin/rootca';\n    var caShortUrl = 'http://rootca.pro/';\n\n    if (caType !== 'cer') {\n      caUrl += '?type=' + caType;\n      caShortUrl += caType;\n    }\n    var hideEditor = this.isHideRules();\n    var hideEditorStyle = getHideStyle(hideEditor);\n    var hideStyle = hideMenus ? ' hide' : '';\n    dataCenter.hideMockMenu = hideEditor;\n    this.hideNetwork = rulesMode;\n    this.hideRules = this.hideValues = hideEditor;\n    this.hidePlugins = pluginsStyle;\n\n    return (\n      <div\n        className={\n          'main v-box' + (showLeftMenu ? ' w-show-left-menu' : '')\n          + (isEditor && !rulesOnlyMode ? ' w-show-editor' : '') + (isRules ? ' w-show-rules' : '')\n          + (rulesOnlyMode || rulesMode ? ' w-show-rules-mode' : '')\n        }\n      >\n        <div className={'w-menu w-' + name + '-menu-list' + hideStyle + (showLeftMenu ? '' : ' w-top')} onContextMenu={this.onTopContextMenu}>\n          <a\n            onClick={this.toggleLeftMenu}\n            draggable=\"false\"\n            className=\"w-switch-layout\"\n            onMouseEnter={forceShowLeftMenu}\n            onMouseLeave={forceHideLeftMenu}\n            style={getHideStyle(networkMode || pluginsOnlyMode)}\n            title=\"Ctrl[Command] + M\"\n          >\n            <Icon\n              name={\n                'chevron-' +\n                (showLeftMenu ? (mustHideLeftMenu ? 'down' : 'up') : 'left')\n              }\n            />\n          </a>\n          <div\n            style={getHideStyle(rulesMode)}\n            onMouseEnter={this.showNetworkOptions}\n            onMouseLeave={this.hideNetworkOptions}\n            className={\n              'w-nav-menu w-menu-wrapper' +\n              (showNetworkOptions ? ' w-menu-wrapper-show' : '')\n            }\n          >\n            <a\n              onClick={this.showNetwork}\n              onDoubleClick={this.toggleTreeView}\n              className={\n                'w-network-menu' + (isNetwork ? ' w-menu-selected' : '')\n              }\n              title={\n                'Double-click to open' +\n                (isTreeView ? ' List View' : ' Tree View')\n              }\n              draggable=\"false\"\n            >\n              <Icon name={networkType} />\n              Network\n            </a>\n            <MenuItem\n              ref=\"networkMenuItem\"\n              options={state.networkOptions}\n              className=\"w-network-menu-item\"\n              onClickOption={this.handleNetwork}\n            />\n          </div>\n          <div\n            style={hideEditorStyle}\n            onMouseEnter={this.showRulesOptions}\n            onMouseLeave={this.hideRulesOptions}\n            className={\n              'w-nav-menu w-menu-wrapper' +\n              (showRulesOptions ? ' w-menu-wrapper-show' : '') +\n              (isRules ? ' w-menu-auto' : '')\n            }\n          >\n            <a\n              onClick={this.showRules}\n              className={\n                'w-rules-menu' + (isRules ? ' w-menu-selected' : '')\n              }\n              draggable=\"false\"\n            >\n              <Icon name=\"list\" className={disabledAllRules ? 'w-disabled' : ''} />\n              Rules\n            </a>\n            <MenuItem\n              ref=\"rulesMenuItem\"\n              name={isRules ? null : 'Open'}\n              options={rulesOptions}\n              checkedOptions={uncheckedRules}\n              disabled={disabledAllRules}\n              className=\"w-rules-menu-item\"\n              onClick={this.showRules}\n              onClickOption={this.showAndActiveRules}\n              onChange={this.selectRulesByOptions}\n            />\n          </div>\n          <div\n            style={hideEditorStyle}\n            onMouseEnter={this.showValuesOptions}\n            onMouseLeave={this.hideValuesOptions}\n            className={\n              'w-nav-menu w-menu-wrapper' +\n              (showValuesOptions ? ' w-menu-wrapper-show' : '') +\n              (isValues ? ' w-menu-auto' : '')\n            }\n          >\n            <a\n              onClick={this.showValues}\n              className={\n                'w-values-menu' + (isValues ? ' w-menu-selected' : '')\n              }\n              draggable=\"false\"\n            >\n              <Icon name=\"folder-close\" />Values\n            </a>\n            <MenuItem\n              ref=\"valuesMenuItem\"\n              name={isValues ? null : 'Open'}\n              options={state.valuesOptions}\n              className=\"w-values-menu-item\"\n              onClick={this.showValues}\n              onClickOption={this.showAndActiveValues}\n            />\n          </div>\n          <div\n            style={pluginsStyle}\n            ref=\"pluginsMenu\"\n            onMouseEnter={this.showPluginsOptions}\n            onMouseLeave={this.hidePluginsOptions}\n            className={\n              'w-nav-menu w-menu-wrapper' +\n              (showPluginsOptions ? ' w-menu-wrapper-show' : '')\n            }\n          >\n            <a\n              onClick={this.showPlugins}\n              className={\n                'w-plugins-menu' + (isPlugins ? ' w-menu-selected' : '')\n              }\n              draggable=\"false\"\n            >\n              <Icon name=\"th-large\" className={disabledAllPlugins ? 'w-disabled' : ''} />\n              Plugins\n            </a>\n            <MenuItem\n              ref=\"pluginsMenuItem\"\n              name={isPlugins ? null : 'Open'}\n              options={pluginsOptions}\n              checkedOptions={state.disabledPlugins}\n              disabled={disabledAllPlugins}\n              className=\"w-plugins-menu-item\"\n              onClick={this.showPlugins}\n              onChange={this.disablePlugin}\n              onClickOption={this.showAndActivePlugins}\n            />\n          </div>\n          {!state.ndr && (\n            <a\n              onClick={this.confirmDisableAllRules}\n              className=\"w-enable-rules-menu w-switch-btn\"\n              title={\n                disabledAllRules ? 'Enable all rules' : 'Disable all rules'\n              }\n              style={getHideStyle(!isRules)}\n              draggable=\"false\"\n            >\n              <Icon name=\"stop\" className={disabledAllRules ? 'w-pause' : ''} />\n              ON\n            </a>\n          )}\n          {!state.ndp && (\n            <a\n              onClick={this.confirmDisableAllPlugins}\n              className=\"w-enable-plugin-menu w-switch-btn\"\n              title={\n                disabledAllPlugins\n                  ? 'Enable all plugins'\n                  : 'Disable all plugins'\n              }\n              style={getHideStyle(!isPlugins)}\n              draggable=\"false\"\n            >\n              <Icon name=\"stop\" className={disabledAllPlugins ? 'w-pause' : ''} />\n              ON\n            </a>\n          )}\n          <UpdateAllBtn hide={!isPlugins} />\n          <a\n            onClick={this.installPlugins}\n            className=\"w-plugins-menu\"\n            style={getHideStyle(!isPlugins)}\n            draggable=\"false\"\n          >\n            <Icon name=\"download-alt\" />\n            Install\n          </a>\n          <RecordBtn\n            ref=\"recordBtn\"\n            hide={!isNetwork}\n            onClick={this.handleAction}\n          />\n          <a\n            onClick={this.importData}\n            style={importMenuStyle}\n            className=\"w-import-menu\"\n            draggable=\"false\"\n          >\n            <Icon name=\"import\" />Import\n          </a>\n          <a\n            onClick={this.exportData}\n            className=\"w-export-menu\"\n            style={importMenuStyle}\n            draggable=\"false\"\n          >\n            <Icon name=\"export\" />Export\n          </a>\n          <a\n            onClick={this.clear}\n            style={getHideStyle(!isNetwork)}\n            className=\"w-remove-menu w-remove-menu-list\"\n            title=\"Ctrl[Command] + X\"\n            draggable=\"false\"\n          >\n            <Icon name=\"remove\" />Clear\n          </a>\n          <a\n            onClick={this.onClickMenu}\n            className=\"w-save-menu\"\n            style={editMenuStyle}\n            draggable=\"false\"\n            title=\"Ctrl[Command] + S\"\n          >\n            <Icon name=\"save-file\" />Save\n          </a>\n          <a\n            className=\"w-create-menu\"\n            style={editMenuStyle}\n            draggable=\"false\"\n            onClick={this.handleCreate}\n          >\n            <Icon name=\"plus\" />Create\n          </a>\n          <a\n            onClick={this.onClickMenu}\n            className={'w-edit-menu' + (disabledEditBtn ? ' w-disabled' : '')}\n            style={editMenuStyle}\n            draggable=\"false\"\n          >\n            <Icon name=\"transfer\" />Rename\n          </a>\n          <div\n            onMouseEnter={this.showAbortOptions}\n            onMouseLeave={this.hideAbortOptions}\n            style={getHideStyle(!isNetwork)}\n            className={\n              'w-menu-wrapper w-abort-menu-list w-menu-auto' +\n              (state.showAbortOptions ? ' w-menu-wrapper-show' : '')\n            }\n          >\n            <a\n              onClick={this.clickReplay}\n              className=\"w-replay-menu\"\n              draggable=\"false\"\n            >\n              <Icon name=\"repeat\" />Replay\n            </a>\n            <MenuItem\n              options={ABORT_OPTIONS}\n              className=\"w-remove-menu-item\"\n              onClickOption={this.abort}\n            />\n          </div>\n          <a\n            onClick={this.composer}\n            className=\"w-com-menu\"\n            style={getHideStyle(!isNetwork)}\n            draggable=\"false\"\n          >\n            <Icon name=\"send\" />Edit\n          </a>\n          <a\n            onClick={this.onClickMenu}\n            className={\n              'w-delete-menu' + (disabledDeleteBtn ? ' w-disabled' : '')\n            }\n            style={editMenuStyle}\n            draggable=\"false\"\n          >\n            <Icon name=\"trash\" />Delete\n          </a>\n          <FilterBtn\n            onClick={this.showSettings}\n            disabledRules={isRules && disabledAllRules}\n            backRulesFirst={isRules && state.backRulesFirst}\n            isNetwork={isNetwork}\n            hide={isPlugins}\n          />\n          <ServiceBtn name={name} />\n          <div\n            onMouseEnter={this.showWeinreOptions}\n            onMouseLeave={this.hideWeinreOptions}\n            className={\n              'w-menu-wrapper' +\n              (showWeinreOptions ? ' w-menu-wrapper-show' : '')\n            }\n          >\n            <a\n              onClick={this.showWeinreOptionsQuick}\n              onDoubleClick={this.showAnonymousWeinre}\n              className=\"w-weinre-menu\"\n              draggable=\"false\"\n            >\n              <Icon name=\"console\" />\n              <span className=\"w-weinre-name\">Weinre</span>\n            </a>\n            <MenuItem\n              ref=\"weinreMenuItem\"\n              name=\"anonymous\"\n              icon=\"console\"\n              options={state.weinreOptions}\n              className=\"w-weinre-menu-item\"\n              onClick={this.showAnonymousWeinre}\n              onClickOption={this.showWeinre}\n            />\n          </div>\n          <a\n            onClick={this.showHttpsSettingsDialog}\n            className=\"w-https-menu\"\n            draggable=\"false\"\n            style={{ color: dataCenter.hasInvalidCerts ? 'var(--c-error)' : undefined }}\n          >\n            <Icon name={state.interceptHttpsConnects ? 'ok-circle' : 'lock'} />\n            <span className=\"w-https-name\">HTTPS</span>\n          </a>\n          <div\n            onMouseEnter={this.showHelpOptions}\n            onMouseLeave={this.hideHelpOptions}\n            className={\n              'w-menu-wrapper' + (showHelpOptions ? ' w-menu-wrapper-show' : '')\n            }\n          >\n            <a\n              onClick={this.showAboutDialog}\n              title={\n                state.hasNewVersion\n                  ? 'A new version is available, click to see details'\n                  : undefined\n              }\n              href=\"https://github.com/avwo/whistle#whistle\"\n              target=\"_blank\"\n            >\n              {state.hasNewVersion ? <i className=\"w-new-version-icon\" /> : null}\n              <Icon name=\"question-sign\" />\n              <span className=\"w-help-name\">Help</span>\n            </a>\n            <MenuItem\n              ref=\"helpMenuItem\"\n              options={state.helpOptions}\n              onClickOption={this.onClickHelpMenu}\n              name={\n                <About\n                  ref=\"aboutDialog\"\n                  clientVersion={clientVersion}\n                  onClick={this.hideHelpOptions}\n                  onCheckUpdate={this.showHasNewVersion}\n                />\n              }\n              className=\"w-help-menu-item\"\n            />\n          </div>\n          <Online name={name} clientVersion={clientVersion} />\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: state.showCreateRules ? 'block' : 'none' }}\n            className=\"w-shadow w-input-menu-item w-create-rules-input\"\n          >\n            <input\n              ref=\"createRulesInput\"\n              onKeyDown={this.createRules}\n              onBlur={this.hideRulesInput}\n              type=\"text\"\n              maxLength=\"64\"\n              placeholder=\"Enter name\"\n            />\n            <button\n              type=\"button\"\n              onClick={this.createRules}\n              className=\"btn btn-primary\"\n            >\n              +Rule\n            </button>\n            <button\n              type=\"button\"\n              onClick={this.createRules}\n              data-type=\"top\"\n              className=\"btn btn-default\"\n            >\n              +Top\n            </button>\n            <button\n              type=\"button\"\n              onClick={this.createRules}\n              data-type=\"group\"\n              className=\"btn btn-default\"\n            >\n              +Group\n            </button>\n          </div>\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: state.showCreateValues ? 'block' : 'none' }}\n            className=\"w-shadow w-input-menu-item w-create-values-input\"\n          >\n            <input\n              ref=\"createValuesInput\"\n              onKeyDown={this.createValues}\n              onBlur={this.hideValuesInput}\n              type=\"text\"\n              maxLength=\"64\"\n              placeholder=\"Enter name\"\n            />\n            <button\n              type=\"button\"\n              onClick={this.createValues}\n              className=\"btn btn-primary\"\n            >\n              +Key\n            </button>\n            <button\n              type=\"button\"\n              onClick={this.createValues}\n              data-type=\"group\"\n              className=\"btn btn-default\"\n            >\n              +Group\n            </button>\n          </div>\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: state.showEditRules ? 'block' : 'none' }}\n            className=\"w-shadow w-input-menu-item w-edit-rules-input\"\n          >\n            <input\n              ref=\"editRulesInput\"\n              onKeyDown={this.editRules}\n              onBlur={this.hideRenameRuleInput}\n              type=\"text\"\n              maxLength=\"64\"\n            />\n            <button\n              type=\"button\"\n              onClick={this.editRules}\n              className=\"btn btn-primary\"\n            >\n              OK\n            </button>\n          </div>\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: state.showEditValues ? 'block' : 'none' }}\n            className=\"w-shadow w-input-menu-item w-edit-values-input\"\n          >\n            <input\n              ref=\"editValuesInput\"\n              onKeyDown={this.editValues}\n              onBlur={this.hideRenameValueInput}\n              type=\"text\"\n              maxLength=\"64\"\n            />\n            <button\n              type=\"button\"\n              onClick={this.editValues}\n              className=\"btn btn-primary\"\n            >\n              OK\n            </button>\n          </div>\n        </div>\n        <div className=\"w-container box fill\">\n          <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n          <ContextMenu onClick={this.onClickTopMenu} ref=\"topContextMenu\" />\n          <div\n            onContextMenu={this.onContextMenu}\n            onDoubleClick={this.onContextMenu}\n            className={\n              'w-left-menu' + (forceShowLeftMenu ? ' w-hover-left-menu' : '') + hideStyle\n            }\n            style={getHideStyle(networkMode || mustHideLeftMenu)}\n            onMouseEnter={forceShowLeftMenu}\n            onMouseLeave={forceHideLeftMenu}\n          >\n            <a\n              onClick={this.showNetwork}\n              className={\n                'w-network-menu' + (isNetwork ? ' w-menu-selected' : '')\n              }\n              style={getHideStyle(rulesMode)}\n              draggable=\"false\"\n            >\n              <Icon name={networkType} />\n              <i className=\"w-left-menu-name\">Network</i>\n            </a>\n            <a\n              onClick={this.showRules}\n              className={\n                'w-save-menu w-rules-menu' +\n                (isRules ? ' w-menu-selected' : '')\n              }\n              style={hideEditorStyle}\n              draggable=\"false\"\n            >\n              <Icon name=\"list\" className={disabledAllRules ? 'w-disabled' : ''} />\n              <i className=\"w-left-menu-name\">Rules</i>\n              <i\n                className=\"w-menu-changed\"\n                style={getHideStyle(!state.rules.hasChanged())}\n              >\n                *\n              </i>\n            </a>\n            <a\n              onClick={this.showValues}\n              className={\n                'w-save-menu w-values-menu' +\n                (isValues ? ' w-menu-selected' : '')\n              }\n              style={hideEditorStyle}\n              draggable=\"false\"\n            >\n              <Icon name=\"folder-close\" />\n              <i className=\"w-left-menu-name\">Values</i>\n              <i\n                className=\"w-menu-changed\"\n                style={getHideStyle(!state.values.hasChanged())}\n              >\n                *\n              </i>\n            </a>\n            <a\n              onClick={this.showPlugins}\n              className={\n                'w-plugins-menu' + (isPlugins ? ' w-menu-selected' : '')\n              }\n              style={pluginsStyle}\n              draggable=\"false\"\n            >\n              <Icon name=\"th-large\" className={disabledAllPlugins ? 'w-disabled' : ''} />\n              <i className=\"w-left-menu-name\">Plugins</i>\n            </a>\n          </div>\n          {state.hasRules ? (\n            <List\n              ref=\"rules\"\n              disabled={disabledAllRules}\n              theme={rulesTheme}\n              lineWrapping={autoRulesLineWrapping}\n              fontSize={rulesFontSize}\n              lineNumbers={showRulesLineNumbers}\n              onSelect={this.selectRules}\n              onUnselect={this.unselectRules}\n              onActive={this.activeRules}\n              modal={state.rules}\n              hide={!isRules}\n              name=\"rules\"\n            />\n          ) : undefined}\n          {state.hasValues ? (\n            <List\n              theme={valuesTheme}\n              onDoubleClick={this.showEditValuesByDBClick}\n              fontSize={valuesFontSize}\n              lineWrapping={autoValuesLineWrapping}\n              lineNumbers={showValuesLineNumbers}\n              onSelect={this.saveValues}\n              onActive={this.activeValues}\n              modal={state.values}\n              hide={!isValues}\n              className=\"w-values-list\"\n              foldGutter={state.foldGutter}\n            />\n          ) : undefined}\n          {state.hasNetwork ? (\n            <Network\n              ref=\"network\"\n              hide={!isNetwork}\n              modal={modal}\n              rulesModal={state.rules}\n            />\n          ) : undefined}\n          {state.hasPlugins ? (\n            <Plugins\n              {...state}\n              onOpen={this.activePluginTab}\n              onClose={this.closePluginTab}\n              onActive={this.activePluginTab}\n              onChange={this.disablePlugin}\n              ref=\"plugins\"\n              hide={!isPlugins}\n            />\n          ) : undefined}\n        </div>\n        <div\n          ref=\"rulesSettingsDialog\"\n          className=\"modal fade w-rules-settings-dialog\"\n        >\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <CloseBtn />\n                <EditorSettings\n                  name=\"rules\"\n                  theme={rulesTheme}\n                  fontSize={rulesFontSize}\n                  lineNumbers={showRulesLineNumbers}\n                  lineWrapping={autoRulesLineWrapping}\n                  onLineWrappingChange={this.onRulesLineWrappingChange}\n                  onThemeChange={this.onRulesThemeChange}\n                  onFontSizeChange={this.onRulesFontSizeChange}\n                  onLineNumberChange={this.onRulesLineNumberChange}\n                />\n                {!state.drm && (\n                  <p className=\"w-editor-settings-box\">\n                    <label className=\"w-align-items\" style={{ color: multiEnv ? 'var(--c-disabled)' : undefined }}>\n                      <input\n                        type=\"checkbox\"\n                        disabled={multiEnv}\n                        checked={!multiEnv && state.allowMultipleChoice}\n                        onChange={this.allowMultipleChoice}\n                      />{' '}\n                      Use multiple rules\n                    </label>\n                  </p>\n                )}\n                {!state.drb && (\n                  <p className=\"w-editor-settings-box\">\n                    <label className=\"w-align-items\">\n                      <input\n                        type=\"checkbox\"\n                        checked={state.backRulesFirst}\n                        onChange={this.enableBackRulesFirst}\n                      />{' '}\n                     The later rules first\n                    </label>\n                  </p>\n                )}\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  data-dismiss=\"modal\"\n                >\n                  Close\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-primary\"\n                  onClick={this.importRulesSettings}\n                >\n                  Import\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-info\"\n                  onClick={this.exportRulesSettings}\n                >\n                  Export\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n        <div\n          ref=\"valuesSettingsDialog\"\n          className=\"modal fade w-values-settings-dialog\"\n        >\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <CloseBtn />\n                <EditorSettings\n                  theme={valuesTheme}\n                  fontSize={valuesFontSize}\n                  lineNumbers={showValuesLineNumbers}\n                  lineWrapping={autoValuesLineWrapping}\n                  onLineWrappingChange={this.onValuesLineWrappingChange}\n                  onThemeChange={this.onValuesThemeChange}\n                  onFontSizeChange={this.onValuesFontSizeChange}\n                  onLineNumberChange={this.onValuesLineNumberChange}\n                />\n                <p className=\"w-editor-settings-box\">\n                  <label className=\"w-align-items\">\n                    <input\n                      type=\"checkbox\"\n                      checked={state.foldGutter}\n                      onChange={this.showFoldGutter}\n                    />{' '}\n                    Show fold gutter\n                  </label>\n                </p>\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  data-dismiss=\"modal\"\n                >\n                  Close\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-primary\"\n                  onClick={this.importValuesSettings}\n                >\n                  Import\n                </button>\n                <button\n                  type=\"button\"\n                  className=\"btn btn-info\"\n                  onClick={this.exportValuesSettings}\n                >\n                  Export\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n        {rulesMode ? null : <NetworkSettings ref=\"networkSettings\" />}\n        <HttpsSettings\n          ref=\"httpsSettings\"\n          caHash={caHash}\n          port={state.port}\n          caUrlList={state.caUrlList}\n          multiEnv={multiEnv}\n          interceptHttpsConnects={state.interceptHttpsConnects}\n          enableHttp2={state.enableHttp2}\n          onEnableHttps={this.interceptHttpsConnects}\n          onEnableHttp2={this.enableHttp2}\n        />\n        <div ref=\"chooseFileType\" className=\"modal fade w-choose-filte-type\">\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <label className=\"w-choose-filte-type-label\">\n                  Save as:\n                  <input\n                    ref=\"sessionsName\"\n                    value={state.filename}\n                    onChange={this.filterFilename}\n                    onKeyDown={this.exportBySave}\n                    placeholder=\"Enter filename (optional)\"\n                    className=\"form-control\"\n                    maxLength=\"64\"\n                  />\n                  <select\n                    ref=\"fileType\"\n                    className=\"form-control\"\n                    value={state.exportFileType}\n                    onChange={this.chooseFileType}\n                  >\n                    <option value=\"whistle\">*.txt</option>\n                    <option value=\"Fiddler\">*.saz</option>\n                    <option value=\"har\">*.har</option>\n                  </select>\n                </label>\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  data-dismiss=\"modal\"\n                >\n                  Cancel\n                </button>\n                <SaveToServiceBtn type=\"network\" onComplete={this.hideChooseFileTypeDialog} getFilename={this.getInputValue} data={this.getExportSessions} />\n                <button\n                  type=\"button\"\n                  onKeyDown={this.exportBySave}\n                  tabIndex=\"0\"\n                  onMouseDown={this.preventBlur}\n                  className=\"btn btn-primary\"\n                  onClick={this.exportBySave}\n                >\n                  Export\n                </button>\n                </div>\n            </div>\n          </div>\n        </div>\n        <LargeDialog ref=\"editorWin\" className=\"w-editor-win\" />\n        <Dialog ref=\"setReplayCount\" wstyle=\"w-replay-count-dialog\">\n          <div className=\"modal-body\">\n            <label>\n              Times:\n              <input\n                ref=\"replayCount\"\n                placeholder={'<= ' + MAX_REPLAY_COUNT}\n                onKeyDown={this.replayRepeat}\n                onChange={this.replayCountChange}\n                value={state.replayCount}\n                className=\"form-control\"\n                maxLength=\"3\"\n              />\n            </label>\n            <button\n              type=\"button\"\n              onKeyDown={this.replayRepeat}\n              tabIndex=\"0\"\n              onMouseDown={this.preventBlur}\n              className=\"btn btn-primary\"\n              disabled={!state.replayCount}\n              onClick={this.replayRepeat}\n            >\n              Replay\n            </button>\n          </div>\n        </Dialog>\n        <div\n          ref=\"showUpdateTipsDialog\"\n          className=\"modal fade w-show-update-tips-dialog\"\n        >\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <CloseBtn />\n                <p className=\"w-show-update-tips\">\n                Whistle has critical updates available.\n                Update to the latest version immediately.\n                </p>\n                <p>Current version: {state.version}</p>\n                <p>Latest version: {state.latestVersion}</p>\n                <p>\n                  View change:{' '}\n                  <a\n                    title=\"Change log\"\n                    href=\"https://github.com/avwo/whistle/blob/master/CHANGELOG.md\"\n                    target=\"_blank\"\n                  >\n                    CHANGELOG.md\n                  </a>\n                </p>\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  onClick={this.donotShowAgain}\n                  data-dismiss=\"modal\"\n                >\n                  Don't Show Again\n                </button>\n                <a\n                  type=\"button\"\n                  className=\"btn btn-primary\"\n                  onClick={this.hideUpdateTipsDialog}\n                  href={util.UPDATE_URL}\n                  target=\"_blank\"\n                >\n                  View Update Guide\n                </a>\n              </div>\n            </div>\n          </div>\n        </div>\n        <Dialog ref=\"confirmReload\" wstyle=\"w-confirm-reload-dialog\">\n          <div className=\"modal-body w-confirm-reload\">\n            <CloseBtn />\n            <div className=\"w-reload-data-tips\"></div>\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              No\n            </button>\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={this.reloadData}\n              data-dismiss=\"modal\"\n            >\n              Yes\n            </button>\n          </div>\n        </Dialog>\n        <ListDialog\n          ref=\"deleteRulesDialog\"\n          title=\"Delete Rules\"\n          tips=\"Do you confirm the deletion of all follow rules or group?\"\n          onConfirm={this.removeRulesBatch}\n          name=\"rules\"\n          isRules=\"1\"\n          list={state.rules.list}\n        />\n        <ListDialog\n          ref=\"deleteValuesDialog\"\n          title=\"Delete Values\"\n          tips=\"Do you confirm the deletion of all follow values or group?\"\n          onConfirm={this.removeValuesBatch}\n          name=\"values\"\n          list={state.values.list}\n        />\n        <ListDialog\n          ref=\"selectRulesDialog\"\n          name=\"rules\"\n          modal={state.rules}\n          list={state.rules.list}\n        />\n        <ListDialog\n          ref=\"selectValuesDialog\"\n          title=\"Export Values\"\n          name=\"values\"\n          list={state.values.list}\n        />\n        <iframe name=\"downloadTargetFrame\" style={HIDE_STYLE} />\n        <form\n          ref=\"exportSessionsForm\"\n          action=\"cgi-bin/sessions/export\"\n          style={HIDE_STYLE}\n          method=\"post\"\n          target=\"downloadTargetFrame\"\n        >\n          <input ref=\"exportFilename\" name=\"exportFilename\" type=\"hidden\" />\n          <input ref=\"exportFileType\" name=\"exportFileType\" type=\"hidden\" />\n          <input ref=\"sessions\" name=\"sessions\" type=\"hidden\" />\n        </form>\n        <SyncDialog ref=\"syncDialog\" />\n        <JSONDialog ref=\"jsonDialog\" />\n        <div id=\"copyTextBtn\" style={HIDE_STYLE} />\n        <MockDialog ref=\"mockDialog\" />\n        <RulesDialog ref=\"rulesDialog\" />\n        <form\n          ref=\"downloadForm\"\n          action=\"cgi-bin/download\"\n          style={HIDE_STYLE}\n          method=\"post\"\n          target=\"downloadTargetFrame\"\n        >\n          <input ref=\"dataType\" name=\"type\" type=\"hidden\" />\n          <input ref=\"filename\" name=\"filename\" type=\"hidden\" />\n          <input ref=\"content\" name=\"content\" type=\"hidden\" />\n        </form>\n        <IframeDialog ref=\"iframeDialog\" />\n        <ImportDialog ref=\"importDialog\" />\n        <ExportDialog ref=\"exportDialog\" />\n        {/* 初始化 EditorDialog 给 Rules 里面的快捷键使用 */}\n        <EditorDialog textEditor standalone />\n        <ServiceDialog />\n      </div>\n    );\n  }\n});\ndataCenter.getInitialData(function (data) {\n  ReactDOM.render(<Index modal={data} />, document.getElementById('container'));\n});\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/inspector.js",
    "content": "var React = require('react');\nvar Divider = require('./divider');\nvar ReqDetail = require('./req-detail');\nvar ResDetail = require('./res-detail');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar Inspector = React.createClass({\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    if (hide != util.getBool(nextProps.hide)) {\n      return true;\n    }\n    if (hide) {\n      return false;\n    }\n    var modal = this.props.modal;\n    var newModal = nextProps.modal;\n    if (!modal || modal !== newModal) {\n      return true;\n    }\n    return !this.endTime || this.refs.divider._needReset;\n  },\n  render: function () {\n    var props = this.props;\n    var modal = props.modal;\n    this.endTime = modal && (modal.endTime || modal.lost);\n    return (\n      <Divider ref=\"divider\" vertical=\"true\" hide={props.hide}>\n        <div className=\"fill v-box\">\n          <ReqDetail modal={modal} />\n        </div>\n        <div className=\"fill v-box\">\n          <div className=\"w-detail-inspectors-title w-detail-inspectors-res\">\n            <Icon name=\"arrow-left\" />Response\n          </div>\n          <ResDetail modal={modal} />\n        </div>\n      </Divider>\n    );\n  }\n});\n\nmodule.exports = Inspector;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/inspectors.js",
    "content": "var React = require('react');\nvar util = require('./util');\nvar Inspector = require('./inspector');\nvar Frames = require('./frames');\nvar LazyInit = require('./lazy-init');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar TabMgr = require('./tab-mgr');\nvar ContextMenu = require('./context-menu');\nvar Properties = require('./properties');\nvar Icon = require('./icon');\n\nvar Inspectors = React.createClass({\n  getInitialState: function () {\n    return { activeName: 'Request', urlModal: { URL: '' } };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  componentDidMount: function () {\n    var self = this;\n    events.on('tabsChange', function () {\n      self.setState({});\n    });\n    events.on('showFrames', function () {\n      self.showTab('Frames');\n    });\n  },\n  showTab: function (name) {\n    if (this.state.activeName !== name) {\n      this.setState({ activeName: name });\n    }\n  },\n  isActive: function (name) {\n    return this.state.activeName === name;\n  },\n  getStyle: function (name) {\n    return 'btn btn-default' + (this.isActive(name) ? ' w-spec-active' : '');\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var modal = props.modal;\n    var urlModal = self.state.urlModal;\n    var hideFrames = !self.isActive('Frames');\n    var hide = util.getBool(props.hide);\n    var tabs = dataCenter.getTabs();\n    var active = this.state.activeName;\n    urlModal.URL = modal && ((modal.isHttps ? 'tunnel://' : '') + modal.url);\n\n    return (\n      <div\n        className={\n          'fill v-box w-detail-inspectors' + (hide ? ' hide' : '')\n        }\n      >\n        <Properties className=\"w-detail-inspectors-url\" modal={urlModal}  showEnableBtn={true} />\n        <div className=\"box w-detail-inspectors-title w-detail-inspectors-tabs\">\n          <button\n            type=\"button\"\n            onClick={function () {\n              self.showTab('Request');\n            }}\n            className={self.getStyle('Request')}\n          >\n            <Icon name=\"arrow-right\" />Request\n          </button>\n          <button\n            type=\"button\"\n            onClick={function () {\n              self.showTab('Frames');\n            }}\n            className={self.getStyle('Frames')}\n          >\n            <Icon name=\"menu-hamburger\" />Frames\n          </button>\n          <div className=\"fill w-custom-tabs\">\n            {tabs.map(function (tab) {\n              var pluginName = tab.plugin;\n              var icon = util.getTabIcon(tab);\n              return (\n                <button\n                  key={pluginName}\n                  onClick={function () {\n                    self.showTab(pluginName);\n                  }}\n                  className={'w-custom-tab-btn ' + self.getStyle(pluginName)}\n                  title={pluginName}\n                >\n                  {icon ? <img className=\"w-tab-icon\" src={icon} /> : null}\n                  {tab.name}\n                </button>\n              );\n            })}\n          </div>\n        </div>\n        <Inspector hide={!self.isActive('Request')} modal={modal} />\n        <LazyInit inited={!hideFrames}>\n          <Frames hide={hideFrames} data={modal} frames={props.frames} />\n        </LazyInit>\n        <TabMgr\n          active={active}\n          hide={hide}\n          tabs={tabs}\n          className=\"w-custom-tab-panel\"\n        />\n        <ContextMenu ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Inspectors;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/is-utf8.js",
    "content": "var MAX_LEN = 1024 * 32;\n\nfunction isUtf8(buf, i) {\n  i = i || 0;\n  for (var len = Math.min(buf.length, MAX_LEN); i < len; i++) {\n    var byte = buf[i];\n    if (\n      byte == 0x09 ||\n      byte == 0x0a ||\n      byte == 0x0d ||\n      (0x20 <= byte && byte <= 0x7f)\n    ) {\n      continue;\n    }\n    ++i;\n    var byte1 = buf[i];\n    if (0xc2 <= byte && byte <= 0xdf) {\n      if (0x80 <= byte1 && byte1 <= 0xbf) {\n        continue;\n      }\n      return !byte1;\n    }\n    ++i;\n    var byte2 = buf[i];\n    if (byte == 0xe0) {\n      if (0xa0 <= byte1 && byte1 <= 0xbf && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n\n    if ((0xe1 <= byte && byte <= 0xec) || byte == 0xee || byte == 0xef) {\n      if (0x80 <= byte1 && byte1 <= 0xbf && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n\n    if (byte == 0xed) {\n      if (0x80 <= byte1 && byte1 <= 0x9f && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n    ++i;\n    var byte3 = buf[i];\n    if (byte == 0xf0) {\n      if (\n        0x90 <= byte1 &&\n        byte1 <= 0xbf &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n    if (0xf1 <= byte && byte <= 0xf3) {\n      if (\n        0x80 <= byte1 &&\n        byte1 <= 0xbf &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n    if (byte == 0xf4) {\n      if (\n        0x80 <= byte1 &&\n        byte1 <= 0x8f &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n\n    return false;\n  }\n  return true;\n}\n\nmodule.exports = function (buf) {\n  if (isUtf8(buf)) {\n    return true;\n  }\n  return buf[0] === 0 && isUtf8(buf, 5);\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/json-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar JSONView = require('./json-viewer');\nvar FilterInput = require('./filter-input');\nvar util = require('./util');\nvar FbBtn = require('./forward-back-btn');\nvar CloseBtn = require('./close-btn');\n\nvar KV_RE = /^(k|v):/;\n\nvar JSONDialog = React.createClass({\n  getInitialState: function () {\n    return { history: [] };\n  },\n  onFilter: function(keyword) {\n    keyword = keyword.trim();\n    var self = this;\n    self._type = 0;\n    var not = keyword[0] === '!';\n    if (not) {\n      keyword = keyword.substring(1).trim();\n    }\n    if (KV_RE.test(keyword)) {\n      self._type = RegExp.$1 === 'k' ? 1 : 2;\n      keyword = keyword.substring(2);\n    }\n    clearTimeout(self.filterTimer);\n    if (self._keyword !== keyword || self._not !== not) {\n      self._keyword = keyword;\n      self._not = not;\n      if (self._type && keyword[0] === '!') {\n        not = true;\n        keyword = keyword.substring(1).trim();\n      }\n      if (keyword) {\n        keyword = {\n          not: not,\n          keyword: keyword.toLowerCase(),\n          regexp: util.toRegExp(keyword)\n        };\n      }\n      self._keywordObj = keyword;\n      if (!keyword) {\n        return self.setState({ curData: null });\n      }\n    }\n    self.filterTimer = setTimeout(function() {\n      var data = self.state.data;\n      self.setState({ curData: data && self.filterJson(data) });\n    }, 600);\n  },\n  filterJson: function(data) {\n    if (!this._keywordObj) {\n      return;\n    }\n    var json = util.filterJsonText(data.str, this._keywordObj, this._type);\n    if (!json) {\n      return;\n    }\n    return {\n      json: json,\n      str: JSON.stringify(json, null, '  ')\n    };\n  },\n  show: function(text, keyPath) {\n    if (!text) {\n      return;\n    }\n    var history = this.state.history;\n    var historyIndex = this.state.historyIndex;\n    var len = history.length;\n    if (historyIndex == null) {\n      historyIndex = len - 1;\n    }\n    var last = historyIndex > -1 && history[historyIndex];\n    var keyName = keyPath ? keyPath[0] : null;\n    if (!last || last[0] !== text || last[1] !== keyName) {\n      ++historyIndex;\n      if (historyIndex !== len) {\n        history.splice(historyIndex, len - historyIndex);\n      }\n      history.push([text, keyName]);\n      var overLen = historyIndex - 35; // max: 36\n      if (overLen > 0) {\n        history.splice(0, overLen);\n      }\n    }\n    this.state.historyIndex = historyIndex;\n    this._show(text, keyPath);\n  },\n  _show: function (text, keyPath) {\n    var self = this;\n    var data = this.state.data;\n    this.setState({ keyPath: Array.isArray(keyPath) ? keyPath : null });\n    if (data && data.text === text) {\n      self.focus();\n      return self.refs.jsonDialog.show();\n    }\n    var json = util.parseJSON(text);\n    if (!json && !/[\\r\\n]/.test(text)) {\n      if (!/\\s/.test(text) && text.indexOf('&') !== -1) {\n        json = util.parseQueryString(text, null, null, decodeURIComponent);\n      } else if (text.indexOf(';') !== -1 || text.indexOf('=') !== -1) {\n        json = util.parseQueryString(text, ';', null, decodeURIComponent);\n      }\n    }\n    if (json) {\n      data = {\n        json: json,\n        text: text,\n        str: JSON.stringify(json, null, '  ')\n      };\n    } else {\n      return util.parseRawJson(text);\n    }\n    this.setState({ curData: this.filterJson(data), data: data }, function () {\n      self.refs.jsonDialog.show();\n      self.focus();\n    });\n  },\n  showHistory: function(index) {\n    this.state.historyIndex = index;\n    var item = this.state.history[index];\n    this._show(item[0], item[1] == null ? null : [item[1]]);\n  },\n  onBack: function() {\n    var historyIndex = this.state.historyIndex;\n    if (historyIndex > 0) {\n      this.showHistory(historyIndex - 1);\n    }\n  },\n  onForward: function() {\n    var history = this.state.history;\n    var historyIndex = this.state.historyIndex + 1;\n    if (historyIndex < history.length) {\n      this.showHistory(historyIndex);\n    }\n  },\n  focus: function() {\n    var self = this;\n    setTimeout(function() {\n      self.refs.filterInput.focus();\n    }, 600);\n  },\n  render: function () {\n    var state = this.state;\n    var history = state.history;\n    var historyIndex = state.historyIndex;\n\n    return (\n      <Dialog ref=\"jsonDialog\" wstyle=\"w-json-dialog\">\n        <div className=\"modal-body\">\n          <FbBtn\n            disabledBack={historyIndex <= 0}\n            disabledForward={historyIndex >= history.length - 1}\n            onBack={this.onBack}\n            onForward={this.onForward}\n          />\n          <CloseBtn />\n          <div\n            className=\"v-box\"\n            style={{ width: 880, height: 560, marginTop: 22, background: this._keywordObj ? 'var(--b-filtered)' : undefined }}\n          >\n            <JSONView keyPath={state.keyPath} dialog data={state.curData || state.data} viewSource={true} />\n          </div>\n          <FilterInput ref=\"filterInput\" onChange={this.onFilter} placeholder=\" (as: xxx, k:xxx or v:xxx)\" />\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nvar JSONDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (text, keyPath) {\n    this.refs.jsonDialog.show(text, keyPath);\n  },\n  render: function () {\n    return <JSONDialog ref=\"jsonDialog\" />;\n  }\n});\n\nmodule.exports = JSONDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/json-viewer.js",
    "content": "require('../css/json-viewer.css');\nvar React = require('react');\nvar $ = require('jquery');\nvar ReactDOM = require('react-dom');\nvar TextView = require('./textview');\nvar CopyBtn = require('./copy-btn');\nvar message = require('./message');\nvar ContextMenu = require('./context-menu');\nvar win = require('./win');\nvar Tips = require('./panel-tips');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar JSONTree = require('./components/react-json-tree')['default'];\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar events = require('./events');\nvar MAX_LENGTH = 1024 * 16;\nvar STR_SELECTOR = 'span[style=\"color: var(--c-jb);\"]';\nvar LINK_RE = /^\"(https?:)?(\\/\\/[^/]\\S+)\"$/i;\nvar contextMenuList = [\n  { name: 'Copy Object' },\n  { name: 'Expand All' },\n  { name: 'Collapse All' }\n];\nvar SEARCH_MENU = [{name: 'Search Object'}];\nvar KEY_PATH = ['root'];\n\nfunction compare(a, b) {\n  return a > b ? 1 : -1;\n}\n\nvar JsonViewer = React.createClass({\n  getInitialState: function () {\n    return { lastData: {} };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  preventBlur: function (e) {\n    e.target.nodeName != 'INPUT' && e.preventDefault();\n  },\n  getCurStr: function() {\n    var data = this.props.data;\n    return data && data.str;\n  },\n  edit: function () {\n    var str = this.getCurStr();\n    if (str) {\n      util.openEditor(str);\n    }\n  },\n  onContextMenu: function(e) {\n    var isDialog = this.props.dialog;\n    var data = this.props.data || {};\n    var ctxMenu = util.getMenuPosition(e, 110, isDialog ? 90 : 120);\n    contextMenuList[0].copyText = data.str || '';\n    ctxMenu.list = isDialog ? contextMenuList : contextMenuList.concat(SEARCH_MENU);\n    this.refs.contextMenu.show(ctxMenu);\n    e.preventDefault();\n  },\n  onClickContextMenu: function(action) {\n    if (action === 'Expand All') {\n      this.expandAll();\n    } else if (action === 'Collapse All') {\n      this.collapseAll();\n    } else if (action === 'Search Object') {\n      this.search();\n    }\n  },\n  search: function() {\n    var str = this.getCurStr();\n    if (str) {\n      events.trigger('showJsonViewDialog', str);\n    }\n  },\n  showMockDialog: function(e) {\n    var self = this;\n    var props = self.props;\n    var reqData = props.reqData;\n    if (reqData) {\n      return events.trigger('showMockDialog', {\n        type: props.reqType,\n        item: reqData\n      });\n    }\n    self.showNameInput(e);\n  },\n  showNameInput: function (e) {\n    var self = this;\n    self.state.showDownloadInput = /w-download/.test(e.target.className);\n    self.state.showNameInput = true;\n    self.forceUpdate(function () {\n      var nameInput = findDOMNode(self.refs.nameInput);\n      var defaultName = !nameInput.value && self.props.defaultName;\n      if (defaultName) {\n        nameInput.value = defaultName;\n      }\n      nameInput.select();\n      nameInput.focus();\n    });\n  },\n  hideNameInput: function () {\n    this.state.showNameInput = false;\n    this.forceUpdate(function () {\n      var nameInput = findDOMNode(this.refs.nameInput);\n      var defaultName = this.props.defaultName;\n      if (defaultName === nameInput.value) {\n        nameInput.value = '';\n      }\n    });\n  },\n  submit: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var modal = dataCenter.valuesModal;\n    if (!modal) {\n      return;\n    }\n    var target = findDOMNode(this.refs.nameInput);\n    var name = target.value.trim();\n    var self = this;\n    if (self.state.showDownloadInput) {\n      self.download();\n      return;\n    }\n    if (!name) {\n      message.error('The key is required');\n      return;\n    }\n\n    if (/\\s/.test(name)) {\n      message.error('Spaces are not allowed in the key');\n      return;\n    }\n    var handleSubmit = function (sure) {\n      if (!sure) {\n        return;\n      }\n      var value = (self.state.lastData.str || '').replace(/\\r\\n|\\r/g, '\\n');\n      dataCenter.values.add({ name: name, value: value }, function (data, xhr) {\n        if (data && data.ec === 0) {\n          modal.add(name, value);\n          target.value = '';\n          target.blur();\n        } else {\n          util.showSystemError(xhr);\n        }\n      });\n    };\n    if (!modal.exists(name)) {\n      return handleSubmit(true);\n    }\n    win.confirm(\n      'The key \\'' + name + '\\' is already in use. Overwrite?',\n      handleSubmit\n    );\n  },\n  download: function () {\n    var target = findDOMNode(this.refs.nameInput);\n    var name = target.value.trim();\n    target.value = '';\n    var data = this.props.data || {};\n    findDOMNode(this.refs.filename).value = name;\n    findDOMNode(this.refs.content).value = data.str || '';\n    findDOMNode(this.refs.downloadForm).submit();\n    this.hideNameInput();\n  },\n  toggle: function () {\n    this.setState({ viewSource: !this.state.viewSource });\n  },\n  componentDidMount: function () {\n    var viewer = $(findDOMNode(this.refs.jsonViewer));\n    viewer\n      .on('mouseenter', STR_SELECTOR, function (e) {\n        if (!(e.ctrlKey || e.metaKey)) {\n          return;\n        }\n        var elem = $(this);\n        if (LINK_RE.test(elem.text())) {\n          elem.addClass('w-is-link');\n        }\n      })\n      .on('mouseleave', STR_SELECTOR, function () {\n        $(this).removeClass('w-is-link');\n      })\n      .on('mousedown', STR_SELECTOR, function (e) {\n        if (!(e.ctrlKey || e.metaKey)) {\n          return;\n        }\n        var elem = $(this);\n        if (LINK_RE.test(elem.text())) {\n          window.open((RegExp.$1 || 'http:') + RegExp.$2);\n        }\n      });\n  },\n  expandAll: function() {\n    var expandedStatus = this.state.expandedStatus || 0;\n    ++expandedStatus;\n    this.setState({\n      expandedStatus: expandedStatus,\n      shouldExpandNode: function() {\n        return expandedStatus;\n      }\n    });\n  },\n  collapseAll: function() {\n    var expandedStatus = this.state.expandedStatus;\n    if (expandedStatus) {\n      expandedStatus = false;\n    } else if (expandedStatus === false) {\n      expandedStatus = 0;\n    } else {\n      expandedStatus = false;\n    }\n    this.setState({\n      expandedStatus: expandedStatus,\n      shouldExpandNode: function() {\n        return expandedStatus;\n      }\n    });\n  },\n  render: function () {\n    var state = this.state;\n    var viewSource = state.viewSource;\n    var props = this.props;\n    var data = props.data;\n    var tips = props.tips;\n    var className = 'fill v-box w-props-wrap w-json-viewer';\n    var noData = !data;\n    if (noData) {\n      data = state.lastData || {};\n      if (tips) {\n        return <div className={className + (props.hide ? ' hide' : '')}><Tips data={tips} /></div>;\n      }\n    } else {\n      state.lastData = data;\n    }\n    var value = data.str || '';\n    if (value && viewSource) {\n      var exceed = value.length - MAX_LENGTH;\n      if (exceed > 512) {\n        value =\n          value.substring(0, MAX_LENGTH) +\n          '...\\r\\n\\r\\n(' +\n          exceed +\n          ' characters left, you can click on the ViewAll button in the upper right corner to view all)\\r\\n';\n      }\n    }\n    return (\n      <div\n        className={className + (noData || props.hide ? ' hide' : '')}\n      >\n        <Tips data={tips} />\n        <div className=\"w-textarea-bar\">\n          <CopyBtn value={data.str} />\n          <a\n            onDoubleClick={this.download}\n            onClick={this.showNameInput}\n            draggable=\"false\"\n          >\n            Download\n          </a>\n          <a style={{display: dataCenter.hideMockMenu ? 'none' : null}} className=\"w-add\" onClick={this.showMockDialog} draggable=\"false\">\n            { props.reqData ? 'Mock' : '+Key' }\n          </a>\n          {viewSource ? (\n            <a onClick={this.edit} draggable=\"false\">\n              ViewAll\n            </a>\n          ) : (props.dialog ? undefined : <a onClick={this.search} draggable=\"false\">\n                Search\n              </a>)}\n          <a onClick={this.toggle}>\n            {viewSource ? 'JSON' : 'Text'}\n          </a>\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: state.showNameInput ? 'block' : 'none' }}\n            className=\"w-shadow w-textarea-input\"\n          >\n            <input\n              ref=\"nameInput\"\n              onKeyDown={this.submit}\n              onBlur={this.hideNameInput}\n              type=\"text\"\n              maxLength=\"64\"\n              placeholder={\n                state.showDownloadInput ? 'Enter filename' : 'Enter key name'\n              }\n            />\n            <button\n              type=\"button\"\n              onClick={this.submit}\n              className=\"btn btn-primary\"\n            >\n              {state.showDownloadInput ? 'OK' : '+Key'}\n            </button>\n          </div>\n          <form\n            ref=\"downloadForm\"\n            action=\"cgi-bin/download\"\n            style={{ display: 'none' }}\n            method=\"post\"\n            target=\"downloadTargetFrame\"\n          >\n            <input ref=\"filename\" name=\"filename\" type=\"hidden\" />\n            <input ref=\"content\" name=\"content\" type=\"hidden\" />\n          </form>\n        </div>\n        <TextView\n          className={'fill w-json-viewer-str' + (viewSource ? '' : ' hide')}\n          value={value}\n        />\n        <div\n          ref=\"jsonViewer\"\n          onContextMenu={this.onContextMenu}\n          className={'fill w-json-viewer-tree' + (viewSource ? ' hide' : '')}\n        >\n          <JSONTree keyPath={props.keyPath || KEY_PATH} data={data.json} sortObjectKeys={compare} shouldExpandNode={state.shouldExpandNode}\n            expandAll={this.expandAll} collapseAll={this.collapseAll} onSearch={props.dialog ? null : this.search} />\n        </div>\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = JsonViewer;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/kv-dialog.js",
    "content": "require('../css/kv.css');\nvar React = require('react');\nvar Dialog = require('./dialog');\nvar util = require('./util');\nvar events = require('./events');\nvar win = require('./win');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar KVDialog = React.createClass({\n  getInitialState: function () {\n    return { list: [], history: [] };\n  },\n  show: function (data, rulesModal, valuesModal, isValues, selectedHistory) {\n    this.isValues = isValues;\n    this.refs.kvDialog.show();\n    this._hideDialog = false;\n    var history = [];\n    var hideDefaultOption = data && data.hideDefaultOption;\n    if (data && Array.isArray(data.list) && typeof data.data === 'object') {\n      var count = 0;\n      data.list.forEach(function(name) {\n        if (name && count < 360 && typeof name === 'string' && name.length <= 256) {\n          ++count;\n          history.push(name);\n        }\n      });\n      if (data.selected) {\n        selectedHistory = history.indexOf(data.selected) === -1 ? '' : data.selected;\n      }\n      data = data.data;\n    }\n    var modal = isValues ? valuesModal : rulesModal;\n    this.setState({\n      hideDefaultOption: hideDefaultOption,\n      selectedHistory: history.indexOf(selectedHistory) === -1 ? (hideDefaultOption ? history[0] : '') : selectedHistory,\n      history: history,\n      modal: modal,\n      list: util.parseImportData(\n        data || '',\n        modal,\n        isValues\n      )\n    });\n  },\n  hide: function () {\n    this.refs.kvDialog.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  selectHistory: function(e) {\n    var onHistoryChange = this.props.onHistoryChange;\n    onHistoryChange && onHistoryChange(e.target.value, this.isValues);\n  },\n  viewContent: function (e) {\n    util.openEditor(e.target.title);\n  },\n\n  export: function () {\n    var data = {};\n    var list = [];\n    this.state.list.forEach(function (item) {\n      if (item.checked) {\n        data[item.name] = item.value;\n        list.push(item.name);\n      }\n    });\n    data[''] = list;\n    util.download(data, (this.isValues ? 'values_' : 'rules_') + util.formatDate() + '.txt');\n  },\n  confirm: function () {\n    var data = {};\n    var list = [];\n    var hasConflict;\n    var self = this;\n    self.state.list.forEach(function (item) {\n      if (item.checked) {\n        hasConflict = hasConflict || item.isConflict;\n        data[item.name] = item.value;\n        list.push(item.name);\n      }\n    });\n    data[''] = list;\n    var save = function() {\n      self.hide();\n      return events.trigger(self.isValues ? 'uploadValues' : 'uploadRules', data);\n    };\n    if (!hasConflict) {\n      return save();\n    }\n    win.confirm(\n      'Content conflict detected. Overwrite existing content?',\n      function (sure) {\n        if (sure) {\n          save();\n        }\n      }\n    );\n  },\n  checkAll: function (e) {\n    var list = this.state.list;\n    if (!list || !list.length) {\n      return;\n    }\n    var checked = e.target.checked;\n    list.forEach(function (item) {\n      item.checked = checked;\n    });\n    this.setState({ list: list });\n  },\n  checkItem: function(e, item) {\n    item.checked = e.target.checked;\n    this.setState({});\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var list = state.list || [];\n    var history = state.history;\n    var selectedHistory = state.selectedHistory || '';\n    var len = list.length;\n    var noData = !len;\n    var checkedCount = 0;\n    var modal = state.modal;\n    var hideDefaultOption = state.hideDefaultOption;\n    var checkedAll = !noData && list.every(function (item) {\n      return item.checked;\n    });\n    var title = self.isValues ? 'Values' : 'Rules';\n\n    return (\n      <Dialog ref=\"kvDialog\" wstyle=\"w-kv-dialog\">\n        <div className=\"modal-header\">\n          <h4>Select {title}</h4>\n          <CloseBtn onClick={self.hide} />\n        </div>\n        <div className=\"modal-body\">\n          {history.length ? <label>\n            History:\n            <select\n              value={selectedHistory}\n              onChange={this.selectHistory}\n              className=\"form-control w-history-record-list\"\n            >\n              {hideDefaultOption ? null : <option value=\"\">\n                Select history\n              </option>}\n              {\n                history.map(function(item, i) {\n                  return (\n                    <option key={i} value={item}>\n                      {item}\n                    </option>\n                  );\n                })\n              }\n            </select>\n          </label> : undefined}\n          <table className=\"table\">\n            <thead>\n              <th className=\"w-kv-box\"><input type=\"checkbox\" checked={checkedAll} onChange={self.checkAll} disabled={noData} /></th>\n              <th className=\"w-kv-name\">\n                Name\n              </th>\n              <th className=\"w-kv-operation\">Content</th>\n            </thead>\n            <tbody className=\"w-hover-body\">\n              {noData ? (\n                <tr>\n                  <td colSpan=\"3\" className=\"w-empty\">\n                    Empty\n                  </td>\n                </tr>\n              ) : (\n                list.map(function (item, i) {\n                  var isGroup = util.isGroup(item.name);\n                  var value = typeof item.value === 'string' ? item.value : '';\n                  var exceed = value.length > 128;\n                  if (item.checked) {\n                    ++checkedCount;\n                  }\n                  var curValue;\n                  var showConflict = item.isConflict && !isGroup;\n                  if (showConflict) {\n                    var oldItem = modal && modal.get(item.name);\n                    curValue = oldItem && oldItem.value;\n                    if (curValue) {\n                      curValue = '<<<<<<<<<<< <<<<<<<<<<< <<<<<<<<<<< OLD <<<<<<<<<<< <<<<<<<<<<< <<<<<<<<<<<\\n\\n' +\n                      curValue + '\\n\\n=========== ========== ========== BOUNDARY ========== ========== ==========\\n\\n' +\n                      value + '\\n\\n>>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> NEW >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>>';\n                    }\n                  }\n\n                  return (\n                    <tr\n                      key={i}\n                      className={item.isConflict ? 'w-kv-conflict' : undefined}\n                    >\n                      <th className=\"w-kv-box\"><input type=\"checkbox\" checked={item.checked} onChange={function(e) {\n                        self.checkItem(e, item);\n                      }} /></th>\n                      <td title={item.name} className=\"w-kv-name\">\n                        {isGroup ? <Icon name=\"triangle-right\" className=\"w-list-group-icon\" /> : null}{item.name}\n                        {showConflict ? <strong onClick={self.viewContent} title={curValue}>[Conflict]</strong> : null}\n                      </td>\n                      <td className=\"w-kv-operation\">\n                        <pre>\n                          {exceed ? value.substring(0, 100) + '...' : value}\n                        </pre>\n                        {exceed ? <a title={value} onClick={self.viewContent}>\n                            View all\n                          </a> : null }\n                      </td>\n                    </tr>\n                  );\n                })\n              )}\n            </tbody>\n          </table>\n        </div>\n        <div className=\"modal-footer\">\n          <label className=\"w-kv-check-all\">\n            <input type=\"checkbox\" checked={checkedAll} onChange={self.checkAll} disabled={noData} />\n            Select all\n          </label>\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            disabled={!checkedCount}\n            onClick={this.export}\n          >\n            Export Selected\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-primary\"\n            disabled={!checkedCount}\n            onClick={this.confirm}\n          >\n            Add To {title} {len ? ' (' + checkedCount + ' / ' + len + ')' : null}\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = KVDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/large-dialog.js",
    "content": "require('../css/large-dialog.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar events = require('./events');\nvar dataCenter = require('./data-center');\nvar CloseBtn = require('./close-btn');\n\nvar AccountDialog = React.createClass({\n  show: function(url) {\n    if (url) {\n      var iframe = this.getIframe();\n      iframe.onload = dataCenter.handleIframeLoad;\n      if (url === 'editor.html') {\n        iframe.setAttribute('data-type', 'fake');\n      }\n      iframe.src = url;\n    }\n    this.refs.dialog.show();\n  },\n  hide: function() {\n    this.refs.dialog.hide();\n  },\n  getIframe: function() {\n    return ReactDOM.findDOMNode(this.refs.iframe);\n  },\n  getWindow: function() {\n    return this.getIframe().contentWindow;\n  },\n  shouldComponentUpdate: function() {\n    return false;\n  },\n  openInNewWin: function() {\n    events.trigger('openInNewWin');\n  },\n  render: function() {\n    var props = this.props;\n    var className = props.className;\n    var hideButton = props.hideButton;\n\n    return (\n      <Dialog ref=\"dialog\" wstyle={'w-large-dialog' + (className ? ' ' + className : '')}>\n        {hideButton ? null : <a className=\"w-open-win-btn\" onClick={this.openInNewWin}>Open In New Window</a>}\n        <CloseBtn />\n        <div className=\"modal-body w-fix-drag\">\n          <iframe ref=\"iframe\" className=\"modal-body\" />\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = AccountDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/lazy-init.js",
    "content": "var React = require('react');\n\nvar LazyInit = React.createClass({\n  render: function () {\n    if (!this.props.inited && !this._inited) {\n      return null;\n    }\n    this._inited = true;\n    return this.props.children;\n  }\n});\n\nmodule.exports = LazyInit;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/list-dialog.js",
    "content": "require('../css/list-dialog.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar util = require('./util');\nvar Dialog = require('./dialog');\nvar Tabs = require('./tabs');\nvar RuleList = require('./rule-list');\nvar $ = require('jquery');\nvar parseRules = require('./parse-rules');\nvar dataCenter = require('./data-center');\nvar ShareViaURLBtn = require('./share-via-url-btn');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar TEMP_LINK_RE_G = /(?:^|\\s)(?:[\\w-]+:\\/\\/)?temp\\/([\\da-z]{64})(?:\\.[\\w-]+)?(?:$|\\s)/mg;\n\nvar ListDialog = React.createClass({\n  getInitialState: function () {\n    return {\n      checkedItems: {},\n      checkedRuleList: [],\n      ruleListLen: 0,\n      filename: '',\n      tabs: [\n        {\n          icon: 'file',\n          name: 'Rules Files',\n          active: true\n        },\n        {\n          icon: 'list',\n          name: 'Rules Items'\n        }\n      ]\n    };\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.dialog.isVisible();\n  },\n  onChange: function (e) {\n    var target = e.target;\n    var name = target.parentNode.title;\n    var checkedItems = this.state.checkedItems;\n    if (target.checked) {\n      checkedItems[name] = 1;\n    } else {\n      delete checkedItems[name];\n    }\n    this.setState({ checkedItems: checkedItems });\n  },\n  donwload: function(data) {\n    var input = findDOMNode(this.refs.filename);\n    var form = findDOMNode(this.refs.exportData);\n    findDOMNode(this.refs.exportName).value = input.value.trim();\n    findDOMNode(this.refs.data).value = JSON.stringify(data);\n    form.submit();\n    input.value = '';\n  },\n  filterFilename: function (e) {\n    this.setState({ filename: util.formatFilename(e.target.value) });\n  },\n  getInputValue: function () {\n    return util.formatFilename(findDOMNode(this.refs.filename).value.trim());\n  },\n  getRuleList: function (cb) {\n    var rulesModal = this.state.rulesModal;\n    var allValues = {};\n    var checkedList = this.state.checkedRuleList;\n    var allList = [];\n    rulesModal.list.forEach(function(item, i) {\n      if (i && item.checked) {\n        allValues = $.extend({}, item.rawValues, allValues);\n      }\n      item.rules.forEach(function(rule) {\n        allList.push(rule.join(' '));\n      });\n    });\n    var defaultItem = rulesModal.list[0];\n    if (defaultItem && defaultItem.checked) {\n      allValues = $.extend({}, defaultItem.rawValues, allValues);\n    }\n\n    var rules = checkedList.filter(function(line) {\n      return allList.indexOf(line) !== -1;\n    }).join('\\n');\n    var values = [];\n    var files = [];\n    rules.replace(/\\{([^\\s}]+)\\}/g, function (_, key) {\n      var val = allValues[key];\n      if (values.indexOf(val) === -1) {\n        values.push(val);\n      }\n    }).replace(TEMP_LINK_RE_G, function (_, filename) {\n      if (files.indexOf(filename) === -1 && files.length < 11) {\n        files.push(filename);\n      }\n    });\n    values = values.join('\\n');\n    if (values) {\n      rules += '\\n\\n' + values;\n    }\n    var newVals;\n    var filename = this.getInputValue() || 'mock_' + util.formatDate() + '.txt';\n    var execCb = function() {\n      cb([rules, newVals || {}], filename);\n    };\n    if (files.length) {\n      dataCenter.getTempFile({ files: files.join() }, function (result) {\n        var list = result && result.list;\n        if (list && list.length) {\n          newVals = {\n            isFile: true,\n            base64: list.shift(),\n            list: list\n          };\n        }\n        execCb();\n      });\n    } else {\n      execCb();\n    }\n  },\n  exportRuleList: function () {\n    this.getRuleList(function (data, filename) {\n      util.download(data, filename);\n    });\n  },\n  isRuleList: function () {\n    return this.state.tabs[1].active;\n  },\n  onConfirm: function (e) {\n    if (e.target.disabled) {\n      return;\n    }\n    this.refs.dialog.hide();\n    if (this.isRuleList()) {\n      return this.exportRuleList();\n    }\n    var items = this.state.checkedItems;\n    this.donwload(items);\n  },\n  getExportData: function(cb) {\n    if (this.isRuleList()) {\n      return this.getRuleList(cb);\n    }\n    cb(this.state.checkedItems);\n  },\n  getAllItems: function () {\n    var list = this.props.list || [];\n    var result = {};\n    list.forEach(function (name) {\n      result[name] = 1;\n    });\n    return result;\n  },\n  show: function (selectedList) {\n    var self = this;\n    var props = self.props;\n    var list = props.list || [];\n    var rulesModal = self.getRulesModal();\n\n    self.refs.dialog.show();\n    if (selectedList) {\n      if (selectedList && typeof selectedList === 'string') {\n        selectedList = [selectedList];\n      }\n      var checkedItems = {};\n      if (Array.isArray(selectedList)) {\n        selectedList.forEach(function(name) {\n          if (list.indexOf(name) !== -1) {\n            checkedItems[name] = 1;\n          }\n        });\n      }\n      self.setState({ checkedItems: checkedItems });\n    }\n    !this.props.onConfirm && setTimeout(function () {\n      var input = findDOMNode(self.refs.filename);\n      input.focus();\n      input.select();\n    }, 500);\n\n    if (rulesModal) {\n      var ruleListLen = 0;\n      var map = {};\n      var modal = self.state.rulesModal;\n      list = rulesModal.list.map(function(name) {\n        var item = rulesModal.get(name);\n        var data = parseRules(item.value) || { rules: [], values: {}, rawValues: {} };\n        var oldData = modal && modal.map[name];\n        data.name = name;\n        data.checked = oldData && oldData.checked;\n        map[name] = data;\n        ruleListLen += data.rules.length;\n        return data;\n      });\n      this.setState({\n        rulesModal: {\n          list: list,\n          map: map\n        },\n        ruleListLen: ruleListLen\n      });\n    }\n  },\n  hide: function() {\n    this.refs.dialog.hide();\n  },\n  preventDefault: function (e) {\n    e.preventDefault();\n  },\n  onTabChange: function (tab) {\n    var self = this;\n    var tabs = self.state.tabs;\n    tabs.forEach(function (t) {\n      t.active = false;\n    });\n    tab.active = true;\n    self.setState({ tabs: tabs });\n  },\n  getRulesModal: function() {\n    var props = this.props;\n    return props.name === 'rules' && props.modal;\n  },\n  onCheckedRuleChange: function (list) {\n    this.setState({checkedRuleList: list});\n  },\n  checkAll: function (e) {\n    var list = this.props.list;\n    if (!list || !list.length) {\n      return;\n    }\n    var checkedItems = {};\n    if (e.target.checked) {\n      list.forEach(function (name) {\n        checkedItems[name] = 1;\n      });\n    }\n    this.setState({ checkedItems: checkedItems });\n  },\n  checkItem: function(e, item) {\n    item.checked = e.target.checked;\n    this.setState({});\n  },\n  onShare: function(err) {\n    if (!err) {\n      this.refs.dialog.hide();\n      findDOMNode(this.refs.filename).value = '';\n    }\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var props = self.props;\n    var tabs = state.tabs;\n    var ruleListLen = state.ruleListLen;\n    var rulesModal = state.rulesModal;\n    var list = props.list || [];\n    var listLen = list.length;\n    var checkedItems = state.checkedItems;\n    var checkedNames = Object.keys(checkedItems);\n    var selectedCount = rulesModal && tabs[1].active ? state.checkedRuleList.length : checkedNames.length;\n    var pageName = props.name;\n    var tips = selectedCount ? props.tips : null;\n    var onConfirm = props.onConfirm;\n    var isRules = props.isRules;\n    var checkedAll = listLen && list.every(function (name) {\n      return checkedNames.indexOf(name) !== -1;\n    });\n\n    return (\n      <Dialog ref=\"dialog\" wclassName=\"w-list-dialog\">\n        { props.title ? <div className=\"modal-header\">\n                <h4>{props.title}</h4>\n                <CloseBtn />\n              </div> : null }\n        <div className=\"modal-body\">\n          {props.title ? null : <CloseBtn />}\n          {rulesModal ? <Tabs tabs={tabs} onChange={self.onTabChange} /> : null}\n          <div className={tabs[0].active ? '' : ' hide'} style={{marginTop: 10}}>\n            <div className=\"w-list-wrapper\">\n              {list.map(function (name, i) {\n                if (!i && isRules) {\n                  return;\n                }\n                return (\n                  <label title={name} key={name}>\n                    <input\n                      onChange={self.onChange}\n                      type=\"checkbox\"\n                      checked={!!checkedItems[name]}\n                    />\n                    {util.isGroup(name) ? <Icon name=\"triangle-right\" className=\"w-list-group-icon\" /> : null}\n                    {name}\n                  </label>\n                );\n              })}\n            </div>\n            {tips ? <h5 className=\"w-list-tips-title\">{tips}</h5> : null}\n          </div>\n          {rulesModal ? <RuleList modal={rulesModal} hide={!tabs[1].active} onChange={self.onCheckedRuleChange} /> : null}\n          {tips ? <div className=\"w-list-tips\">\n              {\n                checkedNames.map(function(name) {\n                  return (\n                    <span key={name}>\n                      {util.isGroup(name) ? <Icon name=\"triangle-right\" className=\"w-list-group-icon\" /> : null}\n                      {name}\n                    </span>\n                  );\n                })\n              }\n            </div> : (onConfirm && !rulesModal ? null : <p style={{marginTop: 10, whiteSpace: 'nowrap'}}>\n                Filename:\n                <input\n                  ref=\"filename\"\n                  value={state.filename}\n                  onChange={self.filterFilename}\n                  style={{ width: 812, display: 'inline-block', marginLeft: 5 }}\n                  className=\"form-control\"\n                  placeholder=\"Enter filename (optional)\"\n                />\n              </p>)}\n        </div>\n        <div className=\"modal-footer\">\n        {onConfirm ? null : <label className={'w-kv-check-all' + (tabs[1].active ? ' hide' : '')}>\n          <input type=\"checkbox\" checked={checkedAll} onChange={this.checkAll} disabled={!listLen} />\n          Select all\n        </label>}\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          {onConfirm ? null : <ShareViaURLBtn getFilename={this.getInputValue} disabled={!selectedCount}\n            type={this.isRuleList() ? 'mock' : props.name}\n            getData={this.getExportData} onComplete={this.onShare} />}\n          <button\n            type=\"button\"\n            className=\"btn btn-primary\"\n            disabled={!selectedCount}\n            onMouseDown={this.preventDefault}\n            onClick={onConfirm ? function() {\n              onConfirm(checkedNames);\n            } : this.onConfirm}\n          >\n            {onConfirm ? 'Confirm' : 'Export' + ' (' + selectedCount + ' / ' + (tabs[1].active ?  ruleListLen : listLen) + ')'}\n          </button>\n        </div>\n        <form\n          action={'cgi-bin/' + pageName + '/export'}\n          ref=\"exportData\"\n          style={{ display: 'none' }}\n          target=\"downloadTargetFrame\"\n        >\n          <input ref=\"exportName\" type=\"hidden\" name=\"filename\" />\n          <input ref=\"data\" type=\"hidden\" name={pageName} />\n        </form>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = ListDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/list-modal.js",
    "content": "var $ = require('jquery');\nvar util = require('./util');\n\nfunction ListModal(list, data) {\n  this.reset(list, data, true);\n}\n\nvar proto = ListModal.prototype;\n\nproto.reset = function (list, data, init) {\n  var self = this;\n  self.list = Array.isArray(list) ? list : [];\n  data = data || {};\n  self.data = {};\n  self.groups = {};\n  self.list.forEach(function (name) {\n    var item = (self.data[name] = data[name] || {});\n    item.key = item.key || util.getKey();\n    item.name = name;\n  });\n  if (!init) {\n    self.filter();\n  }\n};\n\nproto.getList = function () {\n  var data = this.data;\n  return this.list.map(function (key) {\n    return data[key];\n  });\n};\n\nproto._getList = function (prop) {\n  var list = [];\n  var data = this.data;\n  Object.keys(data).forEach(function (name) {\n    var item = data[name];\n    if (item && item[prop]) {\n      list.push(item);\n    }\n  });\n  return list;\n};\n\nproto.getItem = function(name) {\n  return this.data[name];\n};\n\nproto.hasChanged = function () {\n  var data = this.data;\n  return Object.keys(data).some(function (name) {\n    var item = data[name];\n    return item && item.changed && !util.isGroup(item.name);\n  });\n};\n\nproto.getGroupName = function(name) {\n  var i = this.list.indexOf(name);\n  if (i !== -1) {\n    for (; i >=0; i--) {\n      name = this.list[i];\n      if (util.isGroup(name)) {\n        return name;\n      }\n    }\n  }\n};\n\nproto._setBoolProp = function (name, prop, bool) {\n  var item = this.get(name);\n  if (item) {\n    item[prop] = bool !== false;\n  }\n  this.filter();\n  return item;\n};\n\nproto.getSelectedNames = function () {\n  var list = [];\n  var data = this.data;\n  Object.keys(data).forEach(function (name) {\n    var item = data[name];\n    if (item && item.selected) {\n      list.push(item.name);\n    }\n  });\n  return list;\n};\n\nproto.exists = function (name) {\n  return this.list.indexOf(name) != -1;\n};\n\nfunction push(list, name) {\n  if (!util.isGroup(name)) {\n    for (var i = 0, len = list.length; i < len; i++) {\n      if (util.isGroup(list[i])) {\n        return list.splice(i, 0, name);\n      }\n    }\n  }\n  list.push(name);\n}\n\nfunction add(name, value, isPre) {\n  if (!name) {\n    return false;\n  }\n  var data = this.get(name);\n  value = value || '';\n  if (data) {\n    data.value = value;\n    return;\n  }\n  if (isPre) {\n    this.list.splice(1, 0, name);\n  } else {\n    push(this.list, name);\n  }\n  var item = (this.data[name] = {\n    key: util.getKey(),\n    name: name,\n    value: value\n  });\n  this.filter();\n  return item;\n}\n\nproto.add = function (name, value) {\n  return add.call(this, name, value);\n};\n\nproto.unshift = function (name, value) {\n  return add.call(this, name, value, true);\n};\n\nproto.set = function (name, value) {\n  var item = this.get(name);\n  if (item) {\n    if (typeof value == 'string') {\n      item.value = value;\n    } else {\n      $.extend(item, value);\n    }\n  }\n};\n\nproto.get = function (name) {\n  return this.data[name];\n};\n\nproto.getByKey = function (key) {\n  var list = this.list;\n  var data = this.data;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = data[list[i]];\n    if (item.key == key) {\n      return item;\n    }\n  }\n};\n\nproto.setSelected = function (name, selected) {\n  return this._setBoolProp(name, 'selected', selected);\n};\n\nproto.moveTo = function (fromName, toName, group, toTop) {\n  var list = this.list;\n  var fromIndex = list.indexOf(fromName);\n  var toIndex = list.indexOf(toName);\n  if (fromIndex !== -1 && toIndex !== -1 && fromIndex !== toIndex) {\n    if (group && util.isGroup(fromName)) {\n      var data = this.data;\n      var children = [fromName];\n      for (var i = fromIndex + 1, len = list.length; i < len; i++) {\n        var name = data[list[i]].name;\n        if (util.isGroup(name)) {\n          break;\n        }\n        children.push(name);\n      }\n      if (fromIndex < toIndex && util.isGroup(toName)) {\n        for (; toIndex < len; toIndex++) {\n          if (util.isGroup(list[toIndex + 1])) {\n            break;\n          }\n        }\n      }\n      len = children.length;\n      if (len > 1 && fromIndex < toIndex) {\n        toIndex = Math.max(0, toIndex - len + 1);\n      }\n      list.splice(fromIndex, len);\n      children.unshift(toIndex, 0);\n      list.splice.apply(list, children);\n    } else if (toTop || util.isGroup(fromName) || !util.isGroup(toName) || this.getGroupName(fromName) === toName) {\n      list.splice(fromIndex, 1);\n      list.splice(toIndex, 0, fromName);\n    } else {\n      list.splice(fromIndex, 1);\n      list.splice(fromIndex > toIndex ? toIndex + 1 : toIndex, 0, fromName);\n    }\n    return true;\n  }\n};\n\nproto.moveToGroup = function(name, groupName, isTop) {\n  if (!groupName) {\n    return;\n  }\n  var list = this.list;\n  var index = list.indexOf(name);\n  if (index === -1 || list.indexOf(groupName) === -1) {\n    return;\n  }\n  list.splice(index, 1);\n  index = list.indexOf(groupName) + 1;\n  if (isTop) {\n    return list.splice(index, 0, name);\n  }\n  for (var len = list.length; index < len; index++) {\n    if (util.isGroup(list[index])) {\n      break;\n    }\n  }\n  list.splice(index, 0, name);\n};\n\nproto.getSelectedList = function () {\n  return this._getList('selected');\n};\n\nproto.setChanged = function (name, changed) {\n  return this._setBoolProp(name, 'changed', changed);\n};\n\nproto.getChangedList = function () {\n  return this._getList('changed');\n};\n\nproto.getChangedGroupList = function(item) {\n  if (!util.isGroup(item.name)) {\n    return [item];\n  }\n  var result = [];\n  var list = this.list;\n  var data = this.data;\n  var i = list.indexOf(item.name) + 1;\n  if (i > 0) {\n    for (var len = list.length; i < len; i++) {\n      item = data[list[i]];\n      if (util.isGroup(item.name)) {\n        break;\n      }\n      item.changed && result.push(item);\n    }\n  }\n  return result;\n};\n\nproto.clearAllActive = function () {\n  var data = this.data;\n  Object.keys(data).forEach(function (name) {\n    data[name].active = false;\n  });\n};\n\nproto.clearAllSelected = function () {\n  var data = this.data;\n  Object.keys(data).forEach(function (name) {\n    data[name].selected = false;\n  });\n};\n\nproto.setActive = function (name, active) {\n  var item = this.get(name);\n  if (item && !util.isGroup(item.name)) {\n    active = active !== false;\n    active && this.clearAllActive();\n    item.active = active;\n  }\n  return item;\n};\n\nproto.getActiveObj = function() {\n  var obj = {};\n  var list = this.list;\n  var data = this.data;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = data[list[i]];\n    if (util.isGroup(item.name)) {\n      if (item._isNewGroup) {\n        delete item._isNewGroup;\n        obj.groupItem = item;\n        if (obj.activeItem) {\n          return obj;\n        }\n      }\n    } else {\n      if (item.active) {\n        obj.activeItem = item;\n        if (obj.groupItem) {\n          return obj;\n        }\n      }\n    }\n  }\n  return obj;\n};\n\nproto.getActive = function (ensure) {\n  var first;\n  ensure = ensure === true;\n  var list = this.list;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = this.data[list[i]];\n    if (!util.isGroup(item.name)) {\n      if (item.active) {\n        return item;\n      }\n      if (ensure) {\n        first = first || item;\n      }\n    }\n  }\n  if (first) {\n    first.active = true;\n  }\n  return first;\n};\n\nproto.remove = function (name) {\n  var index = this.getIndex(name);\n  if (index != -1) {\n    this.list.splice(index, 1);\n    delete this.data[name];\n    return true;\n  }\n};\n\nproto.removeGroup = function (name) {\n  var list = this.list;\n  var index = list.indexOf(name) + 1;\n  if (!index) {\n    return;\n  }\n  var result = [name];\n  var data = this.data;\n  var hasActive;\n  for (var len = list.length; index < len; index++) {\n    name = list[index];\n    if (util.isGroup(name)) {\n      break;\n    }\n    result.push(name);\n    var item = data[name];\n    hasActive = hasActive || (item && item.active);\n  }\n  result.forEach(function(file) {\n    list.splice(list.indexOf(file), 1);\n    delete data[file];\n  });\n  return hasActive && this.getSibling(name);\n};\n\nproto.rename = function (name, newName) {\n  if (!name || !newName || name == newName) {\n    return;\n  }\n\n  var index = this.getIndex(name);\n  if (index != -1) {\n    this.list[index] = newName;\n    var item = this.data[name];\n    delete this.data[name];\n    this.data[newName] = item;\n    item.name = newName;\n    this.filter();\n    return true;\n  }\n};\n\nproto.getIndex = function (name) {\n  return this.list.indexOf(name);\n};\n\nproto.getSibling = function (name) {\n  var index = this.getIndex(name);\n  var list = this.list;\n  name = list[index + 1];\n  name = util.isGroup(name) && list[index - 1];\n  if (name && !util.isGroup(name)) {\n    return this.data[name];\n  }\n  for (var i = index + 1, len = list.length; i < len; i++) {\n    name = list[i];\n    if (!util.isGroup(name)) {\n      return this.data[name];\n    }\n  }\n  for (i = index - 1; i >= 0; i--) {\n    name = list[i];\n    if (!util.isGroup(name)) {\n      return this.data[name];\n    }\n  }\n};\n\n/**\n * 默认根据name过滤\n * selected[s, active, a]: 根据激活的过滤\n */\nproto.search = function (keyword, disabledType) {\n  keyword = typeof keyword === 'string' ? keyword.trim() : '';\n  var not = keyword[0] === '!';\n  if (not) {\n    keyword = keyword.substring(1).trim();\n  }\n  if (keyword &&(disabledType ? /^(b|v|n|k|s):(.*)$/ : /^(selected|s|active|a|b|v|n|k|s):(.*)$/).test(keyword)) {\n    this._type = RegExp.$1;\n    keyword = RegExp.$2.trim();\n    if (keyword[0] === '!') {\n      not = true;\n      keyword = keyword.substring(1).trim();\n    }\n  } else {\n    this._type = ''; // reset\n  }\n  this._keyword = keyword && (util.toRegExp(keyword) || keyword.toLowerCase());\n  this._not = not;\n  this.filter();\n  return !keyword;\n};\n\nfunction setNot(flag, not) {\n  return not ? !flag : flag;\n}\n\nproto.filter = function () {\n  var not = this._not;\n  var keyword = this._keyword;\n  var list = this.list;\n  var filterBody = this._type === 'b' || this._type === 'v';\n  var filterKey = this._type === 'n' || this._type === 'k';\n  var filterSelected = !!this._type && !filterBody && !filterKey;\n  var data = this.data;\n\n  var group;\n  var showGroup;\n  list.forEach(function (name) {\n    var item = data[name];\n    var hideSelected = filterSelected && !item.selected;\n    if (util.isGroup(name)) {\n      if (group && showGroup) {\n        group.hide = false;\n      }\n      group = item;\n      showGroup = false;\n    }\n    if (!keyword || hideSelected) {\n      item.hide = hideSelected;\n    } else if (filterBody) {\n      item.hide = !item.value || setNot(util.checkKey(item.value, item.value, keyword), not);\n    } else {\n      item.hide = !name || setNot(util.checkKey(name, name, keyword), not);\n      if (item.hide && !filterKey) {\n        item.hide = !item.value || setNot(util.checkKey(item.value, item.value, keyword), not);\n      }\n    }\n    showGroup = showGroup || !item.hide;\n  });\n  return list;\n};\n\nproto.up = function () {\n  var list = this.list;\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  if (activeItem.fixed) {\n    return;\n  }\n\n  var index = activeItem ? list.indexOf(activeItem.name) : len - 1;\n  if (!index || this.data[list[index - 1]].fixed) {\n    return;\n  }\n\n  list[index] = list[index - 1];\n  list[index - 1] = activeItem.name;\n  return activeItem;\n};\n\nproto.down = function () {\n  var list = this.list;\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  if (activeItem.fixed) {\n    return;\n  }\n\n  var index = activeItem ? list.indexOf(activeItem.name) : len - 1;\n  if (index >= len - 1 || this.data[list[index + 1]].fixed) {\n    return;\n  }\n\n  list[index] = list[index + 1];\n  list[index + 1] = activeItem.name;\n  return activeItem;\n};\n\nfunction isVisibleItem(item) {\n  return !item.hide && !util.isGroup(item.name);\n}\n\nproto.prev = function () {\n  var list = this.list;\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  var index = activeItem ? list.indexOf(activeItem.name) : len - 1;\n  var data = this.data;\n  var i, item;\n  for (i = index - 1; i >= 0; i--) {\n    item = data[list[i]];\n    if (isVisibleItem(item)) {\n      return item;\n    }\n  }\n\n  for (i = len - 1; i > index; i--) {\n    item = data[list[i]];\n    if (isVisibleItem(item)) {\n      return item;\n    }\n  }\n};\n\nproto.next = function () {\n  var list = this.list;\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  var index = activeItem ? list.indexOf(activeItem.name) : 0;\n  var data = this.data;\n  var i, item;\n  for (i = index + 1; i < len; i++) {\n    item = data[list[i]];\n    if (isVisibleItem(item)) {\n      return item;\n    }\n  }\n\n  for (i = 0; i < index; i++) {\n    item = data[list[i]];\n    if (isVisibleItem(item)) {\n      return item;\n    }\n  }\n};\n\nmodule.exports = ListModal;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/list.js",
    "content": "require('../css/list.css');\nrequire('../css/files-dialog.css');\nvar $ = require('jquery');\nvar util = require('./util');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar message = require('./message');\nvar Divider = require('./divider');\nvar Editor = require('./editor');\nvar FilterInput = require('./filter-input');\nvar ContextMenu = require('./context-menu');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar iframes = require('./iframes');\nvar storage = require('./storage');\nvar RecycleBinDialog = require('./recycle-bin');\nvar EnabledRulesDialog = require('./enabled-rules');\nvar Icon = require('./icon');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar hideEnableHTTPSTips = window.location.href.indexOf('hideEnableHTTPSTips=1') !== -1;\nvar disabledEditor = window.location.href.indexOf('disabledEditor=1') !== -1;\nvar rulesCtxMenuList = [\n  {\n    name: 'Copy',\n    list: [{ name: 'Name' }, { name: 'Rules' }]\n  },\n  { name: 'Enable', action: 'Save' },\n  {\n    name: 'Create',\n    action: 'Rule'\n  },\n  { name: 'Rename' },\n  { name: 'Delete' },\n  { name: 'Import' },\n  { name: 'Export' },\n  { name: 'Trash' },\n  {\n    name: 'Others',\n    action: 'Plugins',\n    list: []\n  },\n  { name: 'Help', sep: true }\n];\nvar valuesCtxMenuList = [\n  {\n    name: 'Copy',\n    list: [{ name: 'Key', action: 'CopyKey' }, { name: 'Value' }]\n  },\n  { name: 'Save' },\n  {\n    name: 'Create',\n    action: 'Key'\n  },\n  { name: 'Rename' },\n  { name: 'Delete' },\n  {\n    name: 'JSON',\n    list: [\n      { name: 'Validate' },\n      { name: 'Format' },\n      { name: 'Inspect' }\n    ]\n  },\n  { name: 'Import' },\n  { name: 'Export' },\n  { name: 'Trash' },\n  {\n    name: 'Others',\n    action: 'Plugins',\n    list: []\n  },\n  { name: 'Help', sep: true }\n];\nvar NAME_PREFIX = 'listmodal$';\nvar JSON_RE = /^\\s*(?:[\\{｛][\\w\\W]+[\\}｝]|\\[[\\w\\W]+\\])\\s*$/;\nvar curTarget;\n\nfunction getTarget(e) {\n  var target = e.target;\n  var nodeName = target.nodeName;\n  if (nodeName === 'A') {\n    return target;\n  }\n  target = target.parentNode;\n  if (target) {\n    nodeName = target.nodeName;\n    if (nodeName === 'A') {\n      return target;\n    }\n  }\n}\n\nfunction getName(name) {\n  if (typeof name !== 'string') {\n    return '';\n  }\n  return name.substring(name.indexOf('_') + 1);\n}\n\nfunction getDragInfo(e, list) {\n  var target = getTarget(e);\n  if (list && !target) {\n    target = $(findDOMNode(list)).find('a:last')[0];\n  }\n  var name = target && target.getAttribute('data-name');\n  if (!name) {\n    return;\n  }\n  var fromName = getNameFromTypes(e);\n  if (fromName && name.toLowerCase() !== fromName) {\n    return {\n      target: target,\n      toName: getName(name)\n    };\n  }\n}\n\nfunction getNameFromTypes(e) {\n  var type = util.findArray(e.dataTransfer.types, function (type) {\n    if (type.indexOf(NAME_PREFIX) === 0) {\n      return true;\n    }\n  });\n  return type && type.substring(NAME_PREFIX.length);\n}\n\n$(document).on('drop', function () {\n  if (curTarget) {\n    curTarget.style.background = '';\n  }\n  curTarget = null;\n});\n\nfunction getSuffix(name) {\n  if (typeof name != 'string') {\n    return '';\n  }\n  var index = name.lastIndexOf('.');\n  return index == -1 ? '' : name.substring(index + 1);\n}\n\nvar List = React.createClass({\n  getInitialState: function() {\n    var nodes = util.parseJSON(storage.get(this.getCollapseKey()));\n    var map = {};\n    this.collapseGroups = Array.isArray(nodes) ? nodes.filter(function(name) {\n      if (util.isGroup(name) && name[1] && !map[name]) {\n        map[name] = 1;\n        return true;\n      }\n    }) : [];\n    return {};\n  },\n  componentDidMount: function () {\n    var self = this;\n    var visible = !self.props.hide;\n    $(window)\n      .keydown(function (e) {\n        if (visible && (e.ctrlKey || e.metaKey)) {\n          var modal = self.props.modal;\n          if (e.keyCode === 83 && util.hasShortcut('save' + (self.isRules() ? 'Rules' : 'Values') + 'Changes')) {\n            modal.getChangedList().forEach(trigger);\n            return false;\n          }\n        }\n      })\n      .on('hashchange', function () {\n        var disabled = window.location.href.indexOf('disabledEditor=1') !== -1;\n        if (disabled !== disabledEditor) {\n          disabledEditor = disabled;\n          self.setState({});\n        }\n      });\n\n    function trigger(item) {\n      self.onDoubleClick(item);\n    }\n    var modal = self.props.modal;\n    this.curListLen = modal.list.length;\n    this.curActiveItem = modal.getActive();\n    $(findDOMNode(self.refs.list))\n      .focus()\n      .on('keydown', function (e) {\n        var item;\n        if (e.keyCode == 38) {\n          //up\n          item = modal.prev();\n        } else if (e.keyCode == 40) {\n          //down\n          item = modal.next();\n        }\n        if (item) {\n          var group = self.getCurGroup(item);\n          group && self.expandGroup(group.name);\n          e.shiftKey ? self.setState({}) : self.onClick(item);\n          if (self.isRules()) {\n            events.trigger('updateUI');\n          }\n          e.preventDefault();\n        }\n      });\n    var comName = self.isRules() ? 'Rules' : 'Values';\n    events.on('toggleCommentInEditor', function () {\n      var activeItem = modal.getActive();\n      if (activeItem) {\n        events.trigger('save' + comName, activeItem);\n      }\n    });\n    if (self.isRules()) {\n      events.on('enabledRulesCountChange', function() {\n        self.setState({});\n      });\n    }\n\n\n    events.on('reload' + comName + 'RecycleBin', function () {\n      self.reloadRecycleBin(comName);\n    });\n    events.on('expand' + comName + 'Group', function(_, groupName) {\n      var group = self.getGroupByName(groupName);\n      group && self.expandGroup(group.name);\n    });\n    events.on('reqTabsChange', function() {\n      self.setState({});\n    });\n    var scrollToBottom = function() {\n      findDOMNode(self.refs.list).scrollTop = 1000000000;\n    };\n    var focusList = function() {\n      findDOMNode(self.refs.list).focus();\n    };\n    events.on('scroll' + comName + 'Bottom', function() {\n      scrollToBottom();\n    });\n    events.on('focus' + comName + 'List', function() {\n      focusList();\n    });\n    events.on(comName.toLowerCase() + 'NameChanged', function(_, name, newName) {\n      var index = name === newName ? - 1 : self.collapseGroups.indexOf(name);\n      if (index !== -1) {\n        if (self.collapseGroups.indexOf(newName) !== -1) {\n          self.collapseGroups.splice(index, 1);\n        } else {\n          self.collapseGroups[index] = newName;\n        }\n        storage.set(self.getCollapseKey(), JSON.stringify(self.collapseGroups));\n      }\n    });\n    events.on('focus' + (self.isRules() ? 'Rules' : 'Values') + 'FilterInput', function() {\n      self.refs.filterInput.focus();\n    });\n    self.ensureVisible(true);\n  },\n  expandGroup: function(groupName) {\n    var index = this.collapseGroups.indexOf(groupName);\n    if (index !== -1) {\n      this.collapseGroups.splice(index, 1);\n      storage.set(this.getCollapseKey(), JSON.stringify(this.collapseGroups));\n    }\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  componentDidUpdate: function () {\n    var modal = this.props.modal;\n    var curListLen = modal.list.length;\n    var obj = modal.getActiveObj();\n    var curActiveItem = obj.curActiveItem;\n    var groupItem = obj.groupItem;\n    if (groupItem) {\n      this.ensureVisible(false, groupItem);\n    } else if (curListLen > this.curListLen || curActiveItem !== this.curActiveItem) {\n      this.ensureVisible();\n    }\n    this.curListLen = curListLen;\n    this.curActiveItem = curActiveItem;\n    if (this.props.hide) {\n      this.refs.recycleBinDialog.hide();\n    }\n  },\n  ensureVisible: function (init, activeItem) {\n    activeItem = activeItem || this.props.modal.getActive();\n    if (activeItem) {\n      var elem = findDOMNode(this.refs[activeItem.name]);\n      var con = findDOMNode(this.refs.list);\n      util.ensureVisible(elem, con, init);\n    }\n  },\n  onClick: function (item) {\n    var self = this;\n    if (\n      typeof self.props.onActive != 'function' ||\n      self.props.onActive(item) !== false\n    ) {\n      var modal = self.props.modal;\n      modal.setActive(item.name);\n      self.setState({ activeItem: item });\n      self.expandGroup(modal.getGroupName(item.name));\n    }\n  },\n  toggleGroup: function (item) {\n    var index = this.collapseGroups.indexOf(item.name);\n    if (index === -1) {\n      this.collapseGroups.push(item.name);\n    } else {\n      this.collapseGroups.splice(index, 1);\n    }\n    storage.set(this.getCollapseKey(), JSON.stringify(this.collapseGroups));\n    this.setState({});\n  },\n  onClickGroup: function (e) {\n    var name = e.target.getAttribute('data-group');\n    var groups = this.props.modal.groups;\n    var group = groups[name];\n    if (!group) {\n      group = groups[name] = {};\n    }\n    group.expand = !group.expand;\n    this.setState({});\n  },\n  onDoubleClick: function (item, okIcon) {\n    (item.selected && !item.changed) || okIcon\n      ? this.onUnselect(item)\n      : this.onSelect(item);\n    var onDoubleClick = this.props.onDoubleClick;\n    typeof onDoubleClick == 'function' && onDoubleClick(item);\n  },\n  onSelect: function (data) {\n    var onSelect = this.props.onSelect;\n    typeof onSelect == 'function' && onSelect(data);\n  },\n  onUnselect: function (data) {\n    var onUnselect = this.props.onUnselect;\n    typeof onUnselect == 'function' && onUnselect(data);\n  },\n  onChange: function (e) {\n    var modal = this.props.modal;\n    var item = modal.getActive();\n    if (!item) {\n      return;\n    }\n    var oldValue = item.value || '';\n    var value = e.getValue() || '';\n    if (value != oldValue) {\n      var hasChanged = modal.hasChanged();\n      item.changed = true;\n      item.value = value;\n      this.setState({\n        selectedItem: item\n      });\n      if (!hasChanged) {\n        events.trigger('updateGlobal');\n      }\n    }\n  },\n  onFilterChange: function (keyword) {\n    this.props.modal.search(keyword, this.props.name != 'rules');\n    this.setState({ filterText: keyword });\n  },\n  getItemByKey: function (key) {\n    return this.props.modal.getByKey(key);\n  },\n  onDragStart: function (e) {\n    var target = getTarget(e);\n    var name = target && target.getAttribute('data-name');\n    if (name) {\n      e.dataTransfer.setData(NAME_PREFIX + name, 1);\n      e.dataTransfer.setData('-' + NAME_PREFIX, name);\n    }\n  },\n  onDragEnter: function (e) {\n    var info = getDragInfo(e);\n    if (info) {\n      curTarget = info.target;\n      curTarget.style.background = 'var(--b-active)';\n    }\n  },\n  onDragLeave: function (e) {\n    var info = getDragInfo(e);\n    if (info) {\n      info.target.style.background = '';\n    }\n  },\n  onDrop: function (e) {\n    var info = getDragInfo(e, this.refs.list);\n    e.stopPropagation();\n    if (info) {\n      var fromName = getName(e.dataTransfer.getData('-' + NAME_PREFIX));\n      var group = this.collapseGroups.indexOf(fromName) !== -1;\n      var toName = info.toName;\n      var params = {\n        from: fromName,\n        to: toName,\n        group: group\n      };\n      info.target.style.background = '';\n      var toTop = this.isRules() && toName === 'Default';\n      if (toTop) {\n        toName = this.props.modal.list[1];\n        params.to = toName;\n        params.toTop = true;\n      }\n      if (this.props.modal.moveTo(fromName, toName, group, toTop)) {\n        var name = this.props.name === 'rules' ? 'rules' : 'values';\n        dataCenter[name].moveTo(params, function (data, xhr) {\n          if (!data) {\n            util.showSystemError(xhr);\n            return;\n          }\n          if (data.ec === 2) {\n            events.trigger(name + 'Changed');\n          }\n        }\n        );\n        this.setState({});\n        this.triggerChange('move');\n      }\n    }\n  },\n  formatJson: function (item) {\n    var value = (item && item.value) || '';\n    if (/\\S/.test(value)) {\n      var json = util.parseRawJson(value);\n      if (json) {\n        json = JSON.stringify(json, null, '  ');\n        if (value !== json) {\n          item.changed = true;\n          item.value = json;\n          events.trigger('updateGlobal');\n        }\n      }\n    }\n  },\n  reloadRecycleBin: function (name) {\n    if (this.refs.recycleBinDialog.isVisible()) {\n      this._pendingRecycle = false;\n      this.showRecycleBin(name);\n    }\n  },\n  showRecycleBin: function (name) {\n    var self = this;\n    if (self._pendingRecycle) {\n      return;\n    }\n    self._pendingRecycle = true;\n    dataCenter[name.toLowerCase()].recycleList(function (data, xhr) {\n      self._pendingRecycle = false;\n      if (!data) {\n        util.showSystemError(xhr);\n        return;\n      }\n      if (!data.list.length) {\n        return message.info('Trash is empty');\n      }\n      self.refs.recycleBinDialog.show({ name: name, list: data.list });\n    });\n  },\n  getGroupByName: function(name) {\n    var modal = this.props.modal;\n    var item = modal.data[name];\n    if (!item || util.isGroup(item.name)) {\n      return item;\n    }\n    var i = modal.list.indexOf(name) - 1;\n    for (; i >= 0; i--) {\n      item = modal.data[modal.list[i]];\n      if (util.isGroup(item.name)) {\n        return item;\n      }\n    }\n  },\n  getCurGroup: function(item) {\n    item = item || this.currentFocusItem;\n    return item && this.getGroupByName(item.name);\n  },\n  onClickContextMenu: function (action, e, parentAction, menuName) {\n    var self = this;\n    var name = self.props.name === 'rules' ? 'Rules' : 'Values';\n    switch (parentAction || action) {\n    case 'Save':\n      events.trigger('save' + name, self.currentFocusItem);\n      break;\n    case 'Rename':\n      events.trigger('rename' + name, self.currentFocusItem);\n      break;\n    case 'Delete':\n      events.trigger('delete' + name, self.currentFocusItem);\n      break;\n    case 'Rule':\n      events.trigger('createRules', [self.getCurGroup(), self.currentFocusItem]);\n      break;\n    case 'Key':\n      events.trigger('createValues', [self.getCurGroup(), self.currentFocusItem]);\n      break;\n    case 'Export':\n      events.trigger('exportData');\n      break;\n    case 'Import':\n      events.trigger('showImportDialog');\n      break;\n    case 'Trash':\n      self.showRecycleBin(name);\n      break;\n    case 'Validate':\n      var item = self.currentFocusItem;\n      if (item) {\n        if (JSON_RE.test(item.value)) {\n          try {\n            JSON.parse(item.value);\n            message.success('Valid JSON object');\n          } catch (e) {\n            message.error(\n                'Warning: Invalid JSON format in the value of \\'' +\n                item.name + '\\'. ' +  e.message\n              );\n          }\n        } else {\n          message.error('Invalid JSON format');\n        }\n      }\n      break;\n    case 'Format':\n      self.formatJson(self.currentFocusItem);\n      break;\n    case 'Inspect':\n      if (self.currentFocusItem) {\n        events.trigger('showJsonViewDialog', self.currentFocusItem.value);\n      }\n      break;\n    case 'Help':\n      window.open(util.getDocUrl('gui/' + (self.props.name || 'values') + '.html'));\n      break;\n    case 'Plugins':\n      var modal = self.props.modal;\n      var activeItem = self.currentFocusItem;\n      iframes.fork(action, {\n        port: dataCenter.getPort(),\n        type: self.props.name === 'rules' ? 'rules' : 'values',\n        name: menuName,\n        list: modal && modal.getList(),\n        activeItem: activeItem,\n        selectedItem: modal && modal.getActive(),\n        setValue: function(value) {\n          value = value || '';\n          if (activeItem && value != activeItem.value) {\n            activeItem.changed = true;\n            activeItem.value = value;\n            events.trigger('updateGlobal');\n          }\n        }\n      });\n      break;\n    }\n  },\n  triggerChange: function (type) {\n    var data = this.props.modal.data;\n    var list = this.props.modal.list.map(function (name) {\n      var item = data[name];\n      return {\n        name: name,\n        value: (item && item.value) || ''\n      };\n    });\n    util.triggerListChange(this.props.name || 'values', {\n      type: type,\n      url: location.href,\n      list: list\n    });\n  },\n  isRules: function() {\n    return this.props.name == 'rules';\n  },\n  getCollapseKey: function() {\n    return this.isRules() ? 'collapseRulesGroups' : 'collapseValuesGroups';\n  },\n  onContextMenu: function (e) {\n    var name = $(e.target).closest('a').attr('data-name');\n    var modal = this.props.modal;\n    name = name && getName(name);\n    var item = modal.get(name);\n    if (!item) {\n      name = undefined;\n    }\n    this.currentFocusItem = item;\n    var disabled = !name;\n    var isDefault;\n    var isRules = this.isRules();\n    var pluginItem = isRules ? rulesCtxMenuList[8] : valuesCtxMenuList[9];\n    util.addPluginMenus(\n      pluginItem,\n      dataCenter[isRules ? 'getRulesMenus' : 'getValuesMenus'](),\n      isRules ? 7 : 8\n    );\n    if (!isRules) {\n      valuesCtxMenuList[0].list[0].name = name && util.isGroup(name) ? 'Name' : 'Key';\n    }\n    var height = (isRules ? 280 : 315) - (pluginItem.hide ? 30 : 0);\n    pluginItem.maxHeight = height + 30;\n    var data = util.getMenuPosition(e, 110, height);\n    data.className = 'w-contenxt-menu-list';\n    if (isRules) {\n      data.list = rulesCtxMenuList;\n      data.list[1].disabled = disabled;\n      data.list[1].name = 'Save';\n      if (item && !item.changed) {\n        if ((dataCenter.isMultiEnv() && name !== 'Default') || util.isGroup(name)) {\n          data.list[1].disabled = true;\n        } else {\n          data.list[1].name = item.selected ? 'Disable' : 'Enable';\n        }\n      }\n      if (item && item.isDefault) {\n        isDefault = true;\n      }\n      data.list[5].disabled = !modal.list.length;\n    } else {\n      data.list = valuesCtxMenuList;\n      data.list[1].disabled = !item || !item.changed;\n      data.list[5].disabled = disabled;\n      data.list[6].disabled = !modal.list.length;\n    }\n    var copyItem = data.list[0];\n    copyItem.disabled = disabled;\n    if (!disabled) {\n      copyItem.list[0].copyText = name;\n      if (item.value) {\n        copyItem.list[1].disabled = false;\n        copyItem.list[1].copyText = item.value;\n      } else {\n        copyItem.list[1].disabled = true;\n      }\n    }\n    data.list[3].disabled = isDefault || disabled;\n    data.list[4].disabled = isDefault || disabled;\n    this.refs.contextMenu.show(data);\n    e.preventDefault();\n  },\n  onAddRule: function (name) {\n    this.props.modal.setActive(name);\n    this.setState({});\n  },\n  enableAllRules: function () {\n    var self = this;\n    if (self._pendingEnableRules) {\n      return;\n    }\n    self._pendingEnableRules = setTimeout(function () {\n      self._pendingEnableRules = null;\n    }, 2000);\n    $('.w-enable-rules-menu').trigger('click');\n    events.trigger('disableAllRules');\n  },\n  showHttpsSettingsDialog: function() {\n    events.trigger('showHttpsSettingsDialog');\n  },\n  parseList: function() {\n    var isRules = this.isRules();\n    var modal = this.props.modal;\n    var list = modal.list;\n    var data = modal.data;\n    var group;\n    var childCount = 0;\n    var selectedCount = 0;\n    var changed;\n    var active;\n    var setStatus = function() {\n      if (group) {\n        group.changed = changed;\n        group.childCount = childCount;\n        group.selectedCount = selectedCount;\n        childCount = 0;\n        selectedCount = 0;\n        changed = false;\n      }\n    };\n    list.forEach(function(name, i) {\n      var item = data[name];\n      if (util.isGroup(item.name)) {\n        setStatus();\n        item.isGroup = true;\n        if (group) {\n          group.activeGroup = active;\n        }\n        group = item;\n        active = false;\n        group.activeGroup = false;\n      } else if (group) {\n        ++childCount;\n        changed = changed || item.changed;\n        active = active || item.active;\n        if (isRules && item.selected) {\n          ++selectedCount;\n        }\n      }\n    });\n    if (group) {\n      group.activeGroup = active;\n    }\n    setStatus();\n    return list;\n  },\n  onFormat: function(e) {\n    this.formatJson(this.props.modal.getActive());\n    e.preventDefault();\n  },\n  onInspect: function(e) {\n    var item = this.props.modal.getActive();\n    if (item) {\n      events.trigger('showJsonViewDialog', item.value);\n      e.preventDefault();\n    }\n  },\n  switchTab: function(e) {\n    var name = e.target.getAttribute('data-name');\n    if (!name || name === this.state.activeTab) {\n      return;\n    }\n    this.setState({ activeTab: name });\n  },\n  showEnabledRules: function() {\n    var self = this;\n    dataCenter.rules.getEnabledRules(function (data, xhr) {\n      if (!data) {\n        util.showSystemError(xhr);\n        return;\n      }\n      var enabledRules = [];\n      data.list.forEach(function(line) {\n        var file = line[1] ? util.SOURCE_SEP + line[1] + ')' : '';\n        enabledRules.push(line[0] + file);\n      });\n      self.refs.enabledRulesDialog.show(enabledRules);\n    });\n  },\n  render: function () {\n    var self = this;\n    var modal = self.props.modal;\n    var list = modal.list;\n    var data = modal.data;\n    var props = self.props;\n    var disabled = props.disabled;\n    var filterText = self.state.filterText;\n    var activeItem = modal.getActive(true) || '';\n    var isSub, isHide;\n    var isRules = self.isRules();\n    var draggable = false;\n    var activeName = activeItem ? activeItem.name : '';\n    var selected = activeItem.selected;\n    var enabledRulesCount = dataCenter.enabledRulesCount || 0;\n    list = self.parseList();\n    if (isRules) {\n      draggable = list.length > 2;\n      util.triggerRulesActiveChange(activeName);\n    } else if (list.length > 1) {\n      draggable = true;\n      util.triggerValuesActiveChange(activeName);\n    }\n\n    //不设置height为0，滚动会有问题\n    return (\n      <div className={'v-box fill' +\n        (selected && isRules ? ' w-has-selected-rules' : '') +\n        (disabled ? ' w-has-selected-disabled' : '') +\n        (props.hide ? ' hide' : '')}>\n        {disabled || (dataCenter.needEnableHttps() && !hideEnableHTTPSTips && !dataCenter.isPureProxy()) ? (\n          <div className=\"w-record-status\">\n            {disabled ? 'All rules are currently disabled' :\n            'Full functionality of the rules requires activation of the \\'Enable HTTPS (Capture Tunnel Traffic)\\' option'}\n            <button className=\"btn btn-primary\" onClick={disabled ? self.enableAllRules : self.showHttpsSettingsDialog}>\n              {disabled ? 'Enable' : 'Settings'}\n            </button>\n          </div>\n        ) : null}\n        <Divider leftWidth=\"230\">\n          <div className=\"fill v-box w-list-left\">\n            {isRules ? <div className={'w-enabled-rules-btn' + (enabledRulesCount ? '' : ' w-disabled')} onClick={enabledRulesCount ? self.showEnabledRules : null}>\n              <Icon name=\"ok\" data-name=\"enabledRules\" />\n              Enabled Rules ({enabledRulesCount})\n            </div> : null}\n            <div\n              ref=\"list\"\n              tabIndex=\"0\"\n              onContextMenu={this.onContextMenu}\n              onDrop={self.onDrop}\n              className={\n                'fill v-box w-list-data ' +\n                (props.className || '') +\n                (disabled ? ' w-disabled' : '')\n              }\n              style={{ background: filterText ? 'var(--b-filtered)' : undefined }}\n            >\n              {list.map(function (name, i) {\n                var item = data[name];\n                var isDefaultRule = isRules && i === 0;\n                var isGroup = item.isGroup;\n                var title = isGroup ? name.substring(1) : name;\n                isSub = isSub || isGroup;\n                if (isGroup) {\n                  isHide = !filterText && self.collapseGroups.indexOf(name) !== -1;\n                }\n                return (\n                  <a\n                    tabIndex=\"0\"\n                    ref={name}\n                    data-name={i + '_' + name}\n                    onDragStart={isDefaultRule ? undefined : self.onDragStart}\n                    onDragEnter={self.onDragEnter}\n                    onDragLeave={self.onDragLeave}\n                    onDrop={self.onDrop}\n                    style={{ display: item.hide ? 'none' : null }}\n                    key={item.key}\n                    data-key={item.key}\n                    title={title}\n                    draggable={isDefaultRule ? false : draggable}\n                    onClick={function () {\n                      isGroup ? self.toggleGroup(item) : self.onClick(item);\n                    }}\n                    onDoubleClick={isGroup ? null : function (e) {\n                      self.onDoubleClick(item);\n                      e.preventDefault();\n                    }}\n                    className={util.getClasses({\n                      'w-active': !isGroup && item.active,\n                      'w-changed': item.changed,\n                      'w-selected': !isGroup && item.selected,\n                      'w-list-group': isGroup,\n                      'w-list-sub': !isGroup && isSub,\n                      'w-list-group-active': isGroup && item.activeGroup,\n                      'w-hide': !isGroup && isHide,\n                      'w-group-empty': isGroup && !item.childCount\n                    })}\n                  >\n                    {isGroup ? <Icon name={'triangle-' + (isHide ? 'right' : 'bottom')} /> : null}\n                    {title}\n                    {isGroup ? <span className={util.getClasses({\n                      'w-group-child-num': true,\n                      'w-exists-selected': item.selectedCount > 0\n                    })}>({item.selectedCount > 0 ? item.selectedCount + '/' : ''}{item.childCount})</span> : <Icon name=\"ok\" />}\n                  </a>\n                );\n              })}\n            </div>\n            <FilterInput ref=\"filterInput\" onChange={this.onFilterChange} />\n            <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n            <RecycleBinDialog ref=\"recycleBinDialog\" />\n            <EnabledRulesDialog ref=\"enabledRulesDialog\" />\n          </div>\n          <Editor\n            {...self.props}\n            onChange={self.onChange}\n            readOnly={!activeItem || activeItem.hide || disabledEditor}\n            value={activeItem.hide ? '' : activeItem.value}\n            mode={isRules ? 'rules' : getSuffix(activeItem.name)}\n            onFormat={isRules ? null : this.onFormat}\n            onInspect={isRules ? null : this.onInspect}\n          />\n        </Divider>\n      </div>\n    );\n  }\n});\n\nmodule.exports = List;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/menu-item.js",
    "content": "require('../css/menu-item.css');\nvar React = require('react');\nvar util = require('./util');\nvar GitHubIcon = require('./github-icon');\nvar Icon = require('./icon');\n\nvar MenuItem = React.createClass({\n  preventBlur: function (e) {\n    e.preventDefault();\n  },\n  stopPropagation: function (e) {\n    e.stopPropagation();\n  },\n  render: function () {\n    var self = this;\n    var options = self.props.options;\n    if (options && !options.length) {\n      options = null;\n    }\n    var name = self.props.name;\n    var onClick = self.props.onClick || util.noop;\n    var onClickOption = self.props.onClickOption || util.noop;\n    var onDoubleClickOption = self.props.onDoubleClickOption || util.noop;\n    var checkedOptions = self.props.checkedOptions || {};\n    var disabled = self.props.disabled;\n\n    return (\n      <div\n        onBlur={self.props.onBlur}\n        tabIndex=\"0\"\n        onMouseDown={self.preventBlur}\n        style={{ display: util.getBool(self.props.hide) ? 'none' : 'block' }}\n        className={\n          'w-menu-item ' +\n          (self.props.className || '') +\n          (disabled ? ' w-disabled' : '')\n        }\n      >\n        {options ? (\n          <div\n            className=\"w-menu-options\"\n            style={{ border: name ? null : 'none' }}\n          >\n            {options.map(function (option) {\n              return (\n                <a\n                  key={option.name}\n                  className={option.disabled ? 'w-disabled' : undefined}\n                  title={option.title}\n                  onClick={function (e) {\n                    if (option.disabled) {\n                      return;\n                    }\n                    onClickOption(option, e);\n                  }}\n                  onDoubleClick={function () {\n                    onDoubleClickOption(option);\n                  }}\n                  href={option.href || undefined}\n                  target={option.href ? option.target || '_blank' : undefined}\n                  draggable=\"false\"\n                >\n                  {option.icon == 'checkbox' ? (\n                    <input\n                      type=\"checkbox\"\n                      disabled={disabled}\n                      data-name={option.name}\n                      onClick={self.stopPropagation}\n                      onChange={self.props.onChange}\n                      checked={!checkedOptions[option.name]}\n                    />\n                  ) : option.icon === false ? undefined : (\n                    option.icon === 'github' ? <GitHubIcon /> :\n                    <Icon\n                      name={option.icon || 'asterisk'}\n                      className={option.icon ? '' : 'w-hidden'}\n                    />\n                  )}\n                  {option.name}\n                </a>\n              );\n            })}\n          </div>\n        ) : (\n          ''\n        )}\n        {name ? (\n          typeof name === 'string' ? (\n            <a onClick={onClick} className=\"w-menu-open\" draggable=\"false\">\n              <Icon name={self.props.icon || 'folder-open'} />\n              {name}\n            </a>\n          ) : (\n            name\n          )\n        ) : (\n          ''\n        )}\n      </div>\n    );\n  }\n});\n\nmodule.exports = MenuItem;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/message.js",
    "content": "var $ = require('jquery');\nrequire('../css/message.css');\n\nvar cache = {};\n\n$(document).on('click', '.w-message', function () {\n  $(this).stop(true, true).hide();\n});\n\nfunction showMessage(msg, level) {\n  if (level === 'warn') {\n    level = 'warning';\n  } else if (level === 'error') {\n    level = 'danger';\n  }\n  var elem = cache[level];\n  if (!elem) {\n    elem = $('<div class=\"alert alert-' + level + ' w-message\"></div>');\n    cache[level] = elem;\n  }\n  elem.appendTo(document.body);\n  elem.text(msg);\n  elem.stop(true, true).show();\n  elem.css('marginLeft', -elem[0].offsetWidth / 2);\n  elem.delay(2000).fadeOut(1600);\n  return elem;\n}\n\n['error', 'warn', 'info', 'success'].forEach(function (level) {\n  exports[level] = function (msg) {\n    return showMessage(msg, level);\n  };\n});\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/mock-dialog.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar Dialog = require('./dialog');\nvar CopyBtn = require('./copy-btn');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar getGroupRules = require('./protocols').getGroupRules;\nvar win = require('./win');\nvar message = require('./message');\nvar events = require('./events');\nvar PropsEditor = require('./props-editor');\nvar Icon = require('./icon');\nvar HelpIcon = require('./help-icon');\nvar CloseBtn = require('./close-btn');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar MAX_LEN = 64;\nvar fakeIframe = 'javascript:\"<style>html,body{padding:0;margin:0}</style><textarea></textarea>\"';\nvar HIDE_STYLE = { display: 'none' };\nvar iframeStyle = {\n  padding: 0,\n  border: 'none',\n  width: 840,\n  height: 360,\n  margin: 0,\n  verticalAlign: 'top'\n};\nvar VAL_SEP_RE = /^\\s*?`{3,}\\s*?$/mg;\nvar INLINE_PROTOCOLS = ['http://', 'https://', 'ws://', 'wss://', 'tunnel://', 'redirect://', 'locationHref://', 'statusCode://', 'style://',\n'pipe://', 'host://', 'xhost://', 'proxy://', 'xproxy://', 'http-proxy://', 'xhttp-proxy://', 'https-proxy://', 'xhttps-proxy://',\n'socks://', 'xsocks://', 'pac://', 'weinre://', 'log://', 'excludeFilter://', 'includeFilter://', 'ignore://', 'skip://', 'enable://',\n'disable://', 'delete://', 'method://', 'replaceStatus://', 'referer://', 'auth://', 'ua://', 'cache://', 'attachment://', 'forwardedFor://',\n'responseFor://', 'reqDelay://', 'resDelay://', 'reqSpeed://', 'resSpeed://', 'reqType://', 'resType://', 'reqCharset://', 'resCharset://',\n'reqWrite://', 'resWrite://', 'reqWriteRaw://', 'resWriteRaw://', 'sniCallback://', 'lineProps://'];\nvar COMMON_OPS = ['Map Local', 'Map Remote', 'DNS Spoofing', 'Modify URL', 'Modify Method', 'Request Headers', 'Response Headers',\n  'Request Body', 'Response Body', 'Response 404', 'Response 500', 'Abort Request', 'Abort Response', 'Delay Request', 'Delay Response'];\nvar HTTP_PROTOCOLS = ['http://', 'https://', 'ws://', 'wss://'];\nvar HTTP_RE = /^(?:http|ws)s?:\\/\\//;\nvar JSON_STR = '{\\n  \\n}';\nvar IPV6_RE = /^\\[([\\w:]+)\\](:\\d{1,5})?$/;\nvar HOST_RE = /^[a-z][a-z\\d_:-]+(?:\\.[a-z][a-z\\d_:-]+)+?$/;\n\nfunction getDefaultValue(value) {\n  var val = value && value.trim();\n  if (!val || val[0] !== '{' || val[val.length - 1] !== '}') {\n    return JSON_STR;\n  }\n  return value;\n}\n\nfunction getStyle(show) {\n  return show ? null : HIDE_STYLE;\n}\n\nfunction getDefaultHost(value) {\n  value = value && value.trim() || '';\n  if (value === 'localhost' || HOST_RE.test(value) || IPV6_RE.test(value)) {\n    return value;\n  }\n  return '127.0.0.1:8080';\n}\n\nfunction trim(str) {\n  return str ? str.trim() : '';\n}\n\nfunction wrapValue(name, sep, str) {\n  return '\\n' + sep + ' ' + name + '\\n' + str + '\\n' + sep;\n}\n\nfunction getInlineValue(name, str) {\n  name = trim(name);\n  if (!name || !str) {\n    return '';\n  }\n  var sep = '```';\n  if(str.indexOf(sep) === -1) {\n    return wrapValue(name, sep, str);\n  }\n  var list = str.match(VAL_SEP_RE).map(trim);\n  for (var i = 0; i < 11; i++) {\n    if (list.indexOf(sep) === -1) {\n      return wrapValue(name, sep, str);\n    }\n    sep += '`';\n  }\n\n  return wrapValue(name, '````````````', str);\n}\n\nfunction getValue(item, key) {\n  var req = item.req;\n  var res = item.res || '';\n  switch(key) {\n  case 'blank':\n    return '';\n  case 'url':\n    return item.url;\n  case 'method':\n    return req.method;\n  case 'reqHeaders':\n    return JSON.stringify(req.headers, null, '  ');\n  case 'resHeaders':\n    return item.res.headers ? JSON.stringify(res.headers, null, '  ') : '';\n  case 'reqBody':\n    return util.getBody(req, true);\n  case 'resBody':\n    return util.getBody(res);\n  case 'reqJson':\n    return util.getJsonStr(req, true, decodeURIComponent);\n  case 'resJson':\n    return util.getJsonStr(res);\n  case 'reqRaw':\n    return util.getReqRawHeaders(item) + '\\r\\n\\r\\n' + util.getBody(req, true);\n  case 'resRaw':\n    return util.getResRawHeaders(item) + '\\r\\n\\r\\n' + util.getBody(res);\n  case 'statusCode':\n    return res.statusCode;\n  }\n  return '';\n}\n\nfunction isValue(str) {\n  return str[0] === '(' && str[str.length - 1] === ')';\n}\n\nvar MockDialog = React.createClass({\n  getInitialState: function () {\n    return {\n      rules: '',\n      dataSrc: 'resBody',\n      valueType: 'file',\n      protocol: 'file://',\n      inlineValue: '',\n      comment: ''\n    };\n  },\n  show: function (item, dataSrc) {\n    if (!item) {\n      return;\n    }\n    var headers = item.res.headers;\n    var type = util.getContentType(headers);\n    var keyName = util.getFilename(item, true) || '';\n    dataSrc = dataSrc || 'resBody';\n    type = type ? type.toLowerCase() : '';\n    if (type === 'img') {\n      type = headers['content-type'];\n      type = type.split('/')[1].split(';')[0].trim().toLowerCase().replace(/^x-/i, '');\n    } else if (type === 'text') {\n      type = 'txt';\n    } else if (!type && /\\.([\\w-]+)$/.test(keyName)) {\n      type = RegExp.$1;\n    }\n    type = type ? '.' + type : '';\n    this.refs.mockDialog.show();\n    this.setState({\n      hasChanged: false,\n      item: item,\n      pattern: item.url,\n      dataSrc: dataSrc,\n      suffixType: type,\n      valueType: 'file',\n      keyName: keyName.substring(0, MAX_LEN),\n      inlineKey: 'mock_' + util.getTempName() + type,\n      protocol:dataSrc === 'resRaw' ? 'rawfile://' : 'file://'\n    }, this.updateRules);\n    var url = findDOMNode(this.refs.url);\n    setTimeout(function() {\n      url.select();\n      url.focus();\n    }, 600);\n    this._textarea.value = getValue(item, dataSrc);\n  },\n  onValueTypeChange: function(e) {\n    var value = e.target.value;\n    var protocol = this.state.protocol;\n    value = INLINE_PROTOCOLS.indexOf(protocol) === -1 ? value : 'inline';\n    if (value === 'key' && !this.isValuesKey()) {\n      util.shakeElem($(findDOMNode(this.refs.keyName)));\n    }\n    this.setState({\n      valueType: value\n    }, this.updateRules);\n  },\n  onProtoChange: function(e) {\n    var protocol = e.target.value;\n    var curValue = this.state.inlineValue;\n    switch (protocol) {\n    case 'Abort Request':\n      this.setState({\n        protocol: 'enable://',\n        valueType: 'inline',\n        inlineValue: 'abort'\n      }, this.updateRules);\n      break;\n    case 'Abort Response':\n      this.setState({\n        protocol: 'enable://',\n        valueType: 'inline',\n        inlineValue: 'abortRes'\n      }, this.updateRules);\n      break;\n    case 'Delay Request':\n      this.setState({\n        protocol: 'reqDelay://',\n        valueType: 'inline',\n        inlineValue: curValue > 0 ? curValue : '5000'\n      }, this.updateRules);\n      break;\n    case 'Delay Response':\n      this.setState({\n        protocol: 'resDelay://',\n        valueType: 'inline',\n        inlineValue: curValue > 0 ? curValue : '5000'\n      }, this.updateRules);\n      break;\n    case 'Modify URL':\n      this.setState({\n        protocol: 'urlParams://',\n        valueType: 'inline',\n        inlineValue: getDefaultValue(curValue)\n      }, this.updateRules);\n      break;\n    case 'Request Headers':\n      this.setState({\n        protocol: 'reqHeaders://',\n        valueType: 'inline',\n        inlineValue: getDefaultValue(curValue)\n      }, this.updateRules);\n      break;\n    case 'Response Headers':\n      this.setState({\n        protocol: 'resHeaders://',\n        valueType: 'inline',\n        inlineValue: getDefaultValue(curValue)\n      }, this.updateRules);\n      break;\n    case 'Request Body':\n      this.setState({\n        protocol: 'reqMerge://',\n        valueType: 'inline',\n        inlineValue: getDefaultValue(curValue)\n      }, this.updateRules);\n      break;\n    case 'Response Body':\n      this.setState({\n        protocol: 'resMerge://',\n        valueType: 'inline',\n        inlineValue: getDefaultValue(curValue)\n      }, this.updateRules);\n      break;\n    case 'Response 404':\n      this.setState({\n        protocol: 'statusCode://',\n        valueType: 'inline',\n        inlineValue: '404'\n      }, this.updateRules);\n      break;\n    case 'Response 500':\n      this.setState({\n        protocol: 'statusCode://',\n        valueType: 'inline',\n        inlineValue: '500'\n      }, this.updateRules);\n      break;\n    case 'DNS Spoofing':\n      this.setState({\n        protocol: 'host://',\n        valueType: 'inline',\n        inlineValue: getDefaultHost(curValue)\n      }, this.updateRules);\n      break;\n    case 'Map Remote':\n      this.setState({\n        protocol: 'https://',\n        valueType: 'inline',\n        inlineValue: ''\n      }, this.updateRules);\n      break;\n    case 'Modify Method':\n      this.setState({\n        protocol: 'method://',\n        valueType: 'inline',\n        inlineValue: ''\n      }, this.updateRules);\n      break;\n    case 'Map Local':\n      this.setState({\n        protocol: 'file://',\n        valueType: 'inline',\n        inlineValue: ''\n      }, this.updateRules);\n      break;\n    default:\n      this.setState({\n        protocol: protocol\n      }, this.updateRules);\n    }\n  },\n  getValues: function() {\n    var valueType = this.getValueType();\n    var isFile = valueType === 'file';\n    if (!isFile && valueType !== 'key') {\n      return;\n    }\n    var values = {\n      isFile: isFile,\n      name: this.getKeyName()\n    };\n    var value = this._textarea.value;\n    var dataSrc = this.state.dataSrc;\n    var item = this.state.item;\n    if (isFile && value === getValue(item, dataSrc)) {\n      if (dataSrc === 'reqBody') {\n        values.base64 = item.req.base64;\n      } else if (dataSrc === 'resBody') {\n        values.base64 = item.res.base64;\n      }\n    }\n    if (!values.base64) {\n      values.value = value;\n    }\n    return values;\n  },\n  export: function() {\n    var data = [this.wrapComment()];\n    var values = this.getValues();\n    if (values) {\n      data.push(values);\n    }\n    events.trigger('showExportDialog', ['mock', data]);\n  },\n  isValuesKey: function(dataSrc) {\n    return (dataSrc || this.state.dataSrc)[0] === '{';\n  },\n  selectAllText: function(e) {\n    e.target.select();\n  },\n  updateRules: function() {\n    var state = this.state;\n    var pattern = trim(state.pattern);\n    if (!pattern) {\n      return this.setState({ rules: '' });\n    }\n    var protocol = state.protocol || 'file://';\n    var rules = pattern + ' ' + protocol;\n    var valueType = this.getValueType();\n    var suffixType = state.suffixType;\n    if (valueType === 'file') {\n      rules += 'temp/current_file_hash_placeholder' + (suffixType || '');\n    } else if (valueType === 'key') {\n      rules += this.isValuesKey() ? state.dataSrc : (state.keyName ? '{' + state.keyName + '}' : '');\n    } else {\n      var inlineValue = state.inlineValue;\n      if (!/\\s/.test(inlineValue)) {\n        if (HTTP_PROTOCOLS.indexOf(protocol) !== -1 && HTTP_RE.test(inlineValue)) {\n          rules = pattern + ' ' + inlineValue;\n        } else {\n          rules += inlineValue;\n        }\n      } else {\n        inlineValue = getInlineValue(state.inlineKey, inlineValue);\n        rules += (inlineValue ? '{' + state.inlineKey + '}' : '') + inlineValue;\n      }\n    }\n    this.setState({ rules: rules });\n  },\n  onPatternChange: function(e) {\n    var pattern = e.target.value.replace(/\\s+/g, '');\n    this.setState({ pattern: pattern }, this.updateRules);\n  },\n  onInlineValueChange: function(e) {\n    var inlineValue = e.target.value;\n    this.setState({ inlineValue: inlineValue }, this.updateRules);\n  },\n  onKeyNameChange: function(e) {\n    var keyName = e.target.value.replace(/\\s+/g, '');\n    this.setState({ keyName: keyName }, this.updateRules);\n  },\n  onSourceChange: function(e) {\n    var self = this;\n    var dataSrc = e.target.value;\n    var updateValue = function() {\n      self.setState({ dataSrc: dataSrc }, self.updateRules);\n      if (self.isValuesKey(dataSrc)) {\n        var valuesModal = dataCenter.valuesModal;\n        var item = valuesModal.getItem(dataSrc.slice(1, -1));\n        self._textarea.value = item && item.value || '';\n      } else {\n        self._textarea.value = getValue(self.state.item, dataSrc);\n      }\n    };\n    if (!self.state.hasChanged) {\n      return updateValue();\n    }\n    win.confirm('Unsaved changes will be lost. Continue?', function(sure) {\n      if (sure) {\n        updateValue();\n        self.setState({ hasChanged: false });\n      }\n    });\n  },\n  componentDidMount: function() {\n    var self = this;\n    var iframe = findDOMNode(self.refs.iframe);\n    var initTextArea = function() {\n      var textarea = iframe.contentWindow.document.querySelector('textarea');\n      var style = textarea && textarea.style;\n      self._textarea = textarea;\n      if (style) {\n        style.resize = 'none';\n        style.border = 'none';\n        style.width = iframeStyle.width + 'px';\n        style.height = iframeStyle.height + 'px';\n        style.padding = '5px';\n        style.border = '1px solid var(--c-border, #ccc)';\n        style.borderRadius = '3px';\n        textarea.maxLength = 1024 * 1024 * 3;\n        textarea.placeholder='Enter value';\n        textarea.addEventListener('input', function() {\n          self.setState({ hasChanged: true });\n        });\n        textarea.addEventListener('focus', self.hideParams);\n        textarea.onkeydown = function(e) {\n          if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) {\n            e.preventDefault();\n          }\n          util.handleFormat(e, self.formatValue);\n          util.handleTab(e);\n        };\n      }\n    };\n    iframe.onload = initTextArea;\n    initTextArea();\n    events.on('hideMockDialog', function() {\n      self.refs.mockDialog.hide();\n    });\n    events.on('addMockRulesSuccess', function() {\n      if (self.getValueType() === 'file') {\n        self.state.comment = '';\n      }\n    });\n    $(document).on('click mousedown', function(e) {\n      var target = $(e.target);\n      if (!(target.closest('.w-com-params').length ||\n        target.closest('.w-com-params-editor').length ||\n        target.closest('.w-com-dialog').length ||\n        target.closest('.w-win-dialog').length)) {\n        self.hideParams();\n      }\n    });\n  },\n  formatValue: function() {\n    var textarea = this._textarea;\n    try {\n      var val = textarea.value.trim();\n      if (val[0] === '{' || val[0] === '[') {\n        var formattedVal = JSON.stringify(JSON.parse(val), null, '  ');\n        if (textarea.value !== formattedVal) {\n          textarea.value = formattedVal;\n          this.setState({ hasChanged: true });\n        }\n      }\n    } catch (e) {\n      message.error(e.message);\n    }\n  },\n  clearValue: function() {\n    this._textarea.value = '';\n  },\n  removeRules: function() {\n    var self = this;\n    win.confirm('Do you confirm the deletion of the rules?', function(sure) {\n      sure && self.setState({ pattern: '' }, self.updateRules);\n    });\n  },\n  showParams: function() {\n    var url = findDOMNode(this.refs.url).value.replace(/#.*$/, '');\n    var index = url.indexOf('?');\n    var hasQuery = index !== -1;\n    var query = hasQuery ? url.substring(index + 1) : '';\n    var params = util.parseQueryString(query, null, null, decodeURIComponent);\n    this.refs.paramsEditor.update(params);\n    if (this.state.showParams) {\n      this.setState({ hasQuery: hasQuery });\n    } else {\n      this.setState({ showParams: true, hasQuery: hasQuery });\n    }\n  },\n  hideParams: function() {\n    if (this.state.showParams) {\n      this.setState({ showParams: false });\n    }\n  },\n  toggleParams: function() {\n    if (this.state.showParams) {\n      this.hideParams();\n    } else {\n      this.showParams();\n    }\n  },\n  clearQuery: function() {\n    var self = this;\n    win.confirm('Do you confirm the deletion of all params?', function(sure) {\n      if (sure) {\n        self.refs.paramsEditor.clear();\n        self.hideParams();\n      }\n    });\n  },\n  addQueryParam: function() {\n    this.refs.paramsEditor.onAdd();\n  },\n  onParamsChange: function () {\n    var query = this.refs.paramsEditor.toString();\n    var elem = findDOMNode(this.refs.url);\n    this.setState({\n      hasQuery: !!query,\n      pattern: util.replacQuery(elem.value, query)\n    }, this.updateRules);\n  },\n  getValueType: function() {\n    var valueType = this.state.valueType;\n    if (valueType !== 'file') {\n      return valueType;\n    }\n    var protocol = this.state.protocol;\n    return INLINE_PROTOCOLS.indexOf(protocol) === -1 ? valueType : 'inline';\n  },\n  save: function(force) {\n    var self = this;\n    var rules = self.state.rules;\n    var keyName = self.getKeyName();\n    var valueType = self.getValueType();\n    var showKeyValue = valueType === 'key';\n    var next = function(sure) {\n      if (!sure) {\n        return;\n      }\n      if (rules && force !== true) {\n        return events.trigger('showRulesDialog', {\n          rules: self.wrapComment(),\n          values: self.getValues()\n        });\n      }\n      self.saveValue();\n    };\n    if (showKeyValue) {\n      if (!keyName) {\n        findDOMNode(self.refs.keyName).focus();\n        return message.error('The key name is required');\n      }\n      if (force === true) {\n        var item = dataCenter.getValuesModal().getItem(keyName);\n        if (item && item.value !== self._textarea.value) {\n          return win.confirm('The name \\'' + keyName + '\\' is already in use. Overwrite?', next);\n        }\n      }\n    }\n    next(true);\n  },\n  getKeyName: function() {\n    var state = this.state;\n    if (this.isValuesKey()) {\n      return state.dataSrc.slice(1, -1);\n    }\n    return state.keyName;\n  },\n  saveValueOnly: function() {\n    if (this.getValueType() !== 'key' && !this.isValuesKey()) {\n      util.shakeElem($(findDOMNode(this.refs.valueType)));\n      return message.info('Switch to \\'Key Value\\' mode, and set the corresponding key name');\n    }\n    this.save(true);\n  },\n  saveValue: function() {\n    var self = this;\n    var value = self._textarea.value;\n    var filename = self.getKeyName();\n    dataCenter.values.add({\n      name: filename,\n      value: value\n    }, function (result, xhr) {\n      if (result && result.ec === 0) {\n        events.trigger('addNewValuesFile', {\n          filename: filename,\n          data: value\n        });\n        self.refs.mockDialog.hide();\n      } else {\n        util.showSystemError(xhr);\n      }\n    }\n    );\n  },\n  trimInline: function() {\n    var inlineValue = this.state.inlineValue;\n    this.setState({\n      inlineValue: inlineValue.trim()\n    }, this.updateRules);\n  },\n  clearInline: function() {\n    this.setState({ inlineValue: '' }, this.updateRules);\n  },\n  asValue: function() {\n    var inlineValue = this.state.inlineValue;\n    if (/\\s/.test(inlineValue)) {\n      return;\n    }\n    if (!isValue(inlineValue)) {\n      inlineValue = '(' + inlineValue + ')';\n    }\n    this.setState({ inlineValue: inlineValue }, this.updateRules);\n  },\n  valueNotChanged: function() {\n    return this.isValuesKey() ? !this.state.hasChanged : this.getValueType() !== 'key';\n  },\n  onComment: function(e) {\n    this.setState({ comment: e.target.value });\n  },\n  wrapComment: function() {\n    var state = this.state;\n    var rules = state.rules;\n    if (!rules || this.getValueType() !== 'file') {\n      return rules;\n    }\n    var comment = state.comment.trim();\n    return comment ? '# ' + comment + '\\n' + rules : rules;\n  },\n  render: function () {\n    var state = this.state;\n    var valueType = this.getValueType();\n    var isFile = valueType === 'file';\n    var showKeyValue = valueType !== 'inline';\n    var inlineValue = state.inlineValue;\n    var rules = state.rules;\n    var showParams = state.showParams;\n    var hasQuery = state.hasQuery;\n    var dataSrc = state.dataSrc || '';\n    var preStyle = getStyle(rules);\n    var protoList = getGroupRules();\n    var valuesModal = dataCenter.getValuesModal();\n    rules = this.wrapComment();\n\n    return (\n      <Dialog ref=\"mockDialog\" wstyle=\"w-mock-dialog\">\n        <div className={'modal-body' + (isFile ? ' w-mock-file' : '')}>\n          <CloseBtn />\n          <div className=\"w-mock-row\">\n              <span>\n                <HelpIcon docsUrl=\"rules/pattern.html\" />\n                URL Pattern:\n              </span>\n              <input ref=\"url\" onChange={this.onPatternChange} onFocus={this.selectAllText} placeholder=\"Enter url pattern\"\n                value={state.pattern} className=\"form-control w-url-pattern\" maxLength=\"1200\" />\n                <button\n                  className=\"btn btn-default w-com-params\"\n                  onClick={this.toggleParams}\n                >\n                  Params\n                </button>\n          </div>\n          <div className=\"w-mock-row\">\n            <span>Operation:</span>\n            <select className=\"form-control w-mock-protocol\" onChange={this.onProtoChange} value={state.protocol}>\n              {\n                COMMON_OPS.map(function(proto) {\n                  return <option key={proto} value={proto}>{proto}</option>;\n                })\n              }\n              <option disabled>─────────</option>\n              {protoList.map(function(item) {\n                var list = item[1];\n                return list.length ? <optgroup key={item[0]} label={item[0]}>\n                  {\n                    list.map(function(proto) {\n                      return <option key={proto} value={proto}>{proto}</option>;\n                    })\n                  }\n                </optgroup> : null;\n              })}\n            </select>\n            <select ref=\"valueType\" onChange={this.onValueTypeChange} value={valueType} className=\"form-control w-mock-value-options\">\n            <option value=\"file\">System File</option>\n              <option value=\"key\">Key Value</option>\n              <option value=\"inline\">Inline Value</option>\n            </select>\n            <select className=\"form-control w-mock-value-type\" onChange={this.onSourceChange} value={dataSrc} style={getStyle(showKeyValue)} title={dataSrc}>\n              <option value=\"blank\">Blank</option>\n              <option value=\"url\">URL</option>\n              <option value=\"method\">Method</option>\n              <option value=\"statusCode\">Status Code</option>\n              <option value=\"reqHeaders\">Request Headers</option>\n              <option value=\"resHeaders\">Response Headers</option>\n              <option value=\"reqBody\">Request Body</option>\n              <option value=\"resBody\">Response Body</option>\n              <option value=\"reqJson\">Request JSON</option>\n              <option value=\"resJson\">Response JSON</option>\n              <option value=\"reqRaw\">Request Raw</option>\n              <option value=\"resRaw\">Response Raw</option>\n              {\n                valuesModal.list.map(function(key) {\n                  key = '{' + key + '}';\n                  return <option value={key}>{key}</option>;\n                })\n              }\n            </select>\n            <label className=\"w-mock-comment\">\n              # Comment:\n              <input\n                className=\"form-control\"\n                placeholder=\"Enter rule comment\"\n                value={state.comment}\n                onChange={this.onComment}\n                maxLength={32}\n              />\n            </label>\n            <textarea onChange={this.onInlineValueChange} className=\"w-mock-inline\" placeholder=\"Enter value\"\n              style={getStyle(!showKeyValue)} maxLength=\"1200\" value={inlineValue} />\n            <div style={getStyle(!showKeyValue && inlineValue)} className=\"w-mock-action\">\n              <a onClick={this.asValue} style={getStyle(!/\\s/.test(inlineValue) && !isValue(inlineValue))}>AsValue</a>\n              <a onClick={this.trimInline} style={getStyle(/^\\s|\\s$/.test(inlineValue))}>Trim</a>\n              <a onClick={this.clearInline}>Clear</a>\n            </div>\n            <input ref=\"keyName\" onChange={this.onKeyNameChange} placeholder=\"Enter key name\"\n              value={this.isValuesKey() ? dataSrc.slice(1, -1) : state.keyName}\n              readOnly={this.isValuesKey()} onFocus={this.selectAllText} className=\"form-control w-mock-key-name\"\n              style={getStyle(showKeyValue && !isFile)} maxLength={MAX_LEN} />\n          </div>\n          <div className=\"w-mock-row\" style={getStyle(showKeyValue)}>\n            <span>\n              Value:\n            </span>\n            <div className=\"w-fake-iframe w-fix-drag\"><iframe ref=\"iframe\" data-type=\"fake\" onLoad={dataCenter.handleIframeLoad}\n              src={fakeIframe} style={iframeStyle}/></div>\n            <div style={getStyle(showKeyValue)} className=\"w-mock-action\">\n              <a onClick={this.formatValue}>Format</a>\n              <a onClick={this.clearValue}>Clear</a>\n            </div>\n          </div>\n          <div className=\"w-mock-row\" style={preStyle}>\n              <span style={{marginTop: 8}}>\n                Rules:\n              </span>\n              <pre className=\"w-mock-preview\">\n                {rules}\n              </pre>\n              <Icon name=\"trash\" onClick={this.removeRules} />\n              <div className=\"w-mock-rules-action\">\n                <CopyBtn value={rules} />\n              </div>\n          </div>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-info\"\n            onClick={this.export}\n            disabled={!rules}\n          >\n            Export\n          </button>\n          {\n            showKeyValue ? <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              onClick={this.saveValueOnly}\n            >\n              Save As Values\n            </button> : null\n          }\n          {\n          isFile ?\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={this.save}\n              disabled={!rules}\n              title={rules ? '' : 'Please enter URL Pattern first'}\n            >\n              Save As Rules\n            </button> : <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={this.save}\n              disabled={!rules && this.valueNotChanged()}\n            >\n              {rules ? 'Save As Rules' + (!showKeyValue || this.valueNotChanged()  ? '' : ' & Values') : 'Save As Values'}\n            </button>\n          }\n        </div>\n        <div\n            className={'w-layer w-com-params-editor v-box' + (showParams ? '' : ' hide')}\n          >\n            <div className=\"w-filter-bar\">\n              <CloseBtn onClick={this.hideParams} className=\"w-close-params\" />\n              <a style={{display: hasQuery ? null : 'none'}} className=\"w-params-clear-btn\" onClick={this.clearQuery}>\n                <Icon name=\"trash\" />Clear\n              </a>\n              <a onClick={this.addQueryParam}>\n                +Param\n              </a>\n            </div>\n            <PropsEditor\n              ref=\"paramsEditor\"\n              onChange={this.onParamsChange}\n            />\n          </div>\n      </Dialog>\n    );\n  }\n});\n\nvar MockDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (text, dataSrc) {\n    this.refs.mockDialog.show(text, dataSrc);\n  },\n  render: function () {\n    return <MockDialog ref=\"mockDialog\" />;\n  }\n});\n\nmodule.exports = MockDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/modal.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar CloseBtn = require('./close-btn');\nrequire('../css/modal.css');\n\nvar GLOBAL_VAR =\n  '__WHISTLE_MODAL_' +\n  Date.now() +\n  '_' +\n  Math.floor(Math.random() * 1000) +\n  '_';\nvar flag = 0;\n\nfunction getFlag() {\n  if (++flag > 100) {\n    flag = 0;\n  }\n  return flag;\n}\n\nfunction removeWinField(name) {\n  try {\n    delete window[name];\n  } catch (e) {\n    window[name] = undefined;\n  }\n}\n\nfunction createModal(options, callback, gVarName) {\n  var container = document.createElement('div');\n  document.body.appendChild(container);\n  if (options.methods) {\n    window[gVarName] = options.methods;\n  }\n  ReactDOM.render(\n    <Dialog\n      width={options.width}\n      height={options.height}\n      fullCustom={options.fullCustom}\n      wclassName=\"w-dialog-for-plguin\"\n      customRef={function (d) {\n        document.body.removeChild(container);\n        initModal(d, options, gVarName);\n        callback(d);\n      }}\n      onClose={options.onClose}\n      onShow={options.onShow}\n    >\n      {options.fullCustom ? (\n        <CloseBtn />\n      ) : (\n        <div className=\"modal-header\">\n          <h4 />\n          <CloseBtn />\n        </div>\n      )}\n      <div className=\"modal-body\"></div>\n      {options.fullCustom ? undefined :\n        <div className=\"modal-footer\">\n          <button type=\"button\" className=\"btn btn-default\" data-dismiss=\"modal\">\n            Close\n          </button>\n        </div>\n      }\n    </Dialog>,\n    container\n  );\n}\n\nfunction addEvents(html, gVarName) {\n  if (!html || typeof html !== 'string') {\n    return html;\n  }\n  return html.replace(/\\s(on[a-z]+=)\"([^\"]+)\"/g, function (all, name, handle) {\n    var index = handle.indexOf('(');\n    var args;\n    if (index === -1) {\n      args = '(event)';\n    } else {\n      args = handle.substring(index);\n      handle = handle.substring(0, index);\n    }\n    handle = gVarName + '[\\'' + handle + '\\']' + args;\n    return ' ' + name + '\"' + handle + '\"';\n  });\n}\n\nfunction updateCtn(con, html, name) {\n  var elem = con.find('.modal-content>.modal-' + name + ':first');\n  if (html != null) {\n    elem.html(html);\n  } else if (html === false || html === null) {\n    elem.hide();\n  }\n}\n\nfunction initModal(dialog, options, gVarName) {\n  options = options || '';\n  var title = options.title;\n  var body = addEvents(options.body, gVarName);\n  var footer = addEvents(options.footer, gVarName);\n  var con = dialog.container;\n  var headerElem = con.find('.modal-content>.modal-header:first');\n  if (!title || typeof title !== 'string') {\n    if (title === false || title === '') {\n      headerElem.hide();\n    }\n  } else {\n    headerElem.show().find('h4').html(title);\n  }\n  updateCtn(con, body, 'body');\n  updateCtn(con, footer, 'footer');\n}\n\nexports.show = function (options) {\n  var destroyed, dialog;\n  var onClose = options.onClose;\n  var gVarName = GLOBAL_VAR + getFlag();\n  options.onClose = function () {\n    removeWinField(gVarName);\n    dialog && dialog.destroy();\n    dialog = null;\n    if (typeof onClose === 'function') {\n      onClose.call(options);\n    }\n  };\n  createModal(\n    options,\n    function (d) {\n      dialog = d;\n      if (destroyed) {\n        d.hide();\n      } else {\n        d.show();\n      }\n    },\n    gVarName\n  );\n  return function () {\n    if (!destroyed && dialog) {\n      destroyed = true;\n      dialog.hide();\n    }\n  };\n};\n\nexports.create = function (options) {\n  var destroyed, dialog;\n  var onClose = options.onClose;\n  var gVarName = GLOBAL_VAR + getFlag();\n  options.onClose = function () {\n    removeWinField(gVarName);\n    if (destroyed) {\n      dialog && dialog.destroy();\n      dialog = null;\n    }\n    if (typeof onClose === 'function') {\n      onClose.call(options);\n    }\n  };\n  createModal(\n    options,\n    function (d) {\n      dialog = d;\n      if (destroyed) {\n        d.destroy();\n      }\n    },\n    gVarName\n  );\n  return {\n    show: function (options) {\n      if (dialog) {\n        if (options && options.methods) {\n          window[gVarName] = options.methods;\n        }\n        dialog.show();\n        initModal(dialog, options, gVarName);\n      }\n    },\n    hide: function (destroy) {\n      destroyed = destroy;\n      if (dialog) {\n        if (dialog.container.is(':visible')) {\n          dialog.hide();\n        } else if (destroyed) {\n          dialog.destroy();\n        }\n      }\n    }\n  };\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/network-modal.js",
    "content": "var util = require('./util');\nvar storage = require('./storage');\nvar events = require('./events');\n\nvar NUM_OPTIONS = [500, 1000, 1500, 2000, 2500, 3000];\nvar curLength = parseInt(storage.get('maxNetworkRows'), 10) || 1500;\nvar MAX_LENGTH = NUM_OPTIONS.indexOf(curLength) === -1 ? 1500 : curLength;\nvar MAX_COUNT = MAX_LENGTH + 100;\nvar dataCenter;\nvar WIN_NAME_PRE =\n  '__whistle_' + location.href.replace(/\\/[^/]*([#?].*)?$/, '/') + '__';\nvar KW_RE =\n  /^(e|error|style|url|host|h|domain|d|u|composer|fc|content|c|b|body|headers|h|ip|i|status|a|app|result|s|r|method|m|mark|type|t):(.*)$/i;\nvar KW_LIST_RE = /([^\\s]+)(?:\\s+([^\\s]+)(?:\\s+([\\S\\s]+))?)?/;\nvar WS_RE = /^wss?:\\/\\//;\nvar FONT_RE = /font\\//i;\nvar MEDIA_RE = /audio\\/|video\\/|application\\/vnd.apple.mpegurl|application\\/dash+xml/i;\nvar WASM_RE = /application\\/wasm/i;\nvar TYPES = ['Img', 'HTML', 'CSS', 'JS', 'JSON'];\nvar RAW_TYPES = ['Font', 'Media', 'Wasm'];\n\nfunction NetworkModal(list) {\n  this.list = updateOrder(list);\n  this.isTreeView = storage.get('isTreeView') === '1';\n  this.clearRoot();\n}\n\nNetworkModal.MAX_COUNT = MAX_COUNT;\n\nNetworkModal.setMaxRows = function (num) {\n  num = parseInt(num, 10);\n  if (NUM_OPTIONS.indexOf(num) !== -1) {\n    MAX_LENGTH = num;\n    MAX_COUNT = MAX_LENGTH + 100;\n    NetworkModal.MAX_COUNT = MAX_COUNT;\n    storage.set('maxNetworkRows', num);\n  }\n};\n\nNetworkModal.getMaxRows = function () {\n  return MAX_LENGTH;\n};\n\nvar proto = NetworkModal.prototype;\n\n/**\n * 默认根据url过滤\n * url[u]:根据url过滤\n * content[c]: 根据content过滤\n * headers[h]: 根据headers过滤\n * ip[i]: 根据ip过滤\n * status[result]: 根据status过滤\n * method[m]: 根据method过滤\n */\n\nfunction parseKeyword(keyword) {\n  keyword = typeof keyword != 'string' ? '' : keyword.trim();\n  if (!keyword) {\n    return;\n  }\n  var not = keyword[0] === '!';\n  if (not) {\n    keyword = keyword.substring(1);\n  }\n  var type;\n  if (KW_RE.test(keyword)) {\n    type = RegExp.$1.toLowerCase();\n    keyword = RegExp.$2.trim();\n    if (keyword[0] === '!') {\n      not = true;\n      keyword = keyword.substring(1);\n    }\n  }\n  if (!keyword && !type) {\n    return;\n  }\n  return {\n    not: not,\n    type: type || 'url',\n    keyword: keyword.toLowerCase(),\n    regexp: util.toRegExp(keyword)\n  };\n}\n\nfunction parseKeywordList(keyword) {\n  keyword = typeof keyword != 'string' ? '' : keyword.trim();\n  if (!keyword) {\n    return;\n  }\n  var result;\n  var addKw = function (kw) {\n    if (kw) {\n      result = result || [];\n      result.push(kw);\n    }\n  };\n  if (KW_LIST_RE.test(keyword)) {\n    var k1 = RegExp.$1;\n    var k2 = RegExp.$2;\n    var k3 = RegExp.$3;\n    addKw(parseKeyword(k1));\n    addKw(parseKeyword(k2));\n    addKw(parseKeyword(k3));\n  } else {\n    addKw(parseKeyword(keyword));\n  }\n  return result;\n}\n\nproto.search = function (keyword) {\n  this._keyword = parseKeywordList(keyword);\n  this.filter();\n  return keyword;\n};\n\nproto.setFilterType = function (type) {\n  this._filterType = type;\n  this.filter();\n  return type;\n};\n\nfunction checkKeywork(str, opts) {\n  if (!str) {\n    return false;\n  }\n  if (!opts.keyword) {\n    return true;\n  }\n  return opts.regexp\n    ? opts.regexp.test(str)\n    : str.toLowerCase().indexOf(opts.keyword) !== -1;\n}\n\nfunction checkUrl(item, opts) {\n  if (checkKeywork((item.isHttps ? 'tunnel://' : '') + item.url, opts)) {\n    return true;\n  }\n  var rawUrl = util.getRawUrl(item);\n  return checkKeywork(rawUrl, opts);\n}\n\nfunction checkData(item, opts) {\n  if (setNot(checkUrl(item, opts), opts.not)) {\n    return false;\n  }\n  var keys = dataCenter && dataCenter.getDataKeys();\n  var len = keys && keys.length;\n  if (len) {\n    for (var i = 0; i < len; i++) {\n      var value = util.getProperty(item, keys[i]);\n      if (value != null && setNot(checkKeywork(String(value), opts), opts.not)) {\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nproto.hasKeyword = function () {\n  return this._keyword;\n};\n\nproto.setSortColumns = function (columns) {\n  this._columns = columns;\n  this.filter(false);\n};\n\nfunction setNot(flag, not) {\n  return not ? !flag : flag;\n}\n\nfunction hasError(item) {\n  var statusCode = item.res && item.res.statusCode;\n  return item.reqError || item.resError || (item.customData && item.customData.error) ||\n    (statusCode && (!/^\\d+$/.test(statusCode) || statusCode >= 400));\n}\n\nfunction checkItem(item, opts) {\n  switch (opts.type) {\n  case 'mark':\n    return !item.mark || checkData(item, opts);\n  case 'c':\n  case 'content':\n  case 'b':\n  case 'body':\n    var reqBody = util.getBody(item.req, true);\n    var resBody = util.getBody(item.res);\n    return setNot(\n        !checkKeywork(reqBody, opts) && !checkKeywork(resBody, opts),\n        opts.not\n      );\n  case 'headers':\n  case 'h':\n    return setNot(\n        !inObject(item.req.headers, opts) && !inObject(item.res.headers, opts),\n        opts.not\n      );\n  case 'type':\n  case 't':\n    var type = item.res.headers;\n    type = type && type['content-type'];\n    return setNot(\n        !(typeof type == 'string' && checkKeywork(type, opts)),\n        opts.not\n      );\n  case 'domain':\n  case 'host':\n  case 'd':\n  case 'H':\n    return setNot(!checkKeywork(item.isHttps ? item.url : util.getHost(item.url), opts), opts.not);\n  case 'ip':\n  case 'i':\n    return setNot(\n        !checkKeywork(item.req.ip, opts) && !checkKeywork(item.res.ip, opts),\n        opts.not\n      );\n  case 'status':\n  case 's':\n  case 'result':\n  case 'r':\n    var status = item.res.statusCode;\n    return setNot(\n        !checkKeywork(status == null ? '-' : String(status), opts),\n        opts.not\n      );\n  case 'method':\n  case 'm':\n    return setNot(!checkKeywork(item.req.method, opts), opts.not);\n  case 'app':\n  case 'a':\n    return setNot(!checkKeywork(item.appName, opts), opts.not);\n  case 'e':\n  case 'error':\n    return !hasError(item) || checkData(item, opts);\n  case 'style':\n    return !item.style ||  checkData(item, opts);\n  case 'fc':\n  case 'composer':\n    return !item.fc || checkData(item, opts);\n  default:\n    return checkData(item, opts);\n  }\n}\n\nproto.hasUnmarked = function () {\n  var list = this.list;\n  for (var i = list.length - 1; i >= 0; --i) {\n    if (!list[i].mark) {\n      return true;\n    }\n  }\n};\n\nproto.getList = function () {\n  return this._list || this.list;\n};\n\nproto.getTreeLeafs = function(treeId) {\n  if (!treeId) {\n    return;\n  }\n  var isTunnel = !treeId.indexOf('tunnel://');\n  if (isTunnel) {\n    treeId = treeId.substring(9);\n  } else {\n    treeId += '/';\n  }\n  var result = this.list.filter(function (item) {\n    return isTunnel ? item.url === treeId : !item.url.indexOf(treeId);\n  });\n  return result.length ? result : undefined;\n};\n\nfunction toStr(val) {\n  if (val == null) {\n    return '';\n  }\n  return val + '';\n}\n\nfunction checkFilterType(item, filterType) {\n  if (!filterType) {\n    return true;\n  }\n  var isOther = filterType === 'Other';\n  if (isOther || filterType === 'WS') {\n    if (WS_RE.test(item.url)) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  if (isOther || filterType === 'Tunnel') {\n    if (item.isHttps) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  if (isOther || filterType === 'Rules') {\n    if (item[dataCenter.HAS_RULES_KEY]) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  var rawType = util.getRawType(item.res.headers);\n  var type;\n  for (var i = 0, len = RAW_TYPES.length; i < len; i++) {\n    type = RAW_TYPES[i];\n    if (isOther || filterType === type) {\n      var p = type === 'Font' ? FONT_RE : type === 'Media' ? MEDIA_RE : WASM_RE;\n      if (p.test(rawType)) {\n        return !isOther;\n      }\n      if (!isOther) {\n        return false;\n      }\n    }\n  }\n\n  var curType = util.getContentType(rawType);\n  for (i = 0, len = TYPES.length; i < len; i++) {\n    type = TYPES[i];\n    if (isOther || filterType === type) {\n      if (curType === (i ? type : 'IMG')) {\n        return !isOther;\n      }\n      if (!isOther) {\n        return false;\n      }\n    }\n  }\n  if (isOther || filterType === 'Error') {\n    if (hasError(item)) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  if (isOther || filterType === 'Mock') {\n    var rules = item.rules || '';\n    if (rules.rule && rules.rule.isLoc) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  if (isOther || filterType === 'Import') {\n    if (item.importedData) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  if (isOther || filterType === 'Composer') {\n    if (item.fc) {\n      return !isOther;\n    }\n    if (!isOther) {\n      return false;\n    }\n  }\n  return true;\n}\n\nproto.filter = function () {\n  var self = this;\n  var list = self.list;\n  var keyword = self._keyword;\n  var filterType = self._filterType;\n  list.forEach(function (item) {\n    if (!checkFilterType(item, filterType)) {\n      item.hide = true;\n    } else if (keyword) {\n      item.hide = checkItem(item, keyword[0]) ||\n        (keyword[1] && checkItem(item, keyword[1])) ||\n        (keyword[2] && checkItem(item, keyword[2]));\n    } else {\n      item.hide = false;\n    }\n  });\n\n  var columns = self._columns;\n  if (columns && columns.length) {\n    var len = columns.length;\n    self._list = self.list.slice().sort(function (prev, next) {\n      for (var i = 0; i < len; i++) {\n        var column = columns[i];\n        var isBody = column.name === 'body';\n        var name = column.name === 'app' ? 'appName' : null;\n        var prevVal = isBody ? prev.bodySize : util.getCellValue(prev, column, name);\n        var nextVal = isBody ? next.bodySize : util.getCellValue(next, column, name);\n        if (isBody) {\n          if (prevVal === nextVal) {\n            return 0;\n          }\n          var flag = prevVal > nextVal ? 1 : -1;\n          return column.order == 'asc' ? flag : -flag;\n        }\n        if (column.key) {\n          prevVal = toStr(prevVal);\n          nextVal = toStr(nextVal);\n        }\n        var result = compare(prevVal, nextVal, column.order, column.name);\n        if (result) {\n          return result;\n        }\n      }\n\n      return prev.order > next.order ? -1 : 1;\n    });\n  } else {\n    self._list = null;\n  }\n  this.updateTree();\n  this.updateDisplayCount();\n  return list;\n};\n\nfunction compare(prev, next, order, name) {\n  if (prev == next) {\n    return 0;\n  }\n  if (prev == '-') {\n    return 1;\n  }\n  if (next == '-') {\n    return -1;\n  }\n  return order == 'asc'\n    ? _compare(prev, next, name)\n    : -_compare(prev, next, name);\n}\n\nfunction _compare(prev, next, name) {\n  var isNull = next == null || next == '';\n  if (prev == null || prev == '') {\n    return isNull ? 0 : -1;\n  }\n  if (isNull) {\n    return 1;\n  }\n  var isTime = 'dns,request,response,download,time'.indexOf(name) !== -1;\n  if (!isTime && prev > next) {\n    return 1;\n  }\n  var prevType = typeof prev;\n  var nextType = typeof next;\n  if (isTime && prevType === 'string' && nextType === 'string') {\n    return prev.replace('ms', '') - next.replace('ms', '') > 0 ? 1 : -1;\n  }\n  if (prevType != nextType && prevType == 'number') {\n    return 1;\n  }\n\n  return -1;\n}\n\nfunction inObject(obj, opts) {\n  for (var i in obj) {\n    if (checkKeywork(i, opts)) {\n      return true;\n    }\n    var value = obj[i];\n    if (checkKeywork(value == null ? '' : String(value), opts)) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nvar MAX_FS_COUNT = 60;\n\nproto.updateDisplayCount = function () {\n  window.name = WIN_NAME_PRE + this.list.length;\n};\nproto.getDisplayCount = function () {\n  var winName = window.name;\n  if (typeof winName !== 'string' || winName.indexOf(WIN_NAME_PRE) !== 0) {\n    return 0;\n  }\n  var count = parseInt(winName.substring(WIN_NAME_PRE.length));\n  return count >= 0 && count <= MAX_FS_COUNT ? count : MAX_FS_COUNT;\n};\n\nproto.clear = function () {\n  var len = this.list.length;\n  this.clearNetwork = true;\n  this.list.splice(0, len);\n  this._list = null;\n  this.updateTree();\n  this.updateDisplayCount();\n  if (len) {\n    events.trigger('selectedSessionChange');\n  }\n  return this;\n};\n\nproto.removeByHostList = function (hostList) {\n  var list = this.list;\n  for (var i = list.length - 1; i >= 0; --i) {\n    var item = list[i];\n    if (hostList.indexOf(item.isHttps ? item.path : item.hostname) !== -1) {\n      list.splice(i, 1);\n    }\n  }\n  this.update();\n  this.updateDisplayCount();\n};\n\nfunction getNodeIdMap(node, map) {\n  var children = node.children;\n  if (!children) {\n    map[node.data.id] = 1;\n    return map;\n  }\n  children.forEach(function (child) {\n    getNodeIdMap(child, map);\n  });\n  return map;\n}\n\nproto.removeTreeNode = function (path, others) {\n  var node = this.getTreeNode(path);\n  if (!node) {\n    return;\n  }\n  var map = getNodeIdMap(node, {});\n  var list = this.list;\n  for (var i = list.length - 1; i >= 0; --i) {\n    if (others ? !map[list[i].id] : map[list[i].id]) {\n      list.splice(i, 1);\n    }\n  }\n  this.update();\n  this.updateDisplayCount();\n  return true;\n};\n\nproto.removeByUrlList = function (urlList) {\n  var list = this.list;\n  for (var i = list.length - 1; i >= 0; --i) {\n    if (\n      urlList.indexOf(list[i].url.replace(/\\?.*$/, '').substring(0, 1024)) !==\n      -1\n    ) {\n      list.splice(i, 1);\n    }\n  }\n  this.update();\n  this.updateDisplayCount();\n};\n\nproto.removeSelectedItems = function () {\n  var hasSelectedItem;\n  var endIndex = -1;\n  var list = this.list;\n\n  for (var i = list.length - 1; i >= 0; i--) {\n    var item = list[i];\n    if (item.selected && !item.hide) {\n      hasSelectedItem = true;\n      if (endIndex == -1) {\n        endIndex = i;\n      }\n      if (!i) {\n        list.splice(i, endIndex - i + 1);\n      }\n    } else if (endIndex != -1) {\n      list.splice(i + 1, endIndex - i);\n      endIndex = -1;\n    }\n  }\n\n  if (hasSelectedItem) {\n    this.update(false, true);\n    return true;\n  }\n};\n\nproto.remove = function (item) {\n  var list = this.list;\n  var index = list.indexOf(item);\n  if (index !== -1) {\n    list.splice(index, 1);\n    this.update(false, true);\n  }\n};\n\nproto.removeOthers = function (item) {\n  var list = this.list;\n  var index = list.indexOf(item);\n  if (index !== -1) {\n    list.splice(index + 1, list.length - index);\n    if (index !== 0) {\n      list.splice(0, index);\n    }\n    this.update(false, true);\n  }\n};\n\nproto.removeUnselectedItems = function () {\n  var hasUnselectedItem;\n  var endIndex = -1;\n  var list = this.list;\n\n  for (var i = list.length - 1; i >= 0; i--) {\n    var item = list[i];\n    if (!item.selected) {\n      hasUnselectedItem = true;\n      if (endIndex == -1) {\n        endIndex = i;\n      }\n      if (!i) {\n        list.splice(i, endIndex - i + 1);\n      }\n    } else if (endIndex != -1) {\n      list.splice(i + 1, endIndex - i);\n      endIndex = -1;\n    }\n  }\n\n  if (hasUnselectedItem) {\n    this.update(false, true);\n    return true;\n  }\n};\n\nproto.removeUnmarkedItems = function () {\n  var hasUnmarkedItem;\n  var endIndex = -1;\n  var list = this.list;\n\n  for (var i = list.length - 1; i >= 0; i--) {\n    var item = list[i];\n    if (!item.mark) {\n      hasUnmarkedItem = true;\n      if (endIndex == -1) {\n        endIndex = i;\n      }\n      if (!i) {\n        list.splice(i, endIndex - i + 1);\n      }\n    } else if (endIndex != -1) {\n      list.splice(i + 1, endIndex - i);\n      endIndex = -1;\n    }\n  }\n\n  if (hasUnmarkedItem) {\n    this.update(false, true);\n    return true;\n  }\n};\n\nproto.prev = function () {\n  var list = this.getList();\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  var index = activeItem ? list.indexOf(activeItem) : len - 1;\n  var i, item;\n  for (i = index - 1; i >= 0; i--) {\n    item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n\n  for (i = len - 1; i > index; i--) {\n    item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n};\n\nproto.next = function () {\n  var list = this.getList();\n  var len = list.length;\n  if (!len) {\n    return;\n  }\n  var activeItem = this.getActive();\n  var index = activeItem ? list.indexOf(activeItem) : 0;\n  var i, item;\n  for (i = index + 1; i < len; i++) {\n    item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n\n  for (i = 0; i < index; i++) {\n    item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n};\n\nproto.start = function () {\n  var list = this.getList();\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n};\n\nproto.end = function () {\n  var list = this.getList();\n  for (var i = list.length - 1; i >= 0; i++) {\n    var item = list[i];\n    if (!item.hide) {\n      return item;\n    }\n  }\n};\n\nfunction updateList(list, len, hasKeyword) {\n  if (!(len > 0)) {\n    return;\n  }\n  var activeItem = getActive(list);\n  if (hasKeyword) {\n    var i = 0;\n    var length = list.length;\n    while (len > 0 && i < length) {\n      if (list[i].hide) {\n        --length;\n        --len;\n        list.splice(i, 1);\n      } else {\n        ++i;\n      }\n    }\n    len = list.length - MAX_COUNT - 2;\n  }\n  len > 0 && list.splice(0, len);\n  if (activeItem && list.indexOf(activeItem) === -1) {\n    list[0] = activeItem;\n  }\n}\n\nproto.update = function (scrollAtBottom, force) {\n  updateOrder(this.list, force);\n  if (scrollAtBottom && !this.isTreeView) {\n    var exceed = Math.min(this.list.length - MAX_LENGTH, 100);\n    updateList(this.list, exceed, this.hasKeyword());\n  }\n  this.filter();\n  return !this.isTreeView && this.list.length > MAX_LENGTH;\n};\n\nproto.hasSelected = function () {\n  var list = this.list;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (!item.hide && item.selected) {\n      return true;\n    }\n  }\n  return false;\n};\n\nproto.hasUnselected = function () {\n  var list = this.list;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (!item.hide && !item.selected) {\n      return true;\n    }\n  }\n  return false;\n};\n\nproto.getSelected = function () {\n  return this.getActive();\n};\n\nfunction getActive(list) {\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (item.active) {\n      return item;\n    }\n  }\n}\n\nproto.getActive = function () {\n  return getActive(this.list) || this.getSelectedList()[0];\n};\n\nproto.getItem = function (id) {\n  if (!id) {\n    return;\n  }\n  var list = this.list;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (item.id === id) {\n      return item;\n    }\n  }\n};\n\nproto.setSelected = function (item, selected) {\n  item.selected = selected !== false;\n};\n\nproto.getSelectedList = function () {\n  return this.list.filter(function (item) {\n    return !item.hide && item.selected;\n  });\n};\n\nfunction getPrevSelected(start, list) {\n  for (; start >= 0; start--) {\n    var item = list[start - 1];\n    if (!item || (!item.selected && !item.active)) {\n      return start;\n    }\n  }\n  return start;\n}\n\nfunction getNextSelected(start, list) {\n  for (var len = list.length; start < len; start++) {\n    var item = list[start + 1];\n    if (item && item.data) {\n      item = item.data;\n    }\n    if (!item || (!item.selected && !item.active)) {\n      return start;\n    }\n  }\n  return start;\n}\n\nproto.setSelectedList = function (start, end, selectElem) {\n  var list = this.getList();\n  if (this.isTreeView) {\n    list = this.root.list;\n    start = this.getTreeNode(start.id);\n    end = this.getTreeNode(end.id);\n  }\n  start = list.indexOf(start);\n  end = list.indexOf(end);\n  if (start > end) {\n    var temp = getNextSelected(start, list);\n    start = end;\n    end = temp;\n  } else {\n    start = getPrevSelected(start, list);\n  }\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    item = item.data || item;\n    if (i >= start && i <= end) {\n      item.selected = true;\n      selectElem(item, true);\n    } else {\n      item.selected = false;\n    }\n  }\n};\n\nproto.clearSelection = function () {\n  this.list.forEach(function (item) {\n    item.selected = false;\n  });\n};\n\nproto.clearActive = function () {\n  this.list.forEach(function (item) {\n    item.active = false;\n  });\n};\n\nfunction parsePaths(url) {\n  var index = url.indexOf('?');\n  var search = '';\n  if (index !== -1) {\n    search = url.substring(index);\n    url = url.substring(0, index);\n  }\n  index = url.indexOf('://');\n  if (index === -1) {\n    return ['tunnel://' + url, '/'];\n  }\n  index = url.indexOf('/', index + 3);\n  if (index === -1) {\n    return [url, '/'];\n  }\n  var paths = url.substring(index).split('/');\n  paths[0] = url.substring(0, index);\n  var lastIndex = paths.length - 1;\n  paths[lastIndex] = '/' + paths[lastIndex] + search;\n  return paths;\n}\n\nfunction checkHide(item) {\n  var children = item.children;\n  if (!children || item.hide) {\n    return item.hide;\n  }\n  var hide = true;\n  for (var i = 0, len = children.length; i < len; i++) {\n    var child = children[i];\n    if (checkHide(child)) {\n      child.hide = true;\n    } else {\n      hide = false;\n      child.hide = false;\n    }\n  }\n  item.hide = hide;\n  return hide;\n}\n\nfunction handleTree(root, list) {\n  var children = root.children;\n  for (var i = 0, len = children.length; i < len; i++) {\n    var item = children[i];\n    if (!item.hide) {\n      list.push(item);\n      item.children && handleTree(item, list);\n    }\n  }\n  return root;\n}\n\nproto.clearRoot = function () {\n  var root = {\n    children: [],\n    map: {},\n    list: []\n  };\n  this.root = root;\n  return root;\n};\n\nproto.getListByPath = function (path) {\n  var isTunnel = path.indexOf('tunnel://') === 0;\n  if (isTunnel) {\n    path = path.substring(9);\n  } else {\n    path = path + '/';\n  }\n  return this.list.filter(function (item) {\n    return (\n      !item.hide && (isTunnel ? item.url === path : !item.url.indexOf(path))\n    );\n  });\n};\n\nproto.updateTree = function () {\n  if (!this.isTreeView) {\n    this._updateOnTreeView = true; // 非树状展示模式，不更新 tree 数据，等切换 view 时更新\n    return this.root;\n  }\n  this._updateOnTreeView = false;\n  var allData = this.list;\n  var len = allData.length;\n  if (!len) {\n    return this.clearRoot();\n  }\n  var oldRoot = this.root;\n  var root = this.clearRoot();\n  for (var i = 0; i < len; i++) {\n    var item = allData[i];\n    var paths = parsePaths(item.url);\n    var lastIndex = paths.length - 1;\n    var parent = root;\n    var pre = oldRoot;\n    var path;\n    var top;\n    for (var j = 0; j < lastIndex; j++) {\n      var value = paths[j];\n      var next = parent.map[value];\n      var old = pre && pre.map[value];\n      path = j ? path + '/' + value : value;\n      if (!next) {\n        next = {\n          depth: j,\n          path: path,\n          value: value,\n          children: [],\n          map: {}\n        };\n        if (old) {\n          next.expand = old.expand;\n          next.pExpand = old.pExpand;\n        }\n        parent.map[value] = next;\n        parent.children.push(next);\n      }\n      if (j) {\n        next.parent = parent;\n      } else {\n        top = next;\n      }\n      parent = next;\n      pre = old;\n    }\n    var leaf = {\n      depth: lastIndex,\n      parent: parent,\n      value: paths[lastIndex],\n      hide: item.hide,\n      data: item\n    };\n    top.map[item.id] = leaf;\n    parent.children.push(leaf);\n  }\n  root.children.forEach(checkHide);\n  handleTree(root, root.list);\n  return root;\n};\n\nproto.setTreeView = function (isTreeView, quiet) {\n  isTreeView = isTreeView !== false;\n  if (this.isTreeView !== isTreeView) {\n    this.isTreeView = isTreeView;\n    !quiet && storage.set('isTreeView', isTreeView ? '1' : '');\n    isTreeView && this._updateOnTreeView && this.updateTree();\n  }\n};\n\nproto.getTree = function () {\n  return this.root;\n};\n\nproto.getTreeNode = function (id) {\n  var list = this.root.list;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (item.data ? item.data.id === id : item.path === id) {\n      return item;\n    }\n  }\n};\n\nfunction updateOrder(list, force) {\n  var len = list.length;\n  if (len && (force || !list[len - 1].order)) {\n    var order = list[0].order || 1;\n    list.forEach(function (item, i) {\n      item.order = order + i;\n    });\n  }\n\n  return list;\n}\n\nNetworkModal.setDataCenter = function(dc) {\n  dataCenter = dc;\n};\n\nmodule.exports = NetworkModal;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/network-settings.js",
    "content": "require('../css/network-settings.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar NetworkModal = require('./network-modal');\nvar Dialog = require('./dialog');\nvar columns = require('./columns');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar util = require('./util');\nvar win = require('./win');\nvar storage = require('./storage');\nvar Icon = require('./icon');\nvar HelpIcon = require('./help-icon');\nvar CloseBtn = require('./close-btn');\n\nvar NOT_EMPTY_STYLE = { backgroundColor: 'var(--b-filtered)' };\nvar NOT_EMPTY_RE = /[^\\s]/;\n\nvar Settings = React.createClass({\n  getInitialState: function () {\n    var dragger = columns.getDragger();\n    var urlType = storage.get('urlType');\n    dragger.onDrop = dragger.onDrop.bind(this);\n    return $.extend(this.getNetworkSettings(), { dragger: dragger, urlType: urlType === '-' ? '-' : '' });\n  },\n  getNetworkSettings: function () {\n    return $.extend(dataCenter.getFilterText(), {\n      columns: columns.getAllColumns()\n    });\n  },\n  onColumnsResort: function () {\n    events.trigger('onColumnsChanged');\n    this.setState({ columns: columns.getAllColumns() });\n  },\n  resetColumns: function () {\n    var self = this;\n    win.confirm('Do you confirm resetting the network table\\'s columns?', function(sure) {\n      if (sure) {\n        self.setState({ urlType: '' });\n        storage.set('urlType', '');\n        columns.reset();\n        self.onColumnsResort();\n      }\n    });\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.networkSettingsDialog.isVisible();\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('toggleTreeView', function () {\n      self.setState({});\n    });\n    events.on('setNetworkSettings', function(_, settings) {\n      if (!settings) {\n        return;\n      }\n      win.confirm('Do you confirm the changes to the network settings?', function(sure) {\n        if (sure) {\n          self.setSettings(settings);\n        }\n      });\n    });\n  },\n  componentWillUnmount: function () {\n    events.off('toggleTreeView');\n    events.off('setNetworkSettings');\n  },\n  onNetworkSettingsChange: function (e) {\n    var target = e.target;\n    var name = target.getAttribute('data-name');\n    if (!name || name === 'path') {\n      return;\n    }\n    if (name === 'viewOwn') {\n      dataCenter.setOnlyViewOwnData(target.checked);\n      this.setState({});\n      events.trigger('filterChanged');\n      return;\n    }\n    if (name === 'treeView') {\n      events.trigger('switchTreeView');\n      return;\n    }\n    if (name === 'viewAllInNewWindow') {\n      storage.set('viewAllInNewWindow', target.checked ? '1' : '');\n      return this.setState({});\n    }\n    if (name === 'disabledHNR') {\n      storage.set('disabledHNR', target.checked ? '' : '1');\n      return this.setState({});\n    }\n    var settings = this.state;\n    var filterTextChanged;\n    var columnsChanged;\n    switch (name) {\n    case 'filter':\n      settings.disabledFilterText = !target.checked;\n      filterTextChanged = true;\n      break;\n    case 'excludeFilter':\n      settings.disabledExcludeText = !target.checked;\n      filterTextChanged = true;\n      break;\n    case 'filterText':\n      filterTextChanged = true;\n      settings.filterText = target.value;\n      break;\n    case 'excludeText':\n      filterTextChanged = true;\n      settings.excludeText = target.value;\n      break;\n    case 'networkColumns':\n      columnsChanged = true;\n      break;\n    default:\n      columns.setSelected(name, target.checked);\n      columnsChanged = true;\n    }\n    if (filterTextChanged) {\n      dataCenter.setFilterText(settings);\n      events.trigger('filterChanged');\n    } else if (columnsChanged) {\n      events.trigger('onColumnsChanged');\n    }\n    this.setState(settings);\n  },\n  onFilterKeyDown: function (e) {\n    if ((e.ctrlKey || e.metaKey) && e.keyCode == 88) {\n      e.stopPropagation();\n    }\n    util.handleTab(e);\n  },\n  onRowsChange: function (e) {\n    NetworkModal.setMaxRows(e.target.value);\n  },\n  showDialog: function () {\n    var settings = this.getNetworkSettings();\n    this.setState(settings);\n    this.refs.networkSettingsDialog.show();\n  },\n  hideDialog: function () {\n    this.refs.networkSettingsDialog.hide();\n  },\n  editCustomCol: function (e) {\n    e.preventDefault();\n    var self = this;\n    self.refs.editCustomColumn.show();\n    var name = e.target.getAttribute('data-name');\n    var lname = name.toLowerCase();\n    self.setState(\n      {\n        name: name,\n        value: dataCenter[lname],\n        key: dataCenter[lname + 'Key'] || '',\n        nameChanged: false\n      },\n      function () {\n        setTimeout(function () {\n          var input = ReactDOM.findDOMNode(self.refs.newColumnName);\n          input.select();\n          input.focus();\n        }, 360);\n      }\n    );\n  },\n  onNameChange: function (e) {\n    var value = e.target.value;\n    this.setState({\n      value: value.trim(),\n      nameChanged: true\n    });\n  },\n  onKeyChange: function(e) {\n    var value = e.target.value;\n    this.setState({\n      key: value.replace(/\\s+/, ''),\n      nameChanged: true\n    });\n  },\n  setCustomColumn: function(name, key, value) {\n    var self = this;\n    dataCenter.setCustomColumn(\n      {\n        name: name,\n        value: value,\n        key: key\n      },\n      function (data, xhr) {\n        if (!data) {\n          util.showSystemError(xhr);\n          return;\n        }\n        self.refs.editCustomColumn.hide();\n        var lname = name.toLowerCase();\n        dataCenter[lname] = value || name;\n        dataCenter[lname + 'Key'] = key;\n        self.setState({});\n        events.trigger('onColumnTitleChange');\n      }\n    );\n  },\n  changeName: function () {\n    var state = this.state;\n    this.setCustomColumn(state.name, state.key, state.value);\n  },\n  setSettings: function(settings) {\n    if (!settings) {\n      return;\n    }\n    var self = this;\n    var state = self.state;\n    var filterTextChanged;\n    var filterChanged;\n    var columnsChanged;\n    var viewOwn = settings.viewOwn;\n    if (util.isBool(viewOwn) && dataCenter.isOnlyViewOwnData() !== viewOwn) {\n      dataCenter.setOnlyViewOwnData(viewOwn);\n      filterChanged = true;\n    }\n    var treeView = settings.treeView;\n    if (util.isBool(treeView) && treeView !== (storage.get('isTreeView') === '1')) {\n      events.trigger('switchTreeView');\n    }\n\n    var viewInWin = settings.viewAllInWindow;\n    if (util.isBool(viewInWin) && viewInWin !== (storage.get('viewAllInNewWindow') === '1')) {\n      storage.set('viewAllInNewWindow', viewInWin ? '1' : '');\n    }\n\n    var disabledHNR = settings.disabledHNR;\n    if (util.isBool(disabledHNR) && disabledHNR !== (storage.get('disabledHNR') === '1')) {\n      storage.set('disabledHNR', disabledHNR ? '1' : '');\n    }\n\n    var disabledFilterText = settings.disabledFilterText;\n    if (util.isBool(disabledFilterText) && disabledFilterText !== !!state.disabledFilterText) {\n      state.disabledFilterText = disabledFilterText;\n      filterTextChanged = true;\n    }\n\n    var disabledExcludeText = settings.disabledExcludeText;\n    if (util.isBool(disabledExcludeText) && disabledExcludeText !== !!state.disabledExcludeText) {\n      state.disabledExcludeText = disabledExcludeText;\n      filterTextChanged = true;\n    }\n\n    var excludeText = settings.excludeText;\n    if (util.isString(excludeText) && excludeText !== state.excludeText) {\n      state.excludeText = excludeText;\n      filterTextChanged = true;\n    }\n\n    var filterText = settings.filterText;\n    if (util.isString(filterText) && filterText !== state.filterText) {\n      state.filterText = filterText;\n      filterTextChanged = true;\n    }\n\n\n    var list = settings.columns;\n    if (Array.isArray(list)) {\n      state.columns.forEach(function(col) {\n        var selected = list.indexOf(col.name) !== -1;\n        if (col.selected !== selected) {\n          columnsChanged = true;\n          columns.setSelected(col.name, selected);\n        }\n      });\n    }\n    if (settings.maxRows > 0) {\n      NetworkModal.setMaxRows(settings.maxRows);\n    }\n\n    if (filterTextChanged || filterChanged) {\n      if (filterTextChanged) {\n        dataCenter.setFilterText(state);\n      }\n      events.trigger('filterChanged');\n    } else if (columnsChanged) {\n      events.trigger('onColumnsChanged');\n    }\n    this.setState({});\n\n    ['Custom1', 'Custom2'].forEach(function(name) {\n      var lname = name.toLowerCase();\n      var keyName = lname + 'Key';\n      var value = settings[lname];\n      var key = settings[keyName] || '';\n      if (util.isString(value) && util.isString(key)) {\n        value = value.trim();\n        key = key.trim();\n        if (value && (dataCenter[lname] !== value || (dataCenter[keyName] || '') !== key)) {\n          self.setCustomColumn(name, key, value);\n        }\n      }\n    });\n  },\n  import: function(e) {\n    events.trigger('showImportDialog', 'networkSettings');\n  },\n  getSettings: function() {\n    var state = this.state;\n    var columns = [];\n    state.columns.forEach(function(col) {\n      if (col.selected) {\n        columns.push(col.name);\n      }\n    });\n    return {\n      type: 'setNetworkSettings',\n      disabledExcludeText: state.disabledExcludeText,\n      excludeText: state.excludeText,\n      disabledFilterText: state.disabledFilterText,\n      filterText:  state.filterText,\n      columns: columns,\n      custom1: dataCenter.custom1 || 'Custom1',\n      custom1Key: dataCenter.custom1Key,\n      custom2: dataCenter.custom2 || 'Custom2',\n      custom2Key: dataCenter.custom2Key,\n      maxRows: NetworkModal.getMaxRows(),\n      viewOwn: dataCenter.isOnlyViewOwnData(),\n      viewAllInWindow: storage.get('viewAllInNewWindow') === '1',\n      treeView: storage.get('isTreeView') === '1',\n      disabledHNR: storage.get('disabledHNR') === '1'\n    };\n  },\n  export: function() {\n    events.trigger('showExportDialog', ['networkSettings', this.getSettings()]);\n  },\n  onUrlType: function(e) {\n    var urlType = e.target.value;\n    storage.set('urlType', urlType);\n    this.setState({ urlType: urlType });\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var columnList = state.columns;\n    var isTreeView = storage.get('isTreeView') === '1';\n    var viewAllInNewWindow = storage.get('viewAllInNewWindow') === '1';\n\n    return (\n      <Dialog ref=\"networkSettingsDialog\" wstyle=\"w-ns-dialog\">\n        <div onChange={self.onNetworkSettingsChange} className=\"modal-body\">\n          <CloseBtn />\n          <fieldset className=\"w-ns-filter\">\n            <legend>\n              <label>\n                <input\n                  checked={!state.disabledExcludeText}\n                  data-name=\"excludeFilter\"\n                  type=\"checkbox\"\n                  className=\"w-va-mdl\"\n                />\n                <span className=\"w-va-mdl\">Exclude Filter</span>\n              </label>\n              <HelpIcon docsUrl=\"gui/network.html#settings\" />\n            </legend>\n            <textarea\n              disabled={state.disabledExcludeText}\n              onKeyDown={self.onFilterKeyDown}\n              value={state.excludeText}\n              data-name=\"excludeText\"\n              placeholder=\"Type filter text\"\n              style={!state.disabledExcludeText && NOT_EMPTY_RE.test(state.excludeText) ? NOT_EMPTY_STYLE : undefined}\n              maxLength={dataCenter.MAX_EXCLUDE_LEN}\n            />\n          </fieldset>\n          <fieldset className=\"w-ns-filter\">\n            <legend>\n              <label>\n                <input\n                  checked={!state.disabledFilterText}\n                  data-name=\"filter\"\n                  type=\"checkbox\"\n                  className=\"w-va-mdl\"\n                />\n                <span className=\"w-va-mdl\">Include Filter</span>\n              </label>\n              <HelpIcon docsUrl=\"gui/network.html#settings\" />\n            </legend>\n            <textarea\n              disabled={state.disabledFilterText}\n              onKeyDown={self.onFilterKeyDown}\n              value={state.filterText}\n              data-name=\"filterText\"\n              placeholder=\"Type filter text\"\n              style={!state.disabledFilterText && NOT_EMPTY_RE.test(state.filterText) ? NOT_EMPTY_STYLE : undefined}\n              maxLength={dataCenter.MAX_INCLUDE_LEN}\n            />\n          </fieldset>\n          <fieldset className=\"w-ns-columns\">\n            <legend>\n              <label>Network Columns</label>\n              <label onClick={self.resetColumns} className=\"btn btn-default\">\n                Reset\n              </label>\n            </legend>\n            {columnList.map(function (col) {\n              if (col.isPlugin) {\n                return;\n              }\n              var name = col.name;\n              var canEdit1 = name === 'custom1';\n              var canEdit = canEdit1 || name === 'custom2';\n              var title;\n              if (canEdit) {\n                title = canEdit1 ? dataCenter.custom1 : dataCenter.custom2;\n              } else {\n                title = col.title;\n              }\n              return (\n                <label\n                  {...state.dragger}\n                  data-name={name}\n                  draggable={true}\n                  key={name}\n                >\n                  <input\n                    disabled={col.locked}\n                    checked={!!col.selected || !!col.locked}\n                    data-name={name}\n                    type=\"checkbox\"\n                  />\n                  {canEdit ? (\n                    <span title={title} className=\"w-network-custom-col\">\n                      {title}\n                    </span>\n                  ) : (\n                    title\n                  )}\n                  {\n                    name === 'path' ?\n                    <select\n                      className=\"w-query-select\"\n                      value={state.urlType || ''}\n                      onChange={self.onUrlType}\n                    >\n                      <option value=\"\">+Query</option>\n                      <option value=\"-\">-Query</option>\n                    </select> : null\n                  }\n                  {canEdit ? (\n                    <Icon\n                      name=\"edit\"\n                      onClick={self.editCustomCol}\n                      data-name={col.title}\n                      title={'Edit ' + col.title}\n                    />\n                  ) : undefined}\n                </label>\n              );\n            })}\n          </fieldset>\n\n          <label className=\"w-ns-own\">\n            Maximum Rows:\n            <select\n              className=\"form-control\"\n              onChange={self.onRowsChange}\n              value={NetworkModal.getMaxRows()}\n            >\n              <option value=\"500\">500</option>\n              <option value=\"1000\">1000</option>\n              <option value=\"1500\">1500</option>\n              <option value=\"2000\">2000</option>\n              <option value=\"2500\">2500</option>\n              <option value=\"3000\">3000</option>\n            </select>\n          </label>\n          <label className=\"w-ns-own\">\n            <input\n              checked={dataCenter.isOnlyViewOwnData()}\n              data-name=\"viewOwn\"\n              type=\"checkbox\"\n            />\n            Viewing only your computer's network requests (IP: {dataCenter.clientIp})\n          </label>\n          <label className=\"w-ns-own\">\n            <input checked={viewAllInNewWindow} data-name=\"viewAllInNewWindow\" type=\"checkbox\" />\n            ViewAll in new window\n          </label>\n          <label className=\"w-ns-own\">\n            <input checked={isTreeView} data-name=\"treeView\" type=\"checkbox\" />\n            <Icon name=\"tree-conifer\" />\n            Show Tree View (Ctrl[Command] + B)\n          </label>\n          {isTreeView ? (\n            <label style={{marginLeft: 20}} className=\"w-ns-own\">\n              <input\n                checked={storage.get('disabledHNR') !== '1'}\n                data-name=\"disabledHNR\"\n                type=\"checkbox\"\n              />\n              Highlight new requests\n            </label>\n          ) : null}\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-primary\"\n            onClick={self.import}\n          >\n            Import\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-info\"\n            onClick={self.export}\n          >\n            Export\n          </button>\n        </div>\n        <Dialog ref=\"editCustomColumn\" wstyle=\"w-ns-edit\">\n          <div onChange={self.onNetworkSettingsChange} className=\"modal-body\">\n            <CloseBtn />\n            <label>\n              <span>Column Name:</span>\n              <input\n                onChange={this.onNameChange}\n                ref=\"newColumnName\"\n                value={state.value}\n                className=\"form-control\"\n                maxLength=\"16\"\n                placeholder=\"Enter custom column name\"\n              />\n            </label>\n            <label>\n            <span>Data Key:</span>\n              <input\n                onChange={this.onKeyChange}\n                value={state.key}\n                className=\"form-control\"\n                maxLength=\"72\"\n                placeholder=\"Enter data key (as: res.headers.x-server ...)\"\n              />\n            </label>\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Cancel\n            </button>\n            <button\n              disabled={!state.nameChanged}\n              onClick={self.changeName}\n              type=\"button\"\n              className=\"btn btn-primary\"\n            >\n              Confirm\n            </button>\n          </div>\n        </Dialog>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = Settings;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/network.js",
    "content": "var $ = require('jquery');\nvar React = require('react');\nvar util = require('./util');\nvar storage = require('./storage');\nvar Divider = require('./divider');\nvar ReqData = require('./req-data');\nvar Detail = require('./detail');\nvar events = require('./events');\n\nvar getWidth = function (vertical) {\n  var docElem = document.documentElement;\n  if (vertical) {\n    return Math.max(Math.floor(docElem.clientHeight / 2), 360);\n  }\n  return Math.max(Math.floor(docElem.clientWidth / 3), 572);\n};\n\nvar Network = React.createClass({\n  getInitialState: function () {\n    var dockToBottom = storage.get('dockToBottom');\n    if (dockToBottom == null && /[&#?]dockToBottom=true(?:&|$|#)/.test(window.location.search)) {\n      dockToBottom = true;\n    }\n    return {\n      dockToBottom: dockToBottom,\n      rightWidth: getWidth(dockToBottom)\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    $(window)\n      .on('keydown', function (e) {\n        if (self.props.hide) {\n          return;\n        }\n        if ((e.ctrlKey || e.metaKey) && e.keyCode == 68) {\n          if (\n            !util.isFocusEditor() &&\n            !$(e.target).closest('.w-frames-list').length\n          ) {\n            var modal = self.props.modal;\n            if (e.shiftKey) {\n              if (modal && modal.removeUnselectedItems()) {\n                self.setState({});\n              }\n            } else {\n              if (modal && modal.removeSelectedItems()) {\n                self.setState({});\n              }\n            }\n          }\n        }\n      })\n      .on('keydown', function (e) {\n        if (e.keyCode === 123) {\n          if (!self.props.hide) {\n            self.onDockChange();\n          }\n          e.preventDefault();\n        }\n      });\n    events.trigger('networkDidMount');\n    events.on('toggleNetworkDock', self.onDockChange);\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onDockChange: function () {\n    var self = this;\n    var dockToBottom = !self.state.dockToBottom;\n    storage.set('dockToBottom', dockToBottom ? 1 : '');\n    self.setState(\n      {\n        dockToBottom: dockToBottom,\n        rightWidth: getWidth(dockToBottom)\n      },\n      function () {\n        self.refs.divider.reset();\n        var elems = document.querySelectorAll('.w-detail .w-divider');\n        elems.forEach(function (elem) {\n          if (window.MouseEvent) {\n            elem.dispatchEvent(new MouseEvent('dblclick', {\n              bubbles: true,\n              cancelable: true,\n              view: window\n            }));\n          }\n        });\n      }\n    );\n  },\n  render: function () {\n    var modal = this.props.modal;\n    var dockToBottom = this.state.dockToBottom;\n    return (\n      <div className={'v-box fill' + (this.props.hide ? ' hide' : '')}>\n        <Divider\n          ref=\"divider\"\n          vertical={dockToBottom}\n          rightWidth={this.state.rightWidth}\n        >\n          <ReqData modal={modal} />\n          <Detail\n            dockToBottom={dockToBottom}\n            onDockChange={this.onDockChange}\n            modal={modal}\n            rulesModal={this.props.rulesModal}\n          />\n        </Divider>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Network;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/online.js",
    "content": "require('../css/online.css');\nvar $ = require('jquery'); //for bootstrap\nvar React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar DNSDialog = require('./dns-servers-dialog');\nvar storage = require('./storage');\nvar win = require('./win');\nvar message = require('./message');\nvar ShortcutsSettings = require('./shortcuts-settings');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar IPV6_ONLY_VAL = 4;\nvar dialog;\nvar curOrder;\nvar curVerbatim = -1;\nvar theme = 'light';\nvar mediaMatches = false;\nvar borderColor;\nvar setTheme = function() {\n  var newTheme = isDarkMode() ? 'dark' : 'light';\n  if (theme === newTheme) {\n    return;\n  }\n  theme = newTheme;\n  try {\n    var doc = document.documentElement;\n    if (doc.getAttribute('data-theme') !== theme) {\n      doc.setAttribute('data-theme', theme);\n    }\n    var style = getComputedStyle(doc);\n    borderColor = style.getPropertyValue('--c-border').trim();\n  } catch (e) {}\n};\nvar handleThemeChange = setTheme;\n\nvar appearanceMode = storage.get('appearanceMode');\nif (['auto', 'dark', 'light'].indexOf(appearanceMode) === -1) {\n  appearanceMode = 'auto';\n}\n\nfunction isDarkMode() {\n  return (mediaMatches && appearanceMode === 'auto') || appearanceMode === 'dark';\n}\n\nfunction setAppearanceMode(mode) {\n  appearanceMode = mode;\n  handleThemeChange();\n}\n\nfunction onDarkModeChange(cb) {\n  var media;\n  try {\n    media = window.matchMedia('(prefers-color-scheme: dark)');\n    mediaMatches = media.matches;\n  } catch (e) {\n    return;\n  }\n  if (media) {\n    cb();\n    if (typeof media.addEventListener === 'function') {\n      media.addEventListener('change', cb);\n    } else if (typeof media.addListener === 'function') {\n      media.addListener(cb);\n    }\n  }\n}\n\nsetAppearanceMode(appearanceMode);\nonDarkModeChange(function(e) {\n  mediaMatches = e ? e.matches : mediaMatches;\n  handleThemeChange();\n});\n\nfunction updateFakeIframe(iframe) {\n  try {\n    var style = iframe.contentDocument.documentElement.style;\n    if (style.colorScheme !== theme) {\n      style.colorScheme = theme;\n      style.setProperty('--c-border', borderColor);\n    }\n  } catch (e) {}\n}\n\nfunction updateIframeTheme(iframe) {\n  if (iframe.getAttribute('data-type') === 'fake') {\n    return updateFakeIframe(iframe);\n  }\n  try {\n    var win = iframe.contentWindow;\n    var doc = win && win.document.documentElement;\n    if (!doc) {\n      return;\n    }\n    var curTheme = doc.getAttribute('data-theme');\n    if (theme !== curTheme) {\n      doc.setAttribute('data-theme', theme);\n      if (typeof win.onWhistleThemeChange === 'function') {\n        win.onWhistleThemeChange(theme);\n      }\n    }\n  } catch (e) {}\n}\n\ndataCenter.handleIframeLoad = function(e) {\n  updateIframeTheme(e.target);\n};\n\nfunction selectDnsOption(order) {\n  order = +order;\n  if (curVerbatim === 2) {\n    if (order < 1 || order > IPV6_ONLY_VAL) {\n      order = 1;\n    }\n  } else if (curVerbatim === 1) {\n    if (order !== 2 && order !== IPV6_ONLY_VAL) {\n      order = 1;\n    }\n  } else if (order !== IPV6_ONLY_VAL) {\n    order = 0;\n  }\n  if (!dialog || curOrder === order) {\n    return;\n  }\n  curOrder = order;\n  dialog.find('.w-dns-order-option select').val(curOrder);\n}\n\nfunction getDnsOrder(verbatim) {\n  var result = [];\n  if (verbatim) {\n    result.push(\n      '<option value=\"1\">Verbatim</option>',\n      '<option value=\"2\">IPv4-first</option>'\n    );\n    if (verbatim === 2) {\n      result.push('<option value=\"3\">IPv6-first</option>');\n    }\n  } else {\n    result.push('<option value=\"0\">Default</option>');\n  }\n  result.push('<option value=\"' + IPV6_ONLY_VAL + '\">IPv6-only</option>');\n  return result.join('');\n}\n\nfunction createDialog() {\n  if (!dialog) {\n    var proxyInfoList = [\n      '<h5><strong>Uptime:</strong> <span id=\"whistleUptime\">-</span></h5>',\n      '<h5><strong>All Requests:</strong> <span id=\"whistleAllRequests\">-</span></h5>',\n      '<h5><strong>All QPS:</strong> <span id=\"whistleAllQps\">-</span></h5>',\n      '<h5><strong>Requests:</strong> <span id=\"whistleRequests\">-</span></h5>',\n      '<h5><strong>QPS:</strong> <span id=\"whistleQps\">-</span></h5>',\n      '<h5><strong>CPU:</strong> <span id=\"whistleCpu\">-</span></h5>',\n      '<h5><strong>Memory:</strong> <span id=\"whistleMemory\">-</span></h5>'\n    ];\n    dialog = $(\n      '<div class=\"modal fade w-online-dialog\">' +\n      '<div class=\"modal-dialog\">' +\n      '<div class=\"modal-content\">' +\n      '<div class=\"modal-body\">' +\n      '<button type=\"button\" class=\"close\" data-dismiss=\"modal\">&times;</button>' +\n      '<div class=\"w-online-ctn\"></div>' +\n      '<div class=\"w-online-info\">' +  proxyInfoList.join('') + '</div>' +\n      '<h5 class=\"w-theme-option\"><strong>Theme:</strong><select class=\"form-control\"><option value=\"auto\">Auto</option>' +\n      '<option value=\"dark\">Dark</option><option value=\"light\">Light</option></select></h5>'+\n      '<h5 class=\"w-dns-order-option\"><strong>DNS Order:</strong><select class=\"form-control\"></select></h5>' +\n      '<p><a class=\"w-online-dns\">View Custom DNS Servers</a></p>' +\n      '<a class=\"w-online-shortcuts-settings\">Shortcuts Settings</a>' +\n      '</div>' +\n      '<div class=\"modal-footer\">' +\n      '<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>' +\n      // '<button type=\"button\" class=\"btn btn-primary w-login-btn\" data-action=\"login\">' +\n      // '<span class=\"glyphicon glyphicon-log-in\"></span><span class=\"w-login-label\">Login</span></button>' +\n      '</div></div></div></div>'\n    ).appendTo(document.body);\n    var appearanceSelect = dialog.find('.w-theme-option select');\n    appearanceSelect.on('change', function(e) {\n      var val = e.target.value;\n      storage.set('appearanceMode', val);\n      setAppearanceMode(val);\n    });\n    appearanceSelect.val(appearanceMode);\n    dialog.find('.w-dns-order-option select').on('change', function(e) {\n      var target = e.target;\n      var order = +target.value;\n      self._pendingDnsOrder = true;\n      dataCenter.setDnsOrder({ order: order }, function (data, xhr) {\n        setTimeout(function() {\n          self._pendingDnsOrder = false;\n        }, 300);\n        if (!data) {\n          util.showSystemError(xhr);\n          return;\n        }\n        selectDnsOption(order);\n      });\n    });\n  }\n\n  return dialog;\n}\n\nfunction addIndent(list) {\n  return list.map(function (ip) {\n    return '  ' + ip;\n  });\n}\n\nvar Online = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  componentWillMount: function () {\n    var self = this;\n    var online = true;\n    var body = $(document.body);\n    dataCenter.on('serverInfo', function (data) {\n      self.updateServerInfo(data);\n      data && self.checkServerChanged(data);\n      var curState = !!data;\n      if (curState !== online) {\n        online = curState;\n        if (online) {\n          body.removeClass('w-offline-status');\n        } else {\n          body.addClass('w-offline-status');\n        }\n      }\n      self.setState({ server: data });\n    });\n  },\n  componentDidMount: function() {\n    var self = this;\n    dataCenter.setServerInfo = function(info) {\n      !self._pendingDnsOrder && selectDnsOption(info.ipv6Only ? IPV6_ONLY_VAL : info.dnsOrder);\n    };\n    handleThemeChange = function() {\n      setTheme();\n      var iframes = document.querySelectorAll('iframe');\n      for (var i = 0; i < iframes.length; i++) {\n        updateIframeTheme(iframes[i]);\n      }\n    };\n\n    handleThemeChange();\n    setInterval(handleThemeChange, 1600);\n  },\n  checkServerChanged: function (data) {\n    data.mac = data.mac || '';\n    if (this.macAddr === undefined) {\n      this.macAddr = data.mac;\n      this.serverPort = data.port;\n      this.version = data.version;\n      this.baseDir = data.baseDir;\n      this.networkMode = data.networkMode;\n      this.pluginsMode = data.pluginsMode;\n      this.rulesMode = data.rulesMode;\n      this.multiEnv = data.multiEnv;\n      this.rulesOnlyMode = data.rulesOnlyMode;\n    } else if (\n      this.version !== data.version ||\n      this.baseDir !== data.baseDir ||\n      this.rulesOnlyMode !== data.rulesOnlyMode ||\n      this.networkMode !== data.networkMode ||\n      this.pluginsMode !== data.pluginsMode ||\n      this.rulesMode !== data.rulesMode ||\n      this.multiEnv !== data.multiEnv\n    ) {\n      this.refs.confirmReload.show();\n    } else {\n      this.refs.confirmReload.hide();\n    }\n  },\n  showServerInfo: function () {\n    if (this.state.server) {\n      this.updateServerInfo(dataCenter.getServerInfo());\n      dialog.modal('show');\n    }\n  },\n  updateServerInfo: function (server) {\n    if (!server) {\n      return;\n    }\n    var self = this;\n    var clientVersion = self.props.clientVersion;\n    self.state.server = server;\n    var info = [];\n    var whistleId = util.escape(server.whistleId);\n    var username = util.escape(server.username);\n    var host = util.escape(server.host);\n    if (whistleId) {\n      info.push('<h5 class=\"w-whistle-id-option\" title=\"' + whistleId + '\"><strong>Whistle ID:</strong> ' + whistleId + '</h5>');\n    }\n    if (username) {\n      info.push('<h5 class=\"w-system-host\"><strong>Username:</strong> ' + username + '</h5>');\n    }\n    if (host) {\n      info.push('<h5><strong>Host:</strong> ' + host + '</h5>');\n    }\n    if (server.pid) {\n      info.push('<h5><strong>PID:</strong> ' + server.pid + '</h5>');\n    }\n    if (server.nodeVersion) {\n      info.push('<h5><strong>Node:</strong> ' + server.nodeVersion + '</h5>');\n    }\n    if (clientVersion) {\n      info.push('<h5><strong>Client:</strong> v' + clientVersion + '</h5>');\n    }\n    if (server.version) {\n      info.push('<h5><strong>Whistle:</strong> v' + server.version + '</h5>');\n    }\n    var port = server.realPort || server.port;\n    if (port) {\n      var bip = server.realHost != null ? server.realHost : server.bip;\n      if (typeof bip === 'string' && bip.indexOf(':') !== -1) {\n        bip = '[' + bip + ']';\n      }\n      info.push('<h5><strong>Port:</strong> ' + (bip ? bip + ':' + port : port) + '</h5>');\n    }\n    if (server.socksPort) {\n      info.push(\n        '<h5><strong>SOCKS Port:</strong> ' + server.socksPort + '</h5>'\n      );\n    }\n    if (server.httpPort) {\n      info.push('<h5><strong>HTTP Port:</strong> ' + server.httpPort + '</h5>');\n    }\n    if (server.httpsPort) {\n      info.push(\n        '<h5><strong>HTTPS Port:</strong> ' + server.httpsPort + '</h5>'\n      );\n    }\n    if (server.ipv4.length) {\n      info.push('<h5><strong>IPv4:</strong></h5>');\n      info.push('<p>' + server.ipv4.join('<br/>') + '</p>');\n    }\n    if (server.ipv6.length) {\n      info.push('<h5><strong>IPv6:</strong></h5>');\n      info.push('<p>' + server.ipv6.join('<br/>') + '</p>');\n    }\n    createDialog();\n    var ctn = dialog.find('.w-online-ctn').html(info.join(''));\n    var loginBtn = dialog.find('.w-login-btn');\n    var loginElem = loginBtn[0];\n    if (loginElem) {\n      var hasToken = dataCenter.hasWhistleToken;\n      loginElem.className = 'btn w-login-btn  btn-' + (hasToken ? 'danger' : 'primary');\n      loginBtn.attr('data-action', hasToken ? 'logout' : 'login');\n      loginBtn.find('.w-login-label').text(hasToken ? 'Logout' : 'Login');\n      loginBtn.find('.glyphicon')[0].className = 'glyphicon glyphicon-log-' + (hasToken ? 'out' : 'in');\n    }\n    if (curVerbatim !== server.verbatim) {\n      curVerbatim = server.verbatim;\n      dialog.find('.w-dns-order-option select').html(getDnsOrder(server.verbatim));\n    }\n    !self._pendingDnsOrder && selectDnsOption(server.dnsOrder);\n    ctn.find('h5.w-system-host').attr('title', server.host);\n    loginBtn.on('click', function (e) {\n      var action = $(e.target).closest('button').attr('data-action');\n      if (action === 'logout') {\n        return win.confirm('Are you sure you want to log out?', function (sure) {\n          if (!sure) {\n            return;\n          }\n          dataCenter.logout(function (data, xhr) {\n            if (!data) {\n              return util.showSystemError(xhr);\n            }\n            if (data.ec !== 0) {\n              return message.error(data.em || 'Logout failed');\n            }\n            message.success('Logged out successfully');\n            dataCenter.triggerWhistleIdChanged();\n          });\n        });\n      }\n      util.showService('login');\n    });\n    if (!self._initProxyInfo) {\n      self._initProxyInfo = true;\n      var curServerInfo;\n      var isHide = true;\n      var dnsElem = dialog.find('.w-online-dns');\n      var shortcutsElem = dialog.find('.w-online-shortcuts-settings');\n      var hideDns = true;\n\n      dnsElem.on('click', function () {\n        self.refs.dnsDialog.show(dataCenter.getServerInfo());\n      });\n      shortcutsElem.on('click', function () {\n        self.refs.shortcutsSettings.show();\n      });\n      var toggleDns = function (svrInfo) {\n        if (svrInfo && svrInfo.dns) {\n          if (hideDns) {\n            hideDns = false;\n            dnsElem.show();\n          }\n        } else {\n          if (!hideDns) {\n            hideDns = true;\n            dnsElem.hide();\n          }\n        }\n      };\n      toggleDns(server);\n      setInterval(function () {\n        var info = dataCenter.getServerInfo();\n        var pInfo = info && info.pInfo;\n        toggleDns(info);\n        if (!pInfo) {\n          if (isHide) {\n            isHide = true;\n            dialog.find('.w-online-info').hide();\n          }\n          return;\n        }\n        if (isHide) {\n          isHide = false;\n          dialog.find('.w-online-info').show();\n        }\n        var reqElem = dialog.find('#whistleRequests');\n        var uiReqElem = dialog.find('#whistleAllRequests');\n        var cpuElem = dialog.find('#whistleCpu');\n        var memElem = dialog.find('#whistleMemory');\n        var uptimeElem = dialog.find('#whistleUptime');\n        var qpsElem = dialog.find('#whistleQps');\n        var uiQpsElem = dialog.find('#whistleAllQps');\n        uptimeElem.text(util.formatTime(pInfo.uptime));\n        uptimeElem.parent().attr('title', pInfo.uptime);\n        reqElem\n          .parent()\n          .attr(\n            'title',\n            'HTTP[S]: ' +\n              pInfo.httpRequests +\n              ' (Total: ' +\n              pInfo.totalHttpRequests +\n              ')' +\n              '\\nWS[S]: ' +\n              pInfo.wsRequests +\n              ' (Total: ' +\n              pInfo.totalWsRequests +\n              ')' +\n              '\\nTUNNEL: ' +\n              pInfo.tunnelRequests +\n              ' (Total: ' +\n              pInfo.totalTunnelRequests +\n              ')'\n          );\n        uiReqElem\n          .parent()\n          .attr(\n            'title',\n            'HTTP[S]: ' +\n              pInfo.allHttpRequests +\n              ' (Total: ' +\n              pInfo.totalAllHttpRequests +\n              ')' +\n              '\\nWS[S]: ' +\n              pInfo.allWsRequests +\n              ' (Total: ' +\n              pInfo.totalAllWsRequests +\n              ')' +\n              '\\nTUNNEL: ' +\n              pInfo.tunnelRequests +\n              ' (Total: ' +\n              pInfo.totalTunnelRequests +\n              ')'\n          );\n        memElem.parent().attr(\n          'title',\n          Object.keys(pInfo.memUsage)\n            .map(function (key) {\n              return key + ': ' + pInfo.memUsage[key];\n            })\n            .join('\\n')\n        );\n        qpsElem\n          .parent()\n          .attr(\n            'title',\n        [\n          'HTTP[s]: ' + util.getQps(pInfo.httpQps),\n          'WS[S]: ' + util.getQps(pInfo.wsQps),\n          'TUNNEL: ' + util.getQps(pInfo.tunnelQps)\n        ].join('\\n')\n          );\n        uiQpsElem\n          .parent()\n          .attr(\n            'title',\n        [\n          'HTTP[s]: ' + util.getQps(pInfo.allHttpQps),\n          'WS[S]: ' + util.getQps(pInfo.allWsQps),\n          'TUNNEL: ' + util.getQps(pInfo.tunnelQps)\n        ].join('\\n')\n          );\n        var totalCount =\n          pInfo.httpRequests + pInfo.wsRequests + pInfo.tunnelRequests;\n        var totalUICount =\n          pInfo.allHttpRequests + pInfo.allWsRequests + pInfo.tunnelRequests;\n        var allCount =\n          pInfo.totalHttpRequests +\n          pInfo.totalWsRequests +\n          pInfo.totalTunnelRequests;\n        var allUICount =\n          pInfo.totalAllHttpRequests +\n          pInfo.totalAllWsRequests +\n          pInfo.totalTunnelRequests;\n        pInfo.totalCount = totalCount;\n        pInfo.allCount = allCount;\n        pInfo.totalUICount = totalUICount;\n        pInfo.allUICount = allUICount;\n        if (!curServerInfo || !curServerInfo.pInfo) {\n          reqElem.text(totalCount + ' (Total: ' + allCount + ')');\n          uiReqElem.text(totalUICount + ' (Total: ' + allUICount + ')');\n          cpuElem.text(pInfo.cpuPercent + ' (Max: ' + pInfo.maxCpu + ')');\n          memElem.text(\n            util.getSize(pInfo.memUsage.rss) +\n              ' (Max: ' +\n              util.getSize(pInfo.maxRss) +\n              ')'\n          );\n          qpsElem.text(\n            util.getQps(pInfo.totalQps) +\n              ' (Max: ' +\n              util.getQps(pInfo.maxQps) +\n              ')'\n          );\n          uiQpsElem.text(\n            util.getQps(pInfo.totalAllQps) +\n              ' (Max: ' +\n              util.getQps(pInfo.maxAllQps) +\n              ')'\n          );\n        } else {\n          var curPInfo = curServerInfo.pInfo;\n          if (pInfo.memUsage.rss !== curPInfo.memUsage.rss) {\n            memElem.text(\n              util.getSize(pInfo.memUsage.rss) +\n                ' (Max: ' +\n                util.getSize(pInfo.maxRss) +\n                ')'\n            );\n          }\n          if (\n            totalCount !== curPInfo.totalCount ||\n            allCount !== curPInfo.allCount\n          ) {\n            reqElem.text(totalCount + ' (Total: ' + allCount + ')');\n          }\n          if (\n            totalUICount !== curPInfo.totalUICount ||\n            allUICount !== curPInfo.allUICount\n          ) {\n            uiReqElem.text(totalUICount + ' (Total: ' + allUICount + ')');\n          }\n          if (pInfo.cpuPercent !== curPInfo.cpuPercent) {\n            cpuElem.text(pInfo.cpuPercent + ' (Max: ' + pInfo.maxCpu + ')');\n          }\n          if (pInfo.totalQps !== curPInfo.totalQps) {\n            qpsElem.text(\n              util.getQps(pInfo.totalQps) +\n                ' (Max: ' +\n                util.getQps(pInfo.maxQps) +\n                ')'\n            );\n          }\n          if (pInfo.totalAllQps !== curPInfo.totalAllQps) {\n            uiQpsElem.text(\n              util.getQps(pInfo.totalAllQps) +\n                ' (Max: ' +\n                util.getQps(pInfo.maxAllQps) +\n                ')'\n            );\n          }\n        }\n        curServerInfo = info;\n      }, 1000);\n    }\n  },\n  reload: function () {\n    location.reload();\n  },\n  getTitle: function (server) {\n    if (!server) {\n      return;\n    }\n    var info = [];\n    if (server.whistleId) {\n      info.push('Whistle ID: ' + server.whistleId);\n    }\n    if (server.username) {\n      info.push('Username: ' + server.username);\n    }\n    if (server.host) {\n      info.push('Host: ' + server.host);\n    }\n    if (server.pid) {\n      info.push('PID: ' + server.pid);\n    }\n    var port = server.realPort || server.port;\n    if (port) {\n      info.push('Port: ' + port);\n    }\n    if (server.socksPort) {\n      info.push('SOCKS Port: ' + server.socksPort);\n    }\n    if (server.httpPort) {\n      info.push('HTTP Port: ' + server.httpPort);\n    }\n    if (server.httpsPort) {\n      info.push('HTTPS Port: ' + server.httpsPort);\n    }\n\n    if (server.ipv4.length) {\n      info.push('IPv4:');\n      info.push.apply(info, addIndent(server.ipv4));\n    }\n    if (server.ipv6.length) {\n      info.push('IPv6:');\n      info.push.apply(info, addIndent(server.ipv6));\n    }\n    var pInfo = server.pInfo;\n    if (pInfo) {\n      info.push('Uptime: ' + util.formatTime(pInfo.uptime));\n      info.push(\n        'All Requests: ' +\n          (pInfo.allHttpRequests +\n            pInfo.allWsRequests +\n            pInfo.tunnelRequests +\n            ' (Total: ' +\n            (pInfo.totalAllHttpRequests +\n              pInfo.totalAllWsRequests +\n              pInfo.totalTunnelRequests) +\n            ')')\n      );\n      info.push(\n        'Requests: ' +\n          (pInfo.httpRequests +\n            pInfo.wsRequests +\n            pInfo.tunnelRequests +\n            ' (Total: ' +\n            (pInfo.totalHttpRequests +\n              pInfo.totalWsRequests +\n              pInfo.totalTunnelRequests) +\n            ')')\n      );\n      pInfo.cpuPercent && info.push('CPU: ' + pInfo.cpuPercent);\n      info.push('Memory: ' + util.getSize(pInfo.memUsage.rss));\n      info.push('QPS: ' + util.getQps(pInfo.totalQps));\n      info.push('All QPS: ' + util.getQps(pInfo.totalAllQps));\n    }\n    if (server.dns) {\n      info.push('Use custom DNS servers');\n    }\n    return info.join('\\n');\n  },\n  setTitle: function () {\n    var server = dataCenter.getServerInfo() || this.state.server;\n    ReactDOM.findDOMNode(this.refs.onlineMenu).title = this.getTitle(server) || '';\n  },\n  render: function () {\n    var server = this.state.server || '';\n    return (\n      <a\n        ref=\"onlineMenu\"\n        draggable=\"false\"\n        onMouseEnter={this.setTitle}\n        className={'w-online-menu w-online' + (server ? '' : ' w-offline')}\n        onClick={this.showServerInfo}\n      >\n        <Icon name=\"stats\" />\n        {server ? 'Online' : 'Offline'}\n        {server.dns ? (\n          <span>{server.doh ? '(DoH)' : server.r6 ? '(IPv6)' : '(IPv4)'}</span>\n        ) : (server.ipv6Only ? '(IPv6)' : null)}\n        <Dialog\n          ref=\"confirmReload\"\n          wstyle=\"w-confirm-reload-dialog w-confirm-reload-global\"\n        >\n          <div className=\"modal-body w-confirm-reload\">\n            <CloseBtn />\n            The proxy has been modified.\n            <br />\n            Do you want to reload this page?\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Cancel\n            </button>\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={this.reload}\n            >\n              Reload\n            </button>\n          </div>\n        </Dialog>\n        <DNSDialog ref=\"dnsDialog\" />\n        <ShortcutsSettings ref=\"shortcutsSettings\" />\n      </a>\n    );\n  }\n});\n\nmodule.exports = Online;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/order-table.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar OrderTable = React.createClass({\n  scrollToTop: function() {\n    ReactDOM.findDOMNode(this.refs.body).scrollTop = 0;\n  },\n  render: function() {\n    var props = this.props;\n    var rowKey = props.rowKey || 'key';\n    var cols = props.cols || [];\n    var rows = props.rows || [];\n    var emptyUrl = props.emptyUrl;\n\n    return (\n      <div className={'w-order-table fill vertical-box' + (props.hide ? ' hide' : '')}>\n        <table className=\"table w-order-table-head\">\n          <thead>\n            <tr>\n              <th>#</th>\n              {cols.map(function(col) {\n                return <th key={col.name} style={{ width: col.width }}>{col.title || col.name}</th>;\n              })}\n            </tr>\n          </thead>\n        </table>\n        <div ref=\"body\" className=\"w-order-table-body fill\">\n          <table className=\"table\">\n            <tbody className=\"w-hover-body\">\n              {rows.length ? rows.map(function(row, i) {\n                return (\n                  <tr key={row[rowKey] || i}>\n                    <th>{i + 1}</th>\n                    {cols.map(function(col) {\n                      var name = col.name;\n                      return <td key={name} style={{ width: col.width }} className={col.className}>{row[name]}</td>;\n                    })}\n                  </tr>\n                );\n              }) : <tr><td colSpan={cols.length + 1} className=\"w-empty\">\n                  {props.loading ? 'Loading...' : (emptyUrl ? <a href={emptyUrl} target=\"_blank\">Empty</a> : 'Empty')}\n                </td></tr>}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = OrderTable;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/overview.js",
    "content": "require('../css/overview.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar columns = require('./columns');\nvar events = require('./events');\nvar util = require('./util');\nvar storage = require('./storage');\nvar Properties = require('./properties');\nvar dataCenter = require('./data-center');\nvar getHelpUrl = require('./protocols').getHelpUrl;\nvar HelpIcon = require('./help-icon');\n\nvar OVERVIEW = [\n  'URL',\n  'Final URL',\n  'Method',\n  'Http Version',\n  'Status Code',\n  'Status Message',\n  'Client IP',\n  'Client Port',\n  'Client ID',\n  'Server IP',\n  'Server Port',\n  'Request Body',\n  'Response Body',\n  'Content Encoding',\n  'Start Date',\n  'TTFB',\n  'DNS',\n  'Request',\n  'Response',\n  'Download',\n  'Total Duration'\n];\nvar OVERVIEW_PROPS = [\n  'url',\n  'realUrl',\n  'req.method',\n  'req.httpVersion',\n  'res.statusCode',\n  'res.statusMessage',\n  'req.ip',\n  'req.port',\n  'clientId',\n  'res.ip',\n  'res.port',\n  'req.size',\n  'res.size',\n  'contentEncoding'\n];\nvar CSS_MAP = {\n  'TTFB': {\n    className: 'w-overview-timeline',\n    style: {\n      '--overview-bg': 'var(--b-active)'\n    }\n  },\n  'DNS': {\n    className: 'w-overview-timeline',\n    style: {\n      '--overview-bg': 'var(--b-tl-dns)'\n    }\n  },\n  'Request': {\n    className: 'w-overview-timeline',\n    style: {\n      '--overview-bg': 'var(--b-tl-req)'\n    }\n  },\n  'Response': {\n    className: 'w-overview-timeline',\n    style: {\n      '--overview-bg': 'var(--b-tl-res)'\n    }\n  },\n  'Download': {\n    className: 'w-overview-timeline',\n    style: {\n      '--overview-bg': 'var(--b-tl-load)'\n    }\n  }\n};\n/**\n * statusCode://, redirect://[statusCode:]url, [req, res]speed://,\n * [req, res]delay://, method://, [req, res][content]Type://自动lookup,\n * cache://xxxs[no], params://json|string(放在url)\n */\nvar PROTOCOLS = require('./protocols').PROTOCOLS;\nvar DEFAULT_OVERVIEW_MODAL = {};\nvar DEFAULT_RULES_MODAL = {};\nvar PROXY_PROTOCOLS = ['socks', 'http-proxy', 'https-proxy'];\n\nfunction getAtRule(rule) {\n  return rule.rawPattern + ' @' + getMatcher(rule).substring(4) + getPluginName(rule);\n}\n\nfunction getVarRule(rule) {\n  return rule.rawPattern + ' %' + getMatcher(rule).substring(4) + getPluginName(rule);\n}\n\nfunction getStr(str) {\n  return str ? ' ' + str : '';\n}\n\nfunction filterImportant(item) {\n  return item.indexOf('important') !== -1;\n}\n\nfunction getPluginName(rule) {\n  return rule && rule.file ? util.SOURCE_SEP + rule.file + ')' : '';\n}\n\nfunction getRawProps(rule, all) {\n  var filter = getStr(rule.filter);\n  rule = rule.rawProps;\n  if (!rule) {\n    return filter;\n  }\n  if (!all) {\n    rule = rule.filter(filterImportant);\n  }\n  return getStr(rule.join(' ')) + filter;\n}\n\nfunction getInjectProps(rule) {\n  if (rule.strictHtml) {\n    return ' enable://strictHtml';\n  }\n\n  return rule.safeHtml ? ' enable://safeHtml' : '';\n}\n\nfunction getMatcher(rule) {\n  return rule._matcher || rule.matcher;\n}\n\nfunction getRuleStr(rule) {\n  if (!rule) {\n    return;\n  }\n  var matcher = getMatcher(rule);\n  if (rule.port) {\n    var protoIndex = matcher.indexOf(':') + 3;\n    var proto = matcher.substring(0, protoIndex);\n    if (matcher.indexOf(':', protoIndex) !== -1) {\n      matcher = proto + '[' + matcher.substring(protoIndex) + ']';\n    }\n    matcher = matcher + ':' + rule.port;\n  }\n  return rule.rawPattern + ' ' + matcher + getRawProps(rule, true);\n}\n\nfunction getTime(time) {\n  return time === '-' ? '' : time;\n}\n\nfunction ignoreProtocol(name) {\n  return PROXY_PROTOCOLS.indexOf(name) !== -1 || name === 'skip' || /^x/.test(name);\n}\n\nOVERVIEW.forEach(function (name) {\n  DEFAULT_OVERVIEW_MODAL[name] = '';\n});\nPROTOCOLS.forEach(function (name) {\n  if (ignoreProtocol(name)) {\n    return;\n  }\n  DEFAULT_RULES_MODAL[name] = '';\n});\n\nvar Overview = React.createClass({\n  getInitialState: function () {\n    return {\n      showOnlyMatchRules: storage.get('showOnlyMatchRules') == 1\n    };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  componentDidMount: function () {\n    var self = this;\n    var container = ReactDOM.findDOMNode(self.refs.container);\n    events.on('overviewScrollTop', function () {\n      if (!util.getBool(self.props.hide)) {\n        container.scrollTop = 0;\n      }\n    });\n  },\n  showOnlyMatchRules: function (e) {\n    var showOnlyMatchRules = e.target.checked;\n    storage.set('showOnlyMatchRules', showOnlyMatchRules ? 1 : 0);\n    this.setState({\n      showOnlyMatchRules: showOnlyMatchRules\n    });\n  },\n  onHelp: function (e) {\n    var name = e.target.getAttribute('data-name');\n    var helpUrl = getHelpUrl(name);\n    if (!helpUrl) {\n      return;\n    }\n    window.open(name === 'rule' ? helpUrl + 'rule/' : helpUrl);\n  },\n  updateCssMap: function () {\n    Object.keys(CSS_MAP).forEach(function (name) {\n      CSS_MAP[name].style['--overview-width'] = 0;\n    });\n    var modal = this.props.modal;\n    if (!modal || !modal.url) {\n      return;\n    }\n    var total = modal.endTime - modal.startTime;\n    if (!(total > 0)) {\n      return;\n    }\n    CSS_MAP['TTFB'].style['--overview-width'] = modal.ttfb * 100 / total + '%';\n    var width = (modal.dnsTime - modal.startTime) * 100 / total + '%';\n    CSS_MAP['DNS'].style['--overview-width'] = width;\n\n    var reqStyle = CSS_MAP['Request'].style;\n    reqStyle['--overview-left'] = width;\n    reqStyle['--overview-width'] = (modal.requestTime - modal.dnsTime) * 100 / total + '%';\n\n    reqStyle = CSS_MAP['Response'].style;\n    reqStyle['--overview-left'] = (modal.requestTime - modal.startTime) * 100 / total + '%';\n    reqStyle['--overview-width'] = (modal.responseTime - modal.requestTime) * 100 / total + '%';\n\n    reqStyle = CSS_MAP['Download'].style;\n    reqStyle['--overview-left'] = (modal.responseTime - modal.startTime) * 100 / total + '%';\n    reqStyle['--overview-width'] = (modal.endTime - modal.responseTime) * 100 / total + '%';\n  },\n  render: function () {\n    var overviewModal = DEFAULT_OVERVIEW_MODAL;\n    var rulesModal = DEFAULT_RULES_MODAL;\n    var modal = this.props.modal;\n    var showOnlyMatchRules = this.state.showOnlyMatchRules;\n    var realUrl, hasPluginRule;\n\n    if (modal) {\n      overviewModal = {};\n      var rawUrl = util.getRawUrl(modal);\n      OVERVIEW.forEach(function (name, i) {\n        var prop = OVERVIEW_PROPS[i];\n        if (prop) {\n          var value = util.getProperty(modal, prop);\n          if (value && prop === 'res.ip') {\n            value = util.getServerIp(modal);\n          } else if (!value && prop === 'clientId') {\n            value = util.getProperty(modal, 'req.headers.x-whistle-client-id');\n          }\n          var isFinalUrl = prop == 'realUrl';\n          if (value != null) {\n            if (prop == 'req.size' || prop == 'res.size') {\n              value = util.formatSize(value, value ? util.getProperty(modal, prop.substring(0, 4) + 'unzipSize') : -1);\n            } else if (isFinalUrl) {\n              if (value == modal.url) {\n                value = '';\n              } else if (modal.isHttps) {\n                value = 'tunnel://' + value;\n              }\n              realUrl = value;\n            } else if (modal.isHttps && prop === 'url') {\n              value = 'tunnel://' + value;\n            }\n          } else if (prop == 'res.statusMessage') {\n            value = util.getStatusMessage(modal.res);\n          }\n          var loc = isFinalUrl && util.getProperty(modal, 'res.headers.location');\n          overviewModal[name] = value;\n          if (loc) {\n            var statusCode = util.getProperty(modal, 'res.statusCode');\n            if (loc && (statusCode == 301 || statusCode == 302  || statusCode == 303 ||\n              statusCode == 307 || statusCode == 308)) {\n              overviewModal['Redirect URL'] = loc;\n            }\n          }\n        } else {\n          var lastIndex = OVERVIEW.length - 1;\n          var time;\n          switch (name) {\n          case OVERVIEW[lastIndex - 6]:\n            time = util.toLocaleString(new Date(modal.startTime));\n            break;\n          case OVERVIEW[lastIndex - 5]:\n            time = modal.ttfb >= 0 ? modal.ttfb + 'ms' : '';\n            break;\n          case OVERVIEW[lastIndex - 4]:\n            time = getTime(modal.dns);\n            break;\n          case OVERVIEW[lastIndex - 3]:\n            if (modal.requestTime) {\n              time = getTime(modal.request);\n              var protocol = modal.protocol;\n              if (\n                  typeof protocol === 'string' &&\n                  protocol.indexOf('>') !== -1\n                ) {\n                var diffTime = modal.httpsTime - modal.dnsTime;\n                if (diffTime > 0) {\n                  time +=\n                      ' - ' +\n                      diffTime +\n                      'ms(' +\n                      protocol +\n                      ') = ' +\n                      (modal.requestTime - modal.httpsTime) +\n                      'ms';\n                }\n              }\n            }\n            break;\n          case OVERVIEW[lastIndex - 2]:\n            time = getTime(modal.response);\n            break;\n          case OVERVIEW[lastIndex - 1]:\n            time = getTime(modal.download);\n            break;\n          case OVERVIEW[lastIndex]:\n            time = getTime(modal.time);\n            if (modal.endTime) {\n              time = modal.endTime - modal.startTime + 'ms';\n            }\n            break;\n          }\n          overviewModal[name] = time;\n        }\n      });\n      var custom1 = columns.getColumn('custom1');\n      var custom2 = columns.getColumn('custom2');\n      if (modal.sniPlugin) {\n        overviewModal['SNI Plugin'] = modal.sniPlugin;\n      }\n      if (custom1.selected) {\n        overviewModal[(dataCenter.custom1 || 'Custom1') + ' '] = modal.custom1;\n      }\n\n      if (custom2.selected) {\n        overviewModal[(dataCenter.custom2 || 'Custom2') + '  '] = modal.custom2;\n      }\n\n      var rules = modal.rules;\n      var titleModal = {};\n      if (rules) {\n        rulesModal = {};\n        var atRule = rules.G;\n        var clientCert = rules.clientCert;\n        var atCtn;\n        var atTitle;\n        if (atRule) {\n          atCtn = [getAtRule(atRule)];\n          atTitle = [atRule.raw];\n        }\n        var pList = rules.P;\n        if (pList) {\n          pList.forEach(function (item) {\n            atCtn = atCtn || [];\n            atCtn.push(getVarRule(item));\n            atTitle = [item.raw];\n          });\n        }\n        if (clientCert) {\n          atCtn = atCtn || [];\n          atTitle = atTitle || [];\n          atCtn.push(getAtRule(clientCert));\n          atTitle.push(clientCert.raw);\n        }\n        if (atCtn) {\n          rulesModal['@'] = atCtn.join('\\n');\n          titleModal['@'] = atTitle.join('\\n');\n        }\n        PROTOCOLS.forEach(function (name) {\n          if (ignoreProtocol(name)) {\n            return;\n          }\n          var key = name;\n          if (name === 'reqScript') {\n            key = 'rulesFile';\n          } else if (name === 'reqMerge') {\n            key = 'params';\n          } else if (name === 'tlsOptions') {\n            key = 'cipher';\n          } else if (name === 'pathReplace') {\n            key = 'urlReplace';\n          }\n          var rule = rules[key];\n          var pluginRule = name === 'plugin' && rules._pluginRule;\n          if (pluginRule) {\n            hasPluginRule = true;\n            var ruleList = [\n              pluginRule.rawPattern + ' ' + getMatcher(pluginRule) + getRawProps(pluginRule) + getPluginName(pluginRule)\n            ];\n            var titleList = [pluginRule.raw];\n            rule && Array.isArray(rule.list) &&\n              rule.list.forEach(function (item) {\n                ruleList.push(item.rawPattern + ' ' + getMatcher(item) + getRawProps(item) + getPluginName(item));\n                titleList.push(item.raw);\n              });\n            rulesModal[name] = ruleList.join('\\n');\n            titleModal[name] = titleList.join('\\n');\n          } else if (rule && Array.isArray(rule.list)) {\n            var prop = getInjectProps(rule);\n            rulesModal[name] = rule.list\n              .map(function (rule) {\n                return rule.rawPattern + ' ' + getMatcher(rule) + getRawProps(rule, true) + prop + getPluginName(rule);\n              })\n              .join('\\n');\n            titleModal[name] = rule.list\n              .map(function (rule) {\n                return rule.raw;\n              })\n              .join('\\n');\n          } else {\n            var ruleStr = getRuleStr(rule);\n            rulesModal[name] = ruleStr;\n            titleModal[name] = rule ? rule.raw : undefined;\n            if (name === 'host') {\n              var result = [];\n              if (ruleStr) {\n                result.push(ruleStr + (realUrl ? ' (URL: ' + realUrl + ')' : '') + getPluginName(rule));\n              }\n              if (rules.proxy && rules.proxy.host) {\n                result.push(\n                  getRuleStr(rules.proxy.host) + ' (URL: ' + getMatcher(rules.proxy) + ')' + getPluginName(rule)\n                );\n              }\n              rulesModal[name] = result.join('\\n');\n            } else {\n              if (name === 'proxy') {\n                if (realUrl && ruleStr) {\n                  rulesModal[name] += ' (URL: ' + realUrl + ')';\n                }\n              }\n              if (rulesModal[name]) {\n                rulesModal[name] += getPluginName(rule);\n              }\n            }\n          }\n        });\n      }\n    }\n    this.updateCssMap();\n    return (\n      <div\n        ref=\"container\"\n        className={\n          'fill v-box w-detail-ctn w-detail-overview' +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <Properties\n          modal={overviewModal}\n          rawName=\"Original URL\"\n          rawValue={rawUrl}\n          showEnableBtn={modal && !modal.importedData}\n          cssMap={CSS_MAP}\n        />\n        <p\n          className=\"w-detail-overview-title\"\n          style={{ background: showOnlyMatchRules ? 'var(--b-filtered)' : undefined }}\n        >\n          <HelpIcon docsUrl=\"rules/protocols.html\" />\n          All Rules:\n          <label>\n            <input\n              checked={showOnlyMatchRules}\n              onChange={this.showOnlyMatchRules}\n              type=\"checkbox\"\n            />\n            Only show matching rules\n          </label>\n        </p>\n        <Properties\n          onHelp={this.onHelp}\n          className={showOnlyMatchRules ? 'w-hide-no-value w-rules-overview' : 'w-rules-overview'}\n          onClickLocate={util.handleClickLocate}\n          modal={rulesModal}\n          title={titleModal}\n          enableCopyValue\n          name=\"Rules\"\n          hasPluginRule={hasPluginRule}\n        />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Overview;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/panel-tips.js",
    "content": "var React = require('react');\nvar events = require('./events');\nvar EnableHttpsBtn = require('./enable-https-btn');\nvar util = require('./util');\n\n\nvar Tips = React.createClass({\n  showFrames: function() {\n    var data = this.props.data || {};\n    events.trigger('showFrames' + (data.inComposer ? 'InComposer' : ''));\n  },\n  render: function () {\n    var data = this.props.data || { hide: true };\n    var className = 'w-textview-tips' + (data.hide ? ' hide' : '');\n    if (data.isFrames) {\n      return (\n        <a className={className} onClick={this.showFrames}>\n          View Frames\n        </a>\n      );\n    }\n    if (data.isHttps) {\n      return (\n        <div className={className}>\n          <p>\n            {data.importedData ? null : <EnableHttpsBtn />}\n            Tunnel\n          </p>\n          <a\n            href={util.getDocUrl('gui/https.html')}\n            target=\"_blank\"\n          >\n            Click here for more information\n          </a>\n        </div>\n      );\n    }\n    return (\n      <div className={className}>\n        <p>{data.message}</p>\n        {data.url ? (\n          <a href={data.url} target=\"_blank\">\n            Open the URL in new window\n          </a>\n        ) : undefined}\n      </div>\n    );\n  }\n});\n\nmodule.exports = Tips;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/parse-curl.js",
    "content": "\nvar STATES = {\n  '-A': 'user-agent',\n  '--user-agent': 'user-agent',\n  '-H': 'headers',\n  '--header': 'headers',\n  '-d': 'data',\n  '--data': 'data',\n  '--data-ascii': 'data',\n  '--data-raw': 'data',\n  '--data-binary': 'data',\n  '--data-urlencode': 'data',\n  '-u': 'user',\n  '--user': 'user',\n  '-X': 'method',\n  '--request': 'method',\n  '-b': 'cookie',\n  '--cookie': 'cookie'\n};\n\nvar pattern =  /\\s*(?:([^\\s\\\\'\"]+)|'((?:[^'\\\\]|\\\\.)*)'|\"((?:[^\"\\\\]|\\\\.)*)\"|(\\\\.?)|(\\S))(\\s|$)?/;\n\nfunction split(str) {\n  var words = [];\n  var field = '';\n\n  var parseMatch = function(match) {\n    if (match[5] != null) {\n      throw new Error('Unmatched quote');\n    }\n    var word = match[1];\n    var sq = match[2];\n    var dq = match[3];\n    var escape = match[4];\n    var separator = match[6];\n    if (word) {\n      field += word;\n    } else {\n      var addition = sq || dq || escape;\n      if (addition) {\n        field += addition;\n      }\n    }\n    if (separator != null) {\n      words.push(field);\n      field = '';\n    }\n  };\n\n  while (str.length > 0) {\n    var match = str.match(pattern);\n    if (match && match.index != null && match[0] != null) {\n      parseMatch(match);\n      str = str.slice(match.index + match[0].length);\n    } else {\n      str = '';\n    }\n  }\n  if (field) {\n    words.push(field);\n  }\n  return words;\n}\n\nfunction parseArgs(s) {\n  return split(s).reduce(function(result, a){\n    if (!a) {\n      return result;\n    }\n    if (a.indexOf('-X')) {\n      result.push(a);\n    } else {\n      result.push('-X');\n      if ( a = a.substring(2)) {\n        result.push(a);\n      }\n    }\n    return result;\n  }, []);\n}\n\nmodule.exports = function(s) {\n  s = s.trim();\n  if (s.indexOf('curl ')) {\n    return;\n  }\n  var args = parseArgs(s);\n  var result = { method: 'GET', headers: {} };\n  var headers = result.headers;\n  var state = '';\n\n  args.forEach(function(arg){\n    if (/^https?:\\/\\//.test(arg)) {\n      result.url = arg;\n      return;\n    }\n    if (arg === '--compressed') {\n      headers['accept-encoding'] = headers['accept-encoding'] || 'deflate, gzip';\n      return;\n    }\n    if ( arg === '-I' || arg === '--head') {\n      result.method = 'HEAD';\n      return;\n    }\n    if (STATES[arg]) {\n      state = STATES[arg];\n      return;\n    }\n    if (!state) {\n      return;\n    }\n    switch (state) {\n    case 'method':\n      result.method = arg.toUpperCase();\n      break;\n    case 'user':\n      try {\n        headers['authorization'] = 'Basic ' + btoa(arg);\n      } catch (e) {}\n      break;\n    case 'user-agent':\n      headers['user-agent'] = arg;\n      break;\n    case 'cookie':\n      headers.cookie = arg;\n      break;\n    case 'headers':\n      var index = arg.indexOf(': ');\n      if (index !== -1) {\n        arg = arg.replace(/(^|.)\\\\\"/mg, function(_, ch) {\n          return ch === '\\\\' ? '\\\\\"' : (ch || '') + '\"';\n        });\n        var name = arg.substring(0, index).trim().toLowerCase();\n        headers[name] = arg.substring(index + 2).trim();\n      }\n      break;\n    case 'data':\n      if (result.method == 'GET' || result.method == 'HEAD') {\n        result.method = 'POST';\n      }\n      headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded';\n      result.body = result.body ? result.body + '&' + arg : arg;\n      break;\n    }\n    state = '';\n  });\n  return result;\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/parse-rules.js",
    "content": "var util = require('./util');\n\nvar SPACE_RE = /\\s+/g;\nvar IP_PORT_RE = /^(?:\\[([:\\da-f.]+)\\]|(?:\\d{1,3}\\.){3}\\d{1,3})(?::(\\d+))?$/i;\nvar SCHEMA_RE = /^\\/\\//;\nvar REG_EXP_RE = /^\\/(.+)\\/(i?u?|ui)$/;\nvar SPECIAL_RE = /^[\\^!$.:*~]/;\nvar WEB_PROTOCOL_RE = /^(?:https?|wss?|tunnel):\\/\\//;\nvar URL_RE = /^[^@%\\\\/\\{\\}\\(\\)<>]*[^@%\\\\/\\{\\}\\(\\)<>:](?:\\/|$)/;\nvar OLD_FILTER_RE = /^filter:\\/\\/\\w+:.+$/;\nvar FILTER_RE = /^(lineProps|excludeFilter|includeFilter):\\/\\/.*$/;\n\n\nfunction isPattern(item) {\n  return (\n    SPECIAL_RE.test(item) ||\n    SCHEMA_RE.test(item) ||\n    REG_EXP_RE.test(item) ||\n    WEB_PROTOCOL_RE.test(item)\n  );\n}\n\nfunction getPatternIndex(list) {\n  var ipIndex = -1;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (isPattern(item)) {\n      return i;\n    }\n    if (URL_RE.test(item)) {\n      if (!IP_PORT_RE.test(item)) {\n        return i;\n      } else if (ipIndex === -1) {\n        ipIndex = i;\n      }\n    }\n  }\n  return ipIndex;\n}\n\nfunction getFilters(list) {\n  var filters = [];\n  list = list.filter(function (matcher) {\n    if (FILTER_RE.test(matcher) || OLD_FILTER_RE.test(matcher)) {\n      filters.push(matcher);\n      return false;\n    }\n    return true;\n  });\n  return {\n    list: list,\n    filters: filters\n  };\n}\n\nmodule.exports = function (str) {\n  if (!str) {\n    return '';\n  }\n  var values = {};\n  var rawValues = {};\n  var rules = [];\n  var map = {};\n  var addRules = function(rule) {\n    var l = rule.join(' ');\n    if (!map[l]) {\n      map[l] = 1;\n      rules.push(rule);\n    }\n  };\n  util.formatRules(str, values, rawValues).forEach(function (line) {\n    line = line.trim();\n    if (!line) {\n      return;\n    }\n    var list = line.split(SPACE_RE);\n    if (list.length < 2) {\n      if (line[0] === '@' || line[0] === '%') {\n        addRules([line]);\n      }\n      return;\n    }\n    var data = getFilters(list);\n    list = data.list;\n    var index = getPatternIndex(list);\n    if (index === -1) {\n      return addRules([line]);\n    }\n    if (index === 0) {\n      var pattern = list.shift();\n      list.forEach(function (op) {\n        addRules([pattern, op, ...data.filters]);\n      });\n    } else {\n      var opList = [];\n      var patternList = list.filter(function (p) {\n        if (isPattern(p) || URL_RE.test(p)) {\n          return true;\n        }\n        opList.push(p);\n      });\n      opList.forEach(function (op) {\n        patternList.forEach(function (pattern) {\n          addRules([pattern, op, ...data.filters]);\n        });\n      });\n    }\n  });\n  return {\n    rules: rules,\n    values: values,\n    rawValues: rawValues\n  };\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/plugins-mgr.js",
    "content": "require('../css/plugins-mgr.css');\nvar React = require('react');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar storage = require('./storage');\nvar CloseBtn = require('./close-btn');\n\nvar REGISTRY_RE = /^--registry=https?:\\/\\/[^/?]/;\nvar SEP_RE = /\\s*[|,;\\s]+\\s*/;\n\nfunction getRegistry(cmd) {\n  if (!cmd || typeof cmd !== 'string') {\n    return;\n  }\n  cmd = cmd.trim().split(SEP_RE);\n  for (var i = 0, len = cmd.length; i < len; i++) {\n    var name = cmd[i].trim();\n    if (REGISTRY_RE.test(name)) {\n      return name.substring(11, 1035);\n    }\n  }\n}\n\nvar PluginsMgr = React.createClass({\n  getInitialState: function () {\n    return { list: [] };\n  },\n  handleCallback: function (data, xhr) {\n    if (util.showHandlePluginInfo(data, xhr)) {\n      var registry = getRegistry(this._cmd);\n      if (registry) {\n        dataCenter.plugins.addRegistry({ registry: registry }, function (data) {\n          if (data && data.ec === 0) {\n            storage.set('pluginsRegistry', registry);\n          }\n        });\n      }\n    }\n  },\n  installPlugin: function () {\n    dataCenter.plugins.installPlugins({ cmd: this._cmd  }, this.handleCallback);\n  },\n  installPluginExt: function (plugin) {\n    var installPlugin = dataCenter.createCgi(util.getPluginCgiUrl(plugin.moduleName, plugin.installUrl), false, true);\n    installPlugin({ cmd: this._cmd  }, this.handleCallback);\n  },\n  show: function (cmd, list, isUpdate) {\n    var self = this;\n    list = list || [];\n    var len = list.length;\n    self._cmd = cmd;\n    self._hideDialog = false;\n    if (!len) {\n      return self.installPlugin();\n    }\n    self.setState({\n      isUpdate: isUpdate,\n      list: list\n    }, function() {\n      self.refs.pluginsMgr.show();\n    });\n  },\n  hide: function () {\n    this.refs.pluginsMgr.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var list = state.list || [];\n    var isUpdate = state.isUpdate;\n    var actionText = isUpdate ? 'Update' : 'Install';\n\n    return (\n      <Dialog ref=\"pluginsMgr\" wstyle=\"w-plugins-mgr-dialog\">\n        <div className=\"modal-header\">\n          <h4>Select {isUpdate ? 'Updater' : 'Installer'}</h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body\">\n          <div className=\"btn btn-primary plugin-mgr-btn\" data-dismiss=\"modal\" onClick={self.installPlugin}>\n            {actionText} <span>(Use Default)</span>\n          </div>\n          {\n            list.map(function (item) {\n              return (\n                <div className=\"btn btn-default plugin-mgr-btn\" data-dismiss=\"modal\"\n                  key={item.moduleName} onClick={self.installPluginExt.bind(self, item)}>\n                  {actionText} <span>(Use plugin {util.getSimplePluginName(item.moduleName)})</span>\n                </div>\n              );\n            })\n          }\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = PluginsMgr;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/plugins-tabs.js",
    "content": "var React = require('react');\nvar util = require('./util');\nvar TabMgr = require('./tab-mgr');\n\nvar PluginsTabs = React.createClass({\n  getInitialState: function () {\n    var tab = this.props.tabs[0];\n    return {\n      active: tab && tab.plugin\n    };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onSelect: function (tab) {\n    this.setState({ active: tab.plugin });\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var tabs = props.tabs;\n    var hide = props.hide;\n    var active = this.state.active;\n    var single = tabs.length < 2;\n    if (single) {\n      active = tabs[0] && tabs[0].plugin;\n      if (active) {\n        this.state.active = active;\n      }\n    }\n    return (\n      <div className={'fill box w-plugins-tabs' + (hide ? ' hide' : '')}>\n        <div className={'w-plugins-tabs-list' + (single ? ' hide' : '')}>\n          {tabs.map(function (tab) {\n            return (\n              <button\n                key={tab.plugin}\n                onClick={function () {\n                  self.onSelect(tab);\n                }}\n                className={\n                  'btn btn-default' + (active == tab.plugin ? ' active' : '')\n                }\n                title={'[' + tab.plugin + '] ' + tab.name}\n              >\n                {tab.name}\n              </button>\n            );\n          })}\n        </div>\n        <TabMgr active={active} hide={hide} tabs={tabs} />\n      </div>\n    );\n  }\n});\n\nmodule.exports = PluginsTabs;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/plugins.js",
    "content": "require('../css/plugins.css');\nvar $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar events = require('./events');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar win = require('./win');\nvar LazyInit = require('./lazy-init');\nvar storage = require('./storage');\nvar PluginsMgr = require('./plugins-mgr');\nvar ContextMenu = require('./context-menu');\nvar iframes = require('./iframes');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar CMD_RE = /^([\\w]{1,12})(\\s+-g)?$/;\nvar WHISTLE_PLUGIN_RE = /(?:^|[\\s,;|])(?:@[\\w.~-]+\\/)?whistle\\.[a-z\\d_-]+(?:\\@[\\w.^~*-]*)?(?:$|[\\s,;|])/;\nvar PLUGIN_NAME_RE = /^((?:@[\\w.~-]+\\/)?whistle\\.[a-z\\d_-]+)(?:\\@([\\w.^~*-]*))?$/;\nvar SPACE_RE = /\\s+$/;\nvar REGISTRY_RE = /^--registry=https?:\\/\\/[^/?]/;\nvar SEP_RE = /\\s*[|,;\\s]+\\s*/;\nvar SELECT_STYLE = {width: 270};\nvar pendingEnable;\nvar registryCache;\n\nvar CTX_MENU_LIST = [\n  { name: 'Copy' },\n  { name: 'Disable' },\n  { name: 'Option' },\n  { name: 'Rules' },\n  { name: 'Update' },\n  { name: 'Uninstall' },\n  { name: 'Install' },\n  {\n    name: 'Others',\n    action: 'Plugins',\n    list: []\n  },\n  { name: 'Help', sep: true }\n];\n\nfunction getPluginList(plugins, installUrls, disabledPlugins, disabledAllPlugins) {\n  if (!plugins) {\n    return [];\n  }\n  return Object.keys(plugins).sort(util.getPluginComparator(plugins))\n        .map(function (name) {\n          var plugin = plugins[name];\n          if (!disabledAllPlugins && !dataCenter.disableInstaller && plugin && installUrls && disabledPlugins\n            && plugin.installUrl && !disabledPlugins[name.slice(0, -1)]) {\n            installUrls.push(plugin);\n          }\n          return plugin;\n        });\n}\n\nfunction parseRegistry(cmd) {\n  if (!cmd) {\n    return '';\n  }\n  cmd = cmd.split(SEP_RE);\n  for (var i = 0, len = cmd.length; i < len; i++) {\n    var name = cmd[i];\n    if (REGISTRY_RE.test(name)) {\n      return name.substring(11, 1035);\n    }\n  }\n  return '';\n}\n\nfunction enableAllPlugins(e) {\n  if (pendingEnable) {\n    return;\n  }\n  pendingEnable = setTimeout(function () {\n    pendingEnable = null;\n  }, 2000);\n  events.trigger('disableAllPlugins', e);\n}\n\nfunction getArgvs(account, dir) {\n  var params = account ? ' --account=' + account : '';\n  return params + (dir ? ' --dir=' + dir : '');\n}\n\nfunction getCmd(addArgv) {\n  var cmdName = dataCenter.getServerInfo().cmdName;\n  var g = '';\n  if (cmdName && CMD_RE.test(cmdName)) {\n    cmdName = RegExp.$1 + ' ';\n    g = ' ' + RegExp.$2.trim();\n  } else {\n    cmdName = 'w2 ';\n  }\n  return cmdName + 'install' + g + ' ' + (addArgv ? getArgvs(dataCenter.account, dataCenter.whistleName) : '');\n}\n\nwindow.getWhistleProxyServerInfo = function () {\n  var serverInfo = dataCenter.getServerInfo();\n  return serverInfo && $.extend(true, {}, serverInfo);\n};\n\nfunction getParams(plugin) {\n  return getArgvs(plugin.account, plugin.dir);\n}\n\nfunction getUpdateUrl(plugin) {\n  return plugin.updateUrl || plugin.moduleName;\n}\n\nfunction hasRules(plugin) {\n  return plugin.rules || plugin._rules || plugin.resRules;\n}\n\nfunction isOpenExternal(plugin) {\n  return (plugin.pluginHomepage || plugin.openExternal) && !plugin.openInPlugins && !plugin.openInModal;\n}\n\nfunction getHomePage(plugin) {\n  return plugin.pluginHomepage || 'plugin.' + util.getSimplePluginName(plugin) + '/';\n}\n\nfunction getRegistry(url) {\n  return /^https?:\\/\\/[^/?]/.test(url) && url.length <= 1024 ? url : '';\n}\n\nfunction parsePluginName(list, registry) {\n  if (util.isString(list)) {\n    list = list.split(/[\\s,;|]+/);\n  } else if (!Array.isArray(list)) {\n    return;\n  }\n  var plugins = [];\n  list.forEach(function (name) {\n    if (util.isString(name)) {\n      name = name.trim();\n      if (PLUGIN_NAME_RE.test(name)) {\n        plugins.push(name);\n      }\n    }\n  });\n  return plugins.length && {\n    plugins: plugins.join(' '),\n    registry: getRegistry(registry)\n  };\n}\n\nvar Home = React.createClass({\n  componentDidMount: function () {\n    var self = this;\n    var pluginsElem = $(findDOMNode(self.refs.plugins));\n    self.setUpdateAllBtnState();\n    events.on('openPluginOption', function(_, plugin) {\n      if (!plugin) {\n        return;\n      }\n      if (plugin.openInModal) {\n        return events.trigger('showPluginOption', plugin);\n      }\n      var url = getHomePage(plugin);\n      if (isOpenExternal(plugin)) {\n        return window.open(url);\n      }\n      events.trigger('showPluginOptionTab', plugin);\n    });\n    events.on('showUninstallPlugin', function(_, plugin) {\n      self.showUninstall(plugin);\n    });\n    events.on('showInstallPlugin', function(_, plugin) {\n      self.showUpdate(plugin);\n    });\n    events.on('showPluginRules', function(_, plugin) {\n      self.onShowRules(util.getSimplePluginName(plugin));\n    });\n    events.on('installPlugins', function() {\n      self.showInstall();\n    });\n    events.on('showInstallPlugins', function(_, list, registry) {\n      self.showInstall(list, registry);\n    });\n    events.on('showUpdatePlugins', function(_, list, registry) {\n      self.showInstall(list, registry, true);\n    });\n    events.on('highlightPlugin', function(_, name) {\n      if (!name) {\n        return;\n      }\n      var elem = pluginsElem.find('.w-plugins-item[data-name=\"' + name + '\"]');\n      if (elem.length) {\n        util.shakeElem(elem);\n      }\n    });\n    events.on('updateAllPlugins', function () {\n      var data = self.props.data || {};\n      var plugins = data.plugins || {};\n      var newPlugins = {};\n      var registry;\n      getPluginList(plugins).forEach(function (plugin) {\n        if (!plugin || plugin.isProj || !util.compareVersion(plugin.latest, plugin.version)) {\n          return;\n        }\n        registry = registry || plugin.registry;\n        var account = getParams(plugin);\n        var list = newPlugins[account] || [];\n        list.push(getUpdateUrl(plugin));\n        newPlugins[account] = list;\n      });\n      var cmdMsg = Object.keys(newPlugins)\n        .map(function (registry) {\n          var list = newPlugins[registry].join(' ');\n          return getCmd() + list + registry;\n        })\n        .join('\\n\\n');\n      cmdMsg && self.getRegistryList(function(result, r) {\n        self.setState({\n          cmdMsg: cmdMsg,\n          install: false,\n          registry: r || registry || '',\n          registryChanged: true,\n          registryList: result\n        }, self.showMsgDialog);\n      });\n    });\n  },\n  getRegistryList: function(cb) {\n    var list = [];\n    if (this.installUrls) {\n      this.installUrls.forEach(function(plugin) {\n        var reg = plugin.installUrl && plugin.installRegistry;\n        Array.isArray(reg) && reg.forEach(function(r) {\n          if (r && list.indexOf(r) === -1) {\n            list.push(r);\n          }\n        });\n      });\n    }\n    dataCenter.plugins.getRegistryList(function(data, xhr) {\n      var registry = storage.get('pluginsRegistry');\n      if (!data) {\n        util.showSystemError(xhr);\n      } else if (data.ec !== 0) {\n        win.alert(data.em);\n      } else {\n        registryCache = dataCenter.getPluginRegistry();\n        data.list.forEach(function(url) {\n          if (registryCache.indexOf(url) === -1) {\n            registryCache.push(url);\n          }\n        });\n      }\n      if (list.length) {\n        registryCache = registryCache || [];\n        list.forEach(function(url) {\n          if (registryCache.indexOf(url) === -1) {\n            registryCache.push(url);\n          }\n        });\n      }\n      if (!registryCache) {\n        return;\n      }\n      if (!REGISTRY_RE.test('--registry=' + registry)) {\n        registry = '';\n      }\n      cb(registryCache, registry);\n    });\n  },\n  componentDidUpdate: function () {\n    this.setUpdateAllBtnState();\n  },\n  execCmd: function() {\n    var state = this.state;\n    var install = state.install;\n    var cmd = install ? state.installMsg : state.cmdMsg;\n    if (!cmd) {\n      return;\n    }\n    // if (state.registryList && state.registry) {\n    //   cmd += ' --registry=' + state.registry;\n    // }\n    this.refs.pluginsMgrDialog.show(cmd, this.installUrls, !install);\n  },\n  onOpen: function (e) {\n    var name = e.target.getAttribute('data-name');\n    var data = this.props.data;\n    var plugin = data && data.plugins && data.plugins[name + ':'] || {};\n    if (plugin.openInModal) {\n      events.trigger('showPluginOption', plugin);\n    } else {\n      this.props.onOpen && this.props.onOpen(e);\n    }\n    e.preventDefault();\n  },\n  syncData: function (plugin) {\n    dataCenter.syncData(plugin);\n  },\n  showDialog: function () {\n    this.refs.pluginRulesDialog.show();\n  },\n  hideDialog: function () {\n    this.refs.pluginRulesDialog.hide();\n  },\n  onShowRules: function(name) {\n    var plugin = this.props.data.plugins[name + ':'];\n    plugin.name = name;\n    this.setState(\n      {\n        plugin: plugin\n      },\n      this.showDialog\n    );\n  },\n  showRules: function (e) {\n    var name = $(e.target).attr('data-name');\n    this.onShowRules(name);\n  },\n  onCmdChange: function (e) {\n    var self = this;\n    var cmd = e.target.value;\n    self.updateCmdMsg(cmd);\n    self.updateCmdTimer = self.updateCmdTimer || setTimeout(function() {\n      self.updateCmdTimer = null;\n      self.setRegistry(parseRegistry(cmd), false);\n    }, 1000);\n  },\n  showMsgDialog: function () {\n    var self = this;\n    self.refs.operatePluginDialog.show();\n    if (self.state.install) {\n      setTimeout(function() {\n        findDOMNode(self.refs.textarea).focus();\n      }, 600);\n    }\n  },\n  updateCmdMsg: function(msg, cb) {\n    if (this.state.install) {\n      this.setState({ installMsg: msg }, cb);\n    } else {\n      this.setState({ cmdMsg: msg }, cb);\n    }\n  },\n  setRegistry: function(registry, changed) {\n    this.setState({ registry: registry, registryChanged: changed !== false });\n    storage.set('pluginsRegistry', registry);\n  },\n  onRegistry: function(e) {\n    var registry = e.target.value;\n    if (registry === '+Add') {\n      var textarea = findDOMNode(this.refs.textarea);\n      var pkgs = [];\n      var regs = [];\n      var regCmdName = '--registry=';\n      textarea.value.trim().split(/\\s+/).forEach(function(cmd) {\n        if (cmd.indexOf(regCmdName)) {\n          pkgs.push(cmd);\n        } else {\n          regs.push(cmd);\n        }\n      });\n      if (!regs.length) {\n        regs.push(regCmdName);\n      }\n      this.updateCmdMsg(pkgs.concat(regs).join(' '), function() {\n        textarea.focus();\n      });\n      return;\n    }\n    this.setRegistry(registry);\n  },\n  onShowUpdate: function(e) {\n    var self = this;\n    var name = $(e.target).attr('data-name');\n    var plugin = self.props.data.plugins[name + ':'];\n    dataCenter.checkPluginUpdates(plugin, function(showService) {\n      if (showService) {\n        return util.showService('/plugins/store?plugin=' + encodeURIComponent(name));\n      }\n      self.showUpdate(plugin);\n    });\n  },\n  showUpdate: function (plugin) {\n    var self = this;\n    self.getRegistryList(function(result, r) {\n      self.setState(\n        {\n          cmdMsg: getCmd() + getUpdateUrl(plugin) + getParams(plugin),\n          isSys: plugin.isSys,\n          install: false,\n          registry: r || plugin.registry || '',\n          registryChanged: true,\n          registryList: result\n        },\n        self.showMsgDialog\n      );\n    });\n  },\n  showInstall: function(list, registry, isUpdate) {\n    var self = this;\n    self.getRegistryList(function(result, r) {\n      var state = {\n        install: !isUpdate,\n        registryList: result,\n        registry: r || '',\n        registryChanged: true\n      };\n      var options = parsePluginName(list, registry);\n      if (options) {\n        var cmdMsg = getCmd(true) + ' ' + options.plugins;\n        state.cmdMsg = cmdMsg;\n        state.installMsg = cmdMsg;\n        if (options.registry) {\n          state.registry = options.registry;\n        }\n      }\n      self.setState(state, self.showMsgDialog);\n    });\n  },\n  onShowUninstall: function(e) {\n    var name = $(e.target).attr('data-name');\n    var plugin = this.props.data.plugins[name + ':'];\n    this.showUninstall(plugin);\n  },\n  showUninstall: function (plugin) {\n    var name = plugin.moduleName;\n    win.confirm('Do you confirm uninstalling the plugin \\'' + name + '\\'?', function(ok) {\n      if (ok) {\n        dataCenter.plugins.uninstallPlugins({ name: util.getSimplePluginName(plugin) }, function(data, xhr) {\n          if (!data) {\n            return util.showSystemError(xhr);\n          }\n          if (data.ec) {\n            return win.alert((data.em || 'Error') + ', ' + 'try again or manually delete the directory:\\n' + plugin.path,\n            plugin.path\n          , 'Copy Directory Path');\n          }\n          util.showHandlePluginInfo(data);\n        });\n      }\n    });\n  },\n  enableAllPlugins: function (e) {\n    var self = this;\n    var data = self.props.data || {};\n    if (pendingEnable || !data.disabledAllPlugins) {\n      return;\n    }\n    win.confirm('Do you want to turn on Plugins?', function (sure) {\n      sure && enableAllPlugins(e);\n    });\n  },\n  setUpdateAllBtnState: function () {\n    events.trigger('setUpdateAllBtnState', this.hasNewPlugin);\n  },\n  showService: function () {\n    util.showService('/plugins/store');\n  },\n  handleLeave: function () {\n    this.setState({ copied: false });\n  },\n  handleCopy: function () {\n    this.setState({ copied: true });\n  },\n  render: function () {\n    var self = this;\n    var state = self.state || {};\n    var data = self.props.data || {};\n    var plugins = data.plugins || {};\n    var installUrls = [];\n    var disabledPlugins = data.disabledPlugins || {};\n    var list = getPluginList(plugins, installUrls, disabledPlugins, data.disabledAllPlugins);\n    var plugin = state.plugin || {};\n    var install = state.install;\n    var cmdMsg = (install ? state.installMsg : state.cmdMsg) || '';\n    var registryList = state.registryList || [];\n    var registry = state.registry || '';\n    var disabled = data.disabledAllPlugins;\n    var ndp = data.ndp;\n    self.hasNewPlugin = false;\n    self.installUrls = installUrls;\n\n    if (state.registryChanged) {\n      state.registryChanged = false;\n      var regCmd = registry ? ' --registry=' + registry + '  ' : '';\n      if (cmdMsg) {\n        var r = SPACE_RE.exec(cmdMsg);\n        var spaces = r && r[0];\n        cmdMsg = cmdMsg.split('\\n').map(function(line) {\n          line = line.trim();\n          line = line.split(/\\s+/).filter(function(cmd) {\n            return cmd.indexOf('--registry') !== 0;\n          }).join(' ');\n          if (line && regCmd) {\n            line += regCmd;\n          }\n          return line;\n        }).filter(util.noop).join('\\n');\n        if (spaces && !registry) {\n          cmdMsg += spaces;\n        } else if (cmdMsg === 'w2 install') {\n          cmdMsg += ' ';\n        }\n      } else {\n        cmdMsg = getCmd(true) + ' ' + (regCmd ? regCmd : '');\n      }\n      state.cmdMsg = cmdMsg;\n      state.installMsg = cmdMsg;\n    }\n\n    var disabledBtn = !WHISTLE_PLUGIN_RE.test(cmdMsg);\n    var selectStyle = dataCenter.whistleId ? SELECT_STYLE : undefined;\n\n    return (\n      <div\n        ref=\"plugins\"\n        className=\"fill v-box w-plugins\"\n        style={{ display: self.props.hide ? 'none' : '' }}\n      >\n        <div className=\"w-plugins-headers\">\n          <table className=\"table\">\n            <thead>\n              <tr>\n                <th className=\"w-plugins-order\">#</th>\n                <th className=\"w-plugins-active\">Active</th>\n                <th className=\"w-plugins-date\">Date</th>\n                <th className=\"w-plugins-name\">Name</th>\n                <th className=\"w-plugins-version\">Version</th>\n                <th className=\"w-plugins-operation\">Operation</th>\n                <th className=\"w-plugins-desc\">Description</th>\n              </tr>\n            </thead>\n          </table>\n        </div>\n        <div className=\"fill w-plugins-list\">\n          <table className=\"table table-hover\">\n            <tbody>\n              {list.length ? (\n                list.map(function (plugin, i) {\n                  var name = util.getSimplePluginName(plugin);\n                  var checked = !disabledPlugins[name];\n                  var openInModal = plugin.openInModal;\n                  var openExternal = isOpenExternal(plugin);\n                  var url = getHomePage(plugin);\n                  var homepage = plugin.homepage;\n                  var hasNew = util.compareVersion(\n                    plugin.latest,\n                    plugin.version\n                  );\n                  if (hasNew) {\n                    hasNew = ' (NEW: ' + plugin.latest + ')';\n                    self.hasNewPlugin = true;\n                  }\n                  return (\n                    <tr\n                      key={name}\n                      data-name={name}\n                      className={\n                        'w-plugins-item' +\n                        (!disabled && checked ? '' : ' w-plugins-disable') +\n                        (hasNew ? ' w-has-new-version' : '')\n                      }\n                    >\n                      <th\n                        className=\"w-plugins-order\"\n                        onDoubleClick={self.enableAllPlugins}\n                      >\n                        {i + 1}\n                      </th>\n                      <td\n                        className=\"w-plugins-active\"\n                        onDoubleClick={self.enableAllPlugins}\n                      >\n                        <input\n                          type=\"checkbox\"\n                          title={\n                            ndp\n                              ? 'Not allowed disable plugins'\n                              : disabled\n                              ? 'Disabled'\n                              : (checked ? 'Disable ' : 'Enable ') + name\n                          }\n                          data-name={name}\n                          checked={ndp || checked}\n                          disabled={!ndp && disabled}\n                          onChange={self.props.onChange}\n                          className={ndp ? 'w-not-allowed' : undefined}\n                        />\n                      </td>\n                      <td className=\"w-plugins-date\">\n                        {util.toLocaleString(new Date(plugin.mtime))}\n                      </td>\n                      <td className=\"w-plugins-name\" title={plugin.moduleName}>\n                      {plugin.noOpt ? <span>{name}</span> : <a\n                          href={openInModal ? null : url}\n                          target=\"_blank\"\n                          data-name={name}\n                          title={'Show ' + plugin.moduleName + ' Option'}\n                          onClick={openExternal ? null : self.onOpen}\n                        >\n                          {name}\n                        </a>}\n                      </td>\n                      <td className=\"w-plugins-version\">\n                        {homepage ? (\n                          <a href={homepage} target=\"_blank\" title={'Show ' + plugin.moduleName + ' Help'}>\n                            {plugin.version}\n                          </a>\n                        ) : plugin.version}\n                        {hasNew ? <a\n                          className=\"w-new-version\"\n                          data-name={name}\n                          onClick={self.onShowUpdate}\n                          title={'Update ' + plugin.moduleName}\n                        >{hasNew}</a> : null}\n                      </td>\n                      <td className=\"w-plugins-operation\">\n                        {plugin.noOpt ? <span className=\"disabled\">Option</span> : <a\n                          href={openInModal ? null : url}\n                          target=\"_blank\"\n                          data-name={name}\n                          className=\"w-plugin-btn\"\n                          onClick={openExternal ? null : self.onOpen}\n                        >\n                          Option\n                        </a>}\n                        {hasRules(plugin) ? (\n                          <a\n                            draggable=\"false\"\n                            data-name={name}\n                            onClick={self.showRules}\n                          >\n                            Rules\n                          </a>\n                        ) : (\n                          <span className=\"disabled\">Rules</span>\n                        )}\n                        {plugin.isProj ? (\n                          <span className=\"disabled\">Update</span>\n                        ) : (\n                          <a\n                            draggable=\"false\"\n                            className=\"w-plugin-btn w-plugin-update-btn\"\n                            data-name={name}\n                            onClick={self.onShowUpdate}\n                          >\n                            Update\n                          </a>\n                        )}\n                        {(plugin.isProj || plugin.notUn) ? (\n                          <span className=\"disabled\">Uninstall</span>\n                        ) : (\n                          <a\n                            draggable=\"false\"\n                            className=\"w-delete\"\n                            data-name={name}\n                            onClick={self.onShowUninstall}\n                          >\n                            Uninstall\n                          </a>\n                        )}\n                        {homepage ? (\n                          <a\n                            href={homepage}\n                            className=\"w-plugin-btn\"\n                            target=\"_blank\"\n                          >\n                            Help\n                          </a>\n                        ) : (\n                          <span className=\"disabled\">Help</span>\n                        )}\n                        {util.isString(plugin.rulesUrl) ||\n                        util.isString(plugin.valuesUrl) ? (\n                          <a\n                            className=\"w-plugin-btn\"\n                            onClick={function () {\n                              self.syncData(plugin);\n                            }}\n                          >\n                            Sync\n                          </a>\n                        ) : undefined}\n                      </td>\n                      <td className=\"w-plugins-desc\" title={plugin.description}>\n                        {plugin.description}\n                      </td>\n                    </tr>\n                  );\n                })\n              ) : (\n                <tr>\n                  <td colSpan=\"7\" className=\"w-empty\">\n                    <a\n                      href=\"https://github.com/whistle-plugins\"\n                      target=\"_blank\"\n                    >\n                      Empty\n                    </a>\n                  </td>\n                </tr>\n              )}\n            </tbody>\n          </table>\n        </div>\n        <Dialog ref=\"pluginRulesDialog\" wstyle=\"w-plugin-rules-dialog\">\n          <div className=\"modal-header\">\n            <h4>{plugin.name}</h4>\n            <CloseBtn />\n          </div>\n          <div className=\"modal-body\">\n            <div className=\"w-plugin-rules\">\n              {plugin.rules ? (\n                <fieldset>\n                  <legend>rules.txt</legend>\n                  <pre>{plugin.rules}</pre>\n                </fieldset>\n              ) : null}\n              {plugin._rules ? (\n                <fieldset>\n                  <legend>reqRules.txt (_rules.txt)</legend>\n                  <pre>{plugin._rules}</pre>\n                </fieldset>\n              ) : null}\n              {plugin.resRules ? (\n                <fieldset>\n                  <legend>resRules.txt</legend>\n                  <pre>{plugin.resRules}</pre>\n                </fieldset>\n              ) : null}\n            </div>\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Close\n            </button>\n          </div>\n        </Dialog>\n        <Dialog ref=\"operatePluginDialog\" wstyle=\"w-plugin-update-dialog\">\n          <div className=\"modal-body\">\n            <h5>{\n              install ? 'Enter plugin name(s) (space-separated) and click Install:' : 'Update the following plugin(s):'\n            }</h5>\n            <textarea\n              ref=\"textarea\"\n              value={cmdMsg || ''}\n              placeholder={install ? 'SUCH AS: whistle.inspect whistle.abc@1.0.0 @scope/whistle.xyz' : undefined}\n              className={'w-plugin-update-cmd' + (install ? ' w-plugin-install' : '')}\n              maxLength=\"600\"\n              onChange={this.onCmdChange}\n              onKeyDown={util.handleTab}\n            />\n            <a\n              onMouseLeave={this.handleLeave}\n              onClick={this.handleCopy}\n              className={'w-copy-text-with-tips w-plugin-copy-cmd' + (state.copied ? ' w-copied-text' : '')}\n              data-clipboard-text={state.copied ? null : cmdMsg}\n            >{state.copied ? 'Copied' : 'Copy'}</a>\n          </div>\n          <div className=\"modal-footer\">\n            {(registryList.length || registry) ? <label className=\"w-registry-list\">\n              <strong>--registry=</strong>\n              <select className=\"form-control\" value={registry} onChange={this.onRegistry} style={selectStyle}>\n                <option value=\"\">None</option>\n                {\n                  registryList.map(function(url) {\n                    return (\n                      <option key={url} value={url}>{url}</option>\n                    );\n                  })\n                }\n                {registry && registryList.indexOf(registry) === -1 ? <option key={registry} value={registry}>{registry}</option> : null}\n                <option value=\"+Add\">+Add</option>\n              </select>\n            </label> : <label className=\"w-registry-list\">\n              <strong>--registry=</strong>\n              <select className=\"form-control\" value={registry} onChange={this.onRegistry} style={selectStyle}>\n                <option value=\"\">None</option>\n                <option value=\"+Add\">+Add</option>\n              </select>\n            </label>}\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Cancel\n            </button>\n            {dataCenter.whistleId ? <button\n              type=\"button\"\n              className=\"btn btn-warning\"\n              data-dismiss=\"modal\"\n              onClick={this.showService}\n            >\n              <Icon name=\"gift\" />\n              Plugins\n            </button> : null}\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              data-dismiss=\"modal\"\n              onClick={self.execCmd}\n              disabled={disabledBtn}\n            >\n              {install ? 'Install' : 'Update'}\n            </button>\n          </div>\n        </Dialog>\n        <PluginsMgr ref=\"pluginsMgrDialog\" />\n      </div>\n    );\n  }\n});\n\nfunction getPluginInfo(plugin) {\n  if (!plugin) {\n    return '';\n  }\n  var copyText = [];\n  copyText.push('Name: ' + plugin.moduleName);\n  copyText.push('Version: ' + plugin.version);\n  if (plugin.homepage) {\n    copyText.push('Homepage: ' + plugin.homepage);\n  }\n  return copyText.join('\\n');\n}\n\nvar Tabs = React.createClass({\n  componentDidMount: function () {\n    var self = this;\n    var tabPanel = findDOMNode(self.refs.tabPanel);\n    var wrapper = tabPanel.parentNode;\n    var timer;\n\n    function resizeHandler() {\n      clearTimeout(timer);\n      timer = setTimeout(_resizeHandler, 60);\n    }\n\n    function _resizeHandler() {\n      if (self.props.hide) {\n        return;\n      }\n      var height = wrapper.offsetHeight;\n      if (height) {\n        tabPanel.style.width = wrapper.offsetWidth + 'px';\n        tabPanel.style.height = height + 'px';\n      }\n    }\n    self._resizeHandler = resizeHandler;\n    resizeHandler();\n    $(window).on('resize', resizeHandler);\n  },\n  shouldComponentUpdate: function (nextProps) {\n    return !this.props.hide || !nextProps.hide;\n  },\n  componentDidUpdate: function (prevProps) {\n    if (prevProps.hide && !this.props.hide) {\n      this._resizeHandler();\n    }\n  },\n  onClose: function (e) {\n    this.props.onClose && this.props.onClose(e);\n    e.stopPropagation();\n  },\n  onContextMenu: function(e) {\n    e.preventDefault();\n    var target = $(e.target);\n    var row = target.closest('.w-plugins-item');\n    var active;\n    if (!row.length) {\n      active = target.parent().hasClass('active');\n      row = target.closest('.w-plugins-tab');\n    }\n    var name = row.attr('data-name') || '';\n    var props = this.props;\n    var plugin = props.plugins[name + ':'];\n    var disabledPlugins = props.disabledPlugins || {};\n    var disabled = !plugin;\n    this._curPlugin = plugin;\n    var copyText;\n    if (plugin) {\n      copyText = getPluginInfo(plugin);\n    } else if (name === 'Plugins') {\n      copyText = getPluginList(props.plugins).map(getPluginInfo).join('\\n\\n');\n    }\n    CTX_MENU_LIST[0].disabled = !copyText;\n    CTX_MENU_LIST[0].copyText = copyText;\n    CTX_MENU_LIST[1].name = name && disabledPlugins[name] ? 'Enable' : 'Disable';\n    CTX_MENU_LIST[1].disabled = disabled || props.ndp || props.disabledAllPlugins;\n    CTX_MENU_LIST[2].disabled = disabled || plugin.noOpt || active;\n    CTX_MENU_LIST[3].disabled = disabled || !hasRules(plugin);\n    CTX_MENU_LIST[4].disabled = disabled || plugin.isProj;\n    CTX_MENU_LIST[5].disabled = disabled || plugin.isProj || plugin.notUn;\n    CTX_MENU_LIST[8].disabled = plugin && !plugin.homepage;\n    var pluginItem = CTX_MENU_LIST[7];\n    util.addPluginMenus(\n      pluginItem,\n      dataCenter.getPluginsMenus(),\n      6,\n      disabled,\n      null,\n      plugin && util.getSimplePluginName(plugin)\n    );\n    var data = util.getMenuPosition(e, 110, 250 - (pluginItem.hide ? 0 : 30));\n    data.list = CTX_MENU_LIST;\n    this.refs.contextMenu.show(data);\n  },\n  onClickContextMenu: function(action, _, parentAction, name) {\n    var plugin = this._curPlugin;\n    var props = this.props;\n    switch (parentAction || action) {\n    case 'Option':\n      return events.trigger('openPluginOption', plugin);\n    case 'Rules':\n      return events.trigger('showPluginRules', plugin);\n    case 'Disable':\n      return events.trigger('disablePlugin', [plugin, true]);\n    case 'Enable':\n      return events.trigger('disablePlugin', [plugin, false]);\n    case 'Update':\n      return events.trigger('showInstallPlugin', plugin);\n    case 'Uninstall':\n      return events.trigger('showUninstallPlugin', plugin);\n    case 'Help':\n      var homepage = plugin && plugin.homepage;\n      return window.open(homepage || util.getDocUrl('extensions/usage.html'));\n    case 'Install':\n      return events.trigger('showInstallPlugins');\n    case 'Plugins':\n      iframes.fork(action, {\n        port: dataCenter.getPort(),\n        type: 'plugins',\n        name: name,\n        plugin: plugin,\n        plugins: getPluginList(props.plugins),\n        getRegistryList: this.getRegistryList\n      });\n      return;\n    }\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var tabs = props.tabs || [];\n    var activeName = 'Home';\n    var disabled = props.disabledAllPlugins;\n    var active = self.props.active;\n    if (active && active != activeName) {\n      for (var i = 0, len = tabs.length; i < len; i++) {\n        var tab = tabs[i];\n        if (tab.name == active) {\n          activeName = tab.name;\n          break;\n        }\n      }\n    }\n\n    return (\n      <div\n        className=\"w-nav-tabs fill v-box\"\n        style={{\n          display: self.props.hide ? 'none' : '',\n          paddingTop: disabled ? 0 : undefined\n        }}\n        onContextMenu={self.onContextMenu}\n      >\n        {disabled ? (\n          <div className=\"w-record-status\" style={{ marginBottom: 5 }}>\n            All plugins are currently disabled\n            <button className=\"btn btn-primary\" onClick={enableAllPlugins}>\n              Enable\n            </button>\n          </div>\n        ) : null}\n        <ul className=\"nav nav-tabs\">\n          <li\n            className={\n              'w-nav-normal-tab' + (activeName == 'Home' ? ' active' : '')\n            }\n            data-name=\"Home\"\n            onClick={self.props.onActive}\n          >\n            <a draggable=\"false\" data-name=\"Plugins\" className=\"w-plugins-tab\">\n              <Icon name=\"th-large\" />\n              Plugins\n            </a>\n          </li>\n          {tabs.map(function (tab) {\n            var disd;\n            var favicon;\n            if (util.pluginIsDisabled(props, tab.name)) {\n              disd = true;\n            } else {\n              favicon = util.getPluginIcon(dataCenter.getPlugin(tab.name + ':'));\n            }\n            return (\n              <li key={tab.name} className={activeName == tab.name ? ' active' : ''}>\n                <a\n                  data-name={tab.name}\n                  title={tab.name}\n                  onClick={self.props.onActive}\n                  draggable=\"false\"\n                  className={'w-plugins-tab' + (disd ? ' w-plugin-tab-disabled' : '')}\n                >\n                  {disd ? (\n                    <Icon data-name={tab.name} name=\"ban-circle\" />\n                  ) : (favicon ? <img src={favicon} /> : undefined)}\n                  {tab.name}\n                  <span\n                    data-name={tab.name}\n                    className=\"w-close-icon\"\n                    onClick={self.onClose}\n                  >\n                    &times;\n                  </span>\n                </a>\n              </li>\n            );\n          })}\n        </ul>\n        <div className=\"fill v-box w-nav-tab-panel w-fix-drag\">\n          <div ref=\"tabPanel\" className=\"fill v-box\">\n            <Home\n              data={self.props}\n              hide={activeName != 'Home'}\n              onChange={self.props.onChange}\n              onOpen={self.props.onOpen}\n            />\n            {tabs.map(function (tab) {\n              return (\n                <LazyInit key={tab.name} inited={activeName == tab.name}>\n                  <iframe\n                    style={{ display: activeName == tab.name ? '' : 'none' }}\n                    src={tab.url}\n                    onLoad={dataCenter.handleIframeLoad}\n                  />\n                </LazyInit>\n              );\n            })}\n          </div>\n        </div>\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Tabs;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/properties.js",
    "content": "require('../css/properties.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar util = require('./util');\nvar CopyBtn = require('./copy-btn');\nvar ExpandCollapse = require('./expand-collapse');\nvar ContextMenu = require('./context-menu');\nvar JSONTree = require('./components/react-json-tree')['default'];\nvar EnableHttpsBtn = require('./enable-https-btn');\nvar dataCenter = require('./data-center');\nvar HelpIcon = require('./help-icon');\n\nvar TUNNEL_RE = /^tunnel:\\/\\//;\n\nfunction getSrcInfo(val, sep) {\n  var index = sep ? val.indexOf(sep) : -1;\n  var btnText = '';\n  if (index !== -1) {\n    btnText = val.substring(index);\n    val = val.substring(0, index);\n  }\n  return { value: val, btnText: btnText };\n}\n\nvar Properties = React.createClass({\n  getInitialState: function () {\n    return { viewSource: false };\n  },\n  onLocate: function(text) {\n    var onClickLocate = this.props.onClickLocate;\n    onClickLocate && onClickLocate(text);\n  },\n  componentDidMount: function() {\n    var self = this;\n    if (this.props.name === 'Rules') {\n      $(ReactDOM.findDOMNode(self.refs.properties)).on('mouseenter', 'td pre', function (e) {\n        if (!e.ctrlKey && !e.metaKey) {\n          return;\n        }\n        var target = e.target;\n        var text = target.innerText;\n        var index = text && text.indexOf(util.SOURCE_SEP);\n        if (index > 0 && text.substring(index).indexOf(':') !== -1) {\n          target.setAttribute('data-rule-source', '1');\n        }\n      }).on('mouseleave', 'td pre', function (e) {\n        e.target.removeAttribute('data-rule-source');\n      }).on('click', 'td pre', function (e) {\n        if (!e.ctrlKey && !e.metaKey) {\n          return;\n        }\n        self.onLocate(e.target.innerText);\n      });\n    }\n  },\n  toggle: function () {\n    this.setState({ viewSource: !this.state.viewSource });\n  },\n  renderValue: function(val) {\n    return val && val.length >= 2100 ? <ExpandCollapse text={val} /> : val;\n  },\n  renderKey: function(name, value) {\n    if (this.props.hideKeys) {\n      return null;\n    }\n    var onHelp = this.props.onHelp;\n    var showEnableBtn = this.props.showEnableBtn && name === 'URL' && TUNNEL_RE.test(value);\n    var index = this.props.richKey ? name.indexOf('\\r\\u0000(') : -1;\n    return (<th>\n      {onHelp ? <HelpIcon data-name={name} onClick={onHelp} /> : undefined}\n      {showEnableBtn ? <EnableHttpsBtn /> : null}\n      {this.renderValue(index === -1 ? name : name.substring(0, index))}\n      {index === -1 ? null : <span className=\"w-gray\">{name.substring(index + 2)}</span>}\n    </th>);\n  },\n  onContextMenu: function(e) {\n    util.handlePropsContextMenu(e, this.refs.contextMenu);\n  },\n  getElemRef: function() {\n    return this.refs.properties;\n  },\n  handleClick: function(e) {\n    this.onLocate(e.target.dataset.text);\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var isRules = props.name === 'Rules';\n    var showEnableBtn = props.showEnableBtn;\n    var sourceText = props.enableViewSource;\n    var copyValue = props.enableCopyValue;\n    var hasPluginRule = props.hasPluginRule;\n    var viewSource = self.state.viewSource;\n    var rawName = props.rawName;\n    var showJsonView = props.showJsonView;\n    var cssMap = props.cssMap;\n    var rawValue = props.rawValue;\n    var modal = props.modal || {};\n    var title = props.title || {};\n    var clazz = props.wrapClass || '';\n    var itemSep = isRules ? util.SOURCE_SEP : null;\n    var isArr = Array.isArray(modal);\n    var keys = Object.keys(modal);\n    if (sourceText || copyValue) {\n      var result = [];\n      keys.forEach(function (name) {\n        if (hasPluginRule && name === 'rule') {\n          return;\n        }\n        var value = modal[name];\n        name = sourceText ? name + ': ' : '';\n        result.push(\n          Array.isArray(value)\n            ? value\n                .map(function (val) {\n                  return name + util.toString(val);\n                })\n                .join('\\n')\n            : name + util.toString(value)\n        );\n      });\n      sourceText = sourceText && result.join('\\n');\n      if (copyValue) {\n        copyValue = result.filter(util.noop).join('\\n').trim();\n        if (isRules) {\n          copyValue = util.removeRulesComments(copyValue);\n        }\n      }\n    }\n    if (self.textStr !== sourceText) {\n      self.textStr = sourceText;\n      try {\n        self.jsonStr = JSON.stringify(modal, null, '  ');\n      } catch (e) {\n        self.jsonStr = undefined;\n      }\n    }\n\n    return (\n      <div\n        ref=\"properties\"\n        className={\n          'w-props-wrap ' +\n          (viewSource\n            ? 'w-props-view-source '\n            : 'w-props-view-parsed ') +\n          (props.hide ? 'hide' : '') +\n          (clazz ? ' ' + clazz : '')\n        }\n      >\n        {sourceText ? (\n          <div className=\"w-textarea-bar\">\n            <CopyBtn value={sourceText} name=\"AsText\" />\n            {self.jsonStr ? (\n              <CopyBtn value={self.jsonStr} name=\"AsJSON\" />\n            ) : undefined}\n            <a onClick={self.toggle}>{viewSource ? 'Form' : 'Text'}</a>\n          </div>\n        ) : undefined}\n        {copyValue ? (\n          <div className=\"w-textarea-bar\">\n            <CopyBtn value={copyValue} name={props.name} />\n          </div>\n        ) : undefined}\n        {sourceText ? (\n          <pre className=\"w-props-source\">\n            {self.renderValue(sourceText)}\n          </pre>\n        ) : undefined}\n        <table\n          className={\n            'table w-props w-props-parsed ' + (props.className || '')\n          }\n          onContextMenu={self.onContextMenu}\n        >\n          <tbody>\n            {rawValue ? (\n              <tr key=\"raw\" className={rawValue ? undefined : 'w-no-value'}\n                data-name={rawName}  data-value={rawValue}>\n                <th>{rawName}</th>\n                <td className=\"w-props-raw-data w-user-select-none\" title={rawValue}>\n                  <pre>\n                    {self.renderValue(rawValue)}\n                  </pre>\n                </td>\n              </tr>\n            ) : null}\n            {keys.map(function (name, i) {\n              var value = modal[name];\n              if (Array.isArray(value)) {\n                return value.map(function (val, i) {\n                  val = util.toString(val);\n                  var json = showJsonView && util.likeJson(val) && util.parseJSON(val);\n                  return (\n                    <tr key={i} className={val ? undefined : 'w-no-value'}\n                    data-name={name}  data-value={val}>\n                      {self.renderKey(name, val)}\n                      <td\n                        className={json ? 'w-props-json' : 'w-user-select-none'}\n                        onContextMenu={json ? util.stopPropagation : null}\n                      >\n                        {\n                          json ? <JSONTree data={json}\n                            onSearch={function() {\n                              util.showJSONDialog(json);\n                            }}\n                          /> : <pre>\n                            {self.renderValue(val)}\n                          </pre>\n                        }\n                      </td>\n                    </tr>\n                  );\n                });\n              }\n              value = util.toString(value);\n              var json = showJsonView && util.likeJson(value) && util.parseJSON(value);\n              var css = cssMap && cssMap[name];\n              var style = css && css.style;\n              var className = css && css.className;\n              var showInfo = !json && showEnableBtn && name === 'Status Code' && value === 'captureError';\n              var list = isRules ? value.split(util.CRLF_RE) : null;\n\n              return (\n                <tr\n                  key={name}\n                  title={title[name]}\n                  className={value ? undefined : 'w-no-value'}\n                  data-name={name}  data-value={value}\n                >\n                  {self.renderKey(isArr ? i + 1 + '' : name, value)}\n                  <td\n                    className={(json ? 'w-props-json ' : 'w-user-select-none ') + (className || '')}\n                    style={style}\n                    onContextMenu={json ? util.stopPropagation : null}\n                  >\n                    {\n                      json ? <JSONTree data={json} onSearch={function() {\n                        util.showJSONDialog(json);\n                      }} /> : (list ? list.map(function(val) {\n                        var info = getSrcInfo(val, itemSep);\n                        var btnText = info.btnText;\n                        var noLocate = btnText.indexOf(':') === -1 && (!dataCenter.whistleId || (btnText !== '# (From Mock Rules)' && btnText !== '# (From Service Rules)'));\n                        return (\n                          <pre>\n                            {self.renderValue(info.value)}\n                            {noLocate ? <span className=\"w-src-info\">{btnText}</span> : <a className=\"w-src-info\" onClick={self.handleClick} data-text={val}>{btnText}</a> }\n                          </pre>\n                        );\n                      }) : <pre className={showInfo ? 'w-align-items' : null}>{\n                        showInfo ? <HelpIcon className=\"w-props-icon\" docsUrl=\"faq.html#capture-error\" /> : null}{self.renderValue(value)\n                      }</pre>)\n                    }\n                  </td>\n                </tr>\n              );\n            })}\n          </tbody>\n        </table>\n        <ContextMenu ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Properties;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/props-editor.js",
    "content": "require('../css/props-editor.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar util = require('./util');\nvar message = require('./message');\nvar win = require('./win');\nvar ContextMenu = require('./context-menu');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar MAX_FILE_SIZE = 1024 * 1024 * 20;\nvar MAX_NAME_LEN = 128;\nvar MAX_VALUE_LEN = 64 * 1024;\nvar MAX_COUNT = 160;\nvar index = MAX_COUNT;\nvar W2_HEADER_RE = /^x-whistle-/;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar highlight = function (name) {\n  return name === 'x-forwarded-for' || W2_HEADER_RE.test(name);\n};\nvar PropsEditor = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  getValue: function (name, field) {\n    var isHeader = this.props.isHeader;\n    var allowUploadFile = this.props.allowUploadFile;\n    var decode = isHeader ? util.decodeURIComponentSafe : util.noop;\n    var shortName = decode(name.substring(0, MAX_NAME_LEN), isHeader);\n    var result = { name: shortName };\n    if (allowUploadFile && field && field.value != null) {\n      result.value = decode(\n        util.toString(field.value).substring(0, MAX_VALUE_LEN),\n        isHeader\n      );\n      result.data = field.data;\n      result.size = (field.data && field.data.length) || 0;\n      result.type = field.type;\n    } else {\n      result.value = decode(\n        util.toString(field).substring(0, MAX_VALUE_LEN),\n        isHeader\n      );\n    }\n    return result;\n  },\n  update: function (data) {\n    var modal = {};\n    var overflow;\n    if (data) {\n      var self = this;\n      var keys = Object.keys(data);\n      overflow = keys.length >= MAX_COUNT;\n      if (overflow) {\n        keys = keys.slice(0, MAX_COUNT);\n      }\n      keys.forEach(function (name) {\n        var value = data[name];\n        if (!Array.isArray(value)) {\n          modal[name + '_0'] = self.getValue(name, value);\n          return;\n        }\n        value.forEach(function (val, i) {\n          modal[name + '_' + i] = self.getValue(name, val);\n        });\n      });\n    }\n    this.setState({ modal: modal }, this.props.onUpdate);\n    return overflow;\n  },\n  onAdd: function () {\n    if (this.props.disabled) {\n      return;\n    }\n    if (Object.keys(this.state.modal || '').length >= MAX_COUNT) {\n      return message.error('Maximum allowed value: ' + MAX_COUNT);\n    }\n    this.setState({ data: '' });\n    this.showDialog();\n  },\n  clear: function() {\n    if (!Object.keys(this.state.modal || '').length) {\n      return;\n    }\n    this.setState({ modal: {} }, this.props.onChange);\n  },\n  onEdit: function (e) {\n    if (this.props.disabled) {\n      return;\n    }\n    var name = e.target.getAttribute('data-name');\n    var data = this.state.modal[name];\n    this.setState({ data: data });\n    this.showDialog(data);\n  },\n  execCallback: function(e) {\n    if (e.target.getAttribute('data-action') === 'callback') {\n      this.props.callback();\n    }\n  },\n  edit: function (e) {\n    var nameInput = findDOMNode(this.refs.name);\n    var name = nameInput.value.trim();\n    if (!name) {\n      nameInput.focus();\n      return message.error('The name is required');\n    }\n    var valueInput = findDOMNode(this.refs.valueInput);\n    var value = valueInput.value.trim();\n    var state = this.state;\n    var data = state.data;\n    var origName = data.name;\n    data.name = name;\n    data.data = state.fileData;\n    if (state.fileData) {\n      data.size = state.fileSize;\n      data.value = state.filename;\n      data.type = state.fileType;\n    } else {\n      data.value = value;\n    }\n    this.props.onChange(origName, name);\n    this.setState({\n      fileData: null,\n      fileSize: null,\n      filename: null,\n      fileType: null\n    });\n    this.hideDialog();\n    nameInput.value = valueInput.value = '';\n    this.execCallback(e);\n  },\n  add: function (e) {\n    var nameInput = findDOMNode(this.refs.name);\n    var name = nameInput.value.trim();\n    if (!name) {\n      nameInput.focus();\n      return message.error('The name is required');\n    }\n    var valueInput = findDOMNode(this.refs.valueInput);\n    var value = valueInput.value.trim();\n    var modal = this.state.modal;\n    var state = this.state;\n    if (!modal) {\n      modal = {};\n      state.modal = modal;\n    }\n    modal[name + '_' + ++index] = state.fileData\n      ? {\n        name: name,\n        value: state.filename,\n        size: state.fileSize,\n        data: state.fileData,\n        type: state.fileType\n      }\n      : {\n        name: name,\n        value: value\n      };\n    this.props.onChange(name);\n    this.setState({\n      fileData: null,\n      fileSize: null,\n      filename: null,\n      fileType: null\n    });\n    this.hideDialog();\n    nameInput.value = valueInput.value = '';\n    this.execCallback(e);\n  },\n  hideDialog: function () {\n    this.refs.composerDialog.hide();\n  },\n  showDialog: function (data) {\n    this.refs.composerDialog.show();\n    var nameInput = findDOMNode(this.refs.name);\n    if (data) {\n      nameInput.value = data.name || '';\n      if (data.data) {\n        this.setState({\n          filename: data.value,\n          fileSize: data.size,\n          fileData: data.data,\n          fileType: data.type\n        });\n      } else {\n        findDOMNode(this.refs.valueInput).value = data.value || '';\n      }\n    }\n    setTimeout(function () {\n      nameInput.select();\n      nameInput.focus();\n    }, 600);\n  },\n  onRemove: function (e) {\n    var self = this;\n    if (self.props.disabled) {\n      return;\n    }\n    var name = e.target.getAttribute('data-name');\n    var opName = self.props.isHeader ? 'header' : 'param';\n    var item = self.state.modal[name];\n    win.confirm(\n      'Do you confirm the deletion of this ' + opName + ' \\'' + item.name + '\\'?',\n      function (sure) {\n        if (sure) {\n          delete self.state.modal[name];\n          self.props.onChange(item.name);\n          self.setState({});\n        }\n      }\n    );\n  },\n  getFields: function () {\n    var modal = this.state.modal || '';\n    return Object.keys(modal).map(function (key) {\n      return modal[key];\n    });\n  },\n  toString: function () {\n    var modal = this.state.modal || '';\n    var keys = Object.keys(modal);\n    if (this.props.isHeader) {\n      return keys\n        .map(function (key) {\n          var obj = modal[key];\n          return obj.name + ': ' + util.encodeNonLatin1Char(obj.value);\n        })\n        .join('\\r\\n');\n    }\n    return keys\n      .map(function (key) {\n        var obj = modal[key];\n        return (\n          util.encodeURIComponent(obj.name) +\n          '=' +\n          util.encodeURIComponent(obj.value)\n        );\n      })\n      .join('&');\n  },\n  onUpload: function () {\n    if (!this.reading) {\n      findDOMNode(this.refs.readLocalFile).click();\n    }\n  },\n  readLocalFile: function () {\n    var form = new FormData(findDOMNode(this.refs.readLocalFileForm));\n    var file = form.get('localFile');\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Total file size must not exceed 20MB');\n    }\n    var modal = this.state.modal || '';\n    var size = file.size;\n    Object.keys(modal).forEach(function (key) {\n      size += modal[key].size;\n    });\n    if (size > MAX_FILE_SIZE) {\n      return win.alert('Total file size must not exceed 20MB');\n    }\n    var self = this;\n    self.reading = true;\n    util.readFile(file, function (data) {\n      self.reading = false;\n      self.localFileData = data;\n      self.setState({\n        filename: file.name || 'unknown',\n        fileData: data,\n        fileSize: file.size,\n        fileType: file.type\n      });\n    });\n    findDOMNode(this.refs.readLocalFile).value = '';\n  },\n  removeLocalFile: function (e) {\n    var self = this;\n    self.setState(\n      {\n        filename: null,\n        fileData: null\n      },\n      function () {\n        var valueInput = findDOMNode(self.refs.valueInput);\n        valueInput.select();\n        valueInput.focus();\n      }\n    );\n    e.stopPropagation();\n  },\n  onContextMenu: function(e) {\n    util.handlePropsContextMenu(e, this.refs.contextMenu);\n  },\n  render: function () {\n    var self = this;\n    var modal = this.state.modal || '';\n    var filename = this.state.filename;\n    var fileSize = this.state.fileSize;\n    var keys = Object.keys(modal);\n    var isHeader = this.props.isHeader;\n    var allowUploadFile = this.props.allowUploadFile;\n    var data = this.state.data || '';\n    var text = data ? 'Modify' : 'Add';\n    var btnText = text + (isHeader ? ' Header' : ' Param');\n    var cbBtnText = this.props.callback ? text + ' & Send' : null;\n\n    return (\n      <div\n        className={\n          'fill v-box w-props-editor' +\n          (this.props.hide ? ' hide' : '')\n        }\n        title={this.props.title}\n        onDoubleClick={this.props.onDoubleClick}\n      >\n        {keys.length ? (\n          <table className=\"table\">\n            <tbody onContextMenu={this.onContextMenu}>\n              {keys.map(function (name) {\n                var item = modal[name];\n                return (\n                  <tr key={name} data-name={item.name} data-value={item.value}>\n                    <th\n                      className={\n                        isHeader && highlight(item.name) ? 'w-bold' : undefined\n                      }\n                    >\n                      {item.name}\n                    </th>\n                    <td>\n                      <pre>\n                        {item.data ? <Icon name=\"file\" /> : undefined}\n                        {item.data\n                          ? ' [' + util.getSize(item.size) + '] '\n                          : undefined}\n                        {item.value}\n                      </pre>\n                    </td>\n                    <td className=\"w-props-ops\">\n                      <Icon\n                        name=\"edit\"\n                        className=\"w-edit-btn\"\n                        data-name={name}\n                        onClick={self.onEdit}\n                        title=\"Edit\"\n                      />\n                      <Icon\n                        name=\"remove\"\n                        className=\"w-del-btn\"\n                        data-name={name}\n                        onClick={self.onRemove}\n                        title=\"Delete\"\n                      />\n                    </td>\n                  </tr>\n                );\n              })}\n            </tbody>\n          </table>\n        ) : (\n          <button\n            onClick={this.onAdd}\n            disabled={this.props.disabled}\n            className={\n              'btn btn-primary btn-sm w-add-field' +\n              (this.props.isHeader ? ' w-add-header' : '')\n            }\n          >\n            {this.props.isHeader ? '+Header' : '+Param'}\n          </button>\n        )}\n        <Dialog ref=\"composerDialog\" wstyle=\"w-com-dialog\">\n          <div className=\"modal-body\">\n            <CloseBtn />\n            <label>\n              Name:\n              <input\n                ref=\"name\"\n                placeholder=\"Enter name\"\n                className=\"form-control\"\n                maxLength=\"128\"\n              />\n            </label>\n            <div>\n              Value:\n              <div\n                className={\n                  allowUploadFile\n                    ? 'w-props-editor-upload'\n                    : 'w-props-editor-form'\n                }\n              >\n                <div\n                  onClick={this.onUpload}\n                  className={'w-props-editor-file' + (filename ? '' : ' hide')}\n                  title={filename}\n                >\n                  <CloseBtn className=\"w-del-btn\" onClick={this.removeLocalFile} />\n                  <Icon name=\"file\" />\n                  {' [' + util.getSize(fileSize) + '] '}\n                  {filename}\n                </div>\n                <textarea\n                  ref=\"valueInput\"\n                  maxLength={MAX_VALUE_LEN}\n                  placeholder=\"Enter value\"\n                  className={'form-control' + (filename ? ' hide' : '')}\n                  onKeyDown={util.handleTab}\n                />\n                <button\n                  onClick={this.onUpload}\n                  className={'btn btn-primary' + (filename ? ' hide' : '')}\n                >\n                  <Icon name=\"folder-open\" />\n                  Upload\n                </button>\n              </div>\n            </div>\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Cancel\n            </button>\n            {\n              cbBtnText ?\n              <button\n                type=\"button\"\n                className=\"btn btn-default\"\n                data-action=\"callback\"\n                onClick={data ? self.edit : self.add}\n              >\n                {cbBtnText}\n              </button> : null\n            }\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={data ? self.edit : self.add}\n            >\n              {btnText}\n            </button>\n          </div>\n        </Dialog>\n        <form\n          ref=\"readLocalFileForm\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"readLocalFile\"\n            onChange={this.readLocalFile}\n            type=\"file\"\n            name=\"localFile\"\n          />\n        </form>\n        <ContextMenu ref=\"contextMenu\" />\n      </div>\n    );\n  }\n});\n\nmodule.exports = PropsEditor;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/protocols.js",
    "content": "var util = require('./util');\nvar events = require('./events');\nvar PROTOCOLS = [\n  'rule',\n  'style',\n  'pipe',\n  'plugin',\n  'sniCallback',\n  'host',\n  'xhost',\n  'proxy',\n  'xproxy',\n  'http-proxy',\n  'xhttp-proxy',\n  'https-proxy',\n  'xhttps-proxy',\n  'socks',\n  'xsocks',\n  'pac',\n  'log',\n  'weinre',\n  'filter',\n  'ignore',\n  'skip',\n  'enable',\n  'disable',\n  'delete',\n  'urlParams',\n  'pathReplace',\n  'method',\n  'replaceStatus',\n  'reqScript',\n  'resScript',\n  'frameScript',\n  'reqDelay',\n  'resDelay',\n  'reqSpeed',\n  'resSpeed',\n  'headerReplace',\n  'reqHeaders',\n  'resHeaders',\n  'reqCharset',\n  'resCharset',\n  'reqCookies',\n  'resCookies',\n  'reqCors',\n  'resCors',\n  'reqType',\n  'resType',\n  'ua',\n  'auth',\n  'cache',\n  'referer',\n  'attachment',\n  'forwardedFor',\n  'responseFor',\n  'reqBody',\n  'resBody',\n  'reqMerge',\n  'resMerge',\n  'reqPrepend',\n  'resPrepend',\n  'reqAppend',\n  'resAppend',\n  'reqReplace',\n  'resReplace',\n  'htmlPrepend',\n  'htmlBody',\n  'htmlAppend',\n  'cssPrepend',\n  'cssBody',\n  'cssAppend',\n  'jsPrepend',\n  'jsBody',\n  'jsAppend',\n  'reqWrite',\n  'resWrite',\n  'reqWriteRaw',\n  'resWriteRaw',\n  'trailers',\n  'tlsOptions'\n];\n\nvar innerRules = [\n  'file',\n  'xfile',\n  'tpl',\n  'xtpl',\n  'rawfile',\n  'xrawfile',\n  'redirect',\n  'locationHref',\n  'statusCode'\n];\nvar pluginRules = [];\nvar pluginVarList = [];\nvar allPluginNameList = [];\nvar forwardRules = innerRules.slice();\nvar webProtocols = ['http', 'https', 'ws', 'wss', 'tunnel'];\nvar allInnerRules = webProtocols.concat(innerRules).concat(PROTOCOLS.slice(1));\nallInnerRules.splice(allInnerRules.indexOf('plugin'), 1);\nallInnerRules.splice(allInnerRules.indexOf('reqScript') + 1, 0, 'reqRules');\nallInnerRules.splice(allInnerRules.indexOf('resScript') + 1, 0, 'resRules');\nallInnerRules = allInnerRules.map(function (name) {\n  return name + '://';\n});\nallInnerRules.splice(\n  allInnerRules.indexOf('filter://'),\n  1,\n  'excludeFilter://',\n  'includeFilter://'\n);\nallInnerRules.push('lineProps://');\nvar allRules = allInnerRules;\nvar groupRules = [\n  ['Map Local', ['file://', 'xfile://', 'tpl://', 'xtpl://', 'rawfile://', 'xrawfile://']],\n  ['Map Remote', ['https://', 'http://', 'wss://', 'ws://', 'tunnel://']],\n  ['DNS Spoofing', ['host://', 'xhost://', 'proxy://', 'xproxy://', 'http-proxy://', 'xhttp-proxy://',\n    'https-proxy://', 'xhttps-proxy://', 'socks://', 'xsocks://', 'pac://']],\n  ['Rewrite Request', ['urlParams://', 'pathReplace://','sniCallback://', 'method://', 'tlsOptions://', 'reqHeaders://', 'forwardedFor://',\n    'ua://', 'auth://', 'cache://', 'referer://', 'reqType://', 'reqCharset://', 'reqCookies://',\n    'reqCors://', 'reqBody://', 'reqMerge://', 'reqPrepend://', 'reqAppend://', 'reqReplace://', 'reqWrite://',\n    'reqWriteRaw://', 'reqRules://', 'reqScript://']],\n  ['Rewrite Response', ['statusCode://', 'replaceStatus://', 'redirect://', 'locationHref://', 'resHeaders://', 'responseFor://',\n    'resCharset://', 'resType://', 'resCookies://', 'attachment://', 'resCors://', 'resBody://', 'resMerge://', 'resPrepend://', 'resAppend://', 'resReplace://',\n    'htmlPrepend://', 'htmlBody://', 'htmlAppend://', 'cssPrepend://', 'cssBody://', 'cssAppend://', 'jsPrepend://', 'jsBody://',\n    'jsAppend://', 'trailers://', 'resWrite://', 'resWriteRaw://', 'resRules://', 'resScript://', 'frameScript://']],\n  ['General', ['pipe://', 'delete://', 'headerReplace://']],\n  ['Throttle', ['reqDelay://', 'resDelay://', 'reqSpeed://', 'resSpeed://']],\n  ['Tools', ['weinre://', 'log://']],\n  ['Settings', ['style://', 'enable://', 'disable://', 'lineProps://']],\n  ['Filters', ['ignore://', 'skip://', 'excludeFilter://', 'includeFilter://']],\n  ['Plugins', []]\n];\n\nvar pluginsOptions = [];\n\nexports.setPlugins = function (pluginsState) {\n  var disabledPlugins = pluginsState.disabledPlugins;\n  pluginsOptions = pluginsState.pluginsOptions;\n  pluginRules = [];\n  pluginVarList = [];\n  allPluginNameList = [];\n  forwardRules = innerRules.slice();\n  allRules = allInnerRules.slice();\n  var pluginsProtos = [];\n  groupRules[groupRules.length - 1][1] = pluginsProtos;\n\n  if (!pluginsState.disabledAllPlugins) {\n    pluginsOptions.forEach(function (plugin, i) {\n      if (!i) {\n        return;\n      }\n      var name = plugin.name;\n      if (!disabledPlugins[name]) {\n        var vars = plugin.pluginVars;\n        if (vars) {\n          var hintSuffix = vars.hintSuffix;\n          if (hintSuffix) {\n            hintSuffix.forEach(function(suffix) {\n              pluginVarList.push('%' + name + suffix);\n            });\n          } else {\n            pluginVarList.push('%' + name + '=');\n          }\n        }\n        allPluginNameList.push(name);\n        var proto;\n        if (!plugin.hideShortProtocol && name.indexOf('_') === -1) {\n          forwardRules.push(name);\n          proto = name + '://';\n          allRules.push(proto);\n          pluginsProtos.push(proto);\n        }\n        if (!plugin.hideLongProtocol) {\n          pluginRules.push('whistle.' + name, 'plugin.' + name);\n          proto = 'whistle.' + name + '://';\n          allRules.push(proto);\n          pluginsProtos.push(proto);\n        }\n      }\n    });\n  }\n  events.trigger('updatePlugins');\n};\n\nexports.PROTOCOLS = PROTOCOLS;\n\nexports.getGroupRules = function() {\n  return groupRules;\n};\n\nexports.getForwardRules = function () {\n  return forwardRules;\n};\n\nexports.getPluginRules = function () {\n  return pluginRules;\n};\n\nexports.getPluginVarList = function() {\n  return pluginVarList;\n};\n\nexports.getAllPluginNameList = function () {\n  return allPluginNameList;\n};\n\nexports.getAllRules = function () {\n  return allRules;\n};\n\nfunction getPlugin(rule) {\n  rule = rule.substring(rule.indexOf('.') + 1);\n  for (var i = 0, len = pluginsOptions.length; i < len; i++) {\n    var plugin = pluginsOptions[i];\n    if (plugin.name === rule) {\n      return plugin;\n    }\n  }\n}\n\nexports.getHelpUrl = function (rule) {\n  if (!rule || rule === 'rule') {\n    return util.getDocUrl('rules/protocols.html');\n  }\n  if (rule === 'includeFilter' || rule === 'excludeFilter') {\n    return util.getDocUrl('rules/filters.html');\n  }\n  if (rule === 'skip') {\n    return util.getDocUrl('rules/skip.html');\n  }\n  if (rule === 'lineProps') {\n    return util.getDocUrl('rules/lineProps.html');\n  }\n  if (rule === 'reqRules') {\n    return util.getDocUrl('rules/reqRules.html');\n  }\n  if (rule === 'resRules') {\n    return util.getDocUrl('rules/resRules.html');\n  }\n  if (innerRules.indexOf(rule) !== -1 || webProtocols.indexOf(rule) !== -1 || PROTOCOLS.indexOf(rule) !== -1) {\n    if (rule === 'http-proxy') {\n      rule = 'proxy';\n    } else if (rule === 'xhttp-proxy') {\n      rule = 'xproxy';\n    } else if (rule === 'tlsOptions') {\n      rule = 'cipher';\n    }\n    return util.getDocUrl('rules/' + rule + '.html');\n  }\n  rule = getPlugin(rule);\n  if (rule && rule.homepage) {\n    return rule.homepage;\n  }\n  return util.getDocUrl('rules/protocols.html');\n};\n\nexports.getPlugin = getPlugin;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/qrcode-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar QRCodeImg = require('./qrcode');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar QRCodeDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  shouldComponentUpdate: function () {\n    return this.refs.qrcodeDialog.isVisible();\n  },\n  show: function (url) {\n    this.refs.qrcodeDialog.show();\n    this.setState({ url: url });\n  },\n  render: function () {\n    var url = this.state.url;\n\n    return (\n      <Dialog ref=\"qrcodeDialog\" wstyle=\"w-qrcode-dialog\">\n        <div className=\"modal-body\">\n          <h4>QR Code</h4>\n          <CloseBtn />\n          <div className=\"w-qrcode-url-wrap\">\n            <input readOnly value={url} />\n            <Icon name=\"copy\" className=\"w-copy-text-with-tips\" data-clipboard-text={url} />\n          </div>\n          <QRCodeImg url={this.state.url} />\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = QRCodeDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/qrcode.js",
    "content": "var React = require('react');\nvar QRCode = require('qrcode');\n\nfunction getSize(size) {\n  return size > 0 ? size : 320;\n}\n\nfunction getDataURL(options, cb) {\n  QRCode.toDataURL(\n    options.url,\n    {\n      width: getSize(options.width),\n      height: getSize(options.height),\n      margin: 0\n    },\n    function (err, dataURL) {\n      cb(err, dataURL);\n    }\n  );\n}\n\nvar QRCodeImg = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  renderQRCode: function () {\n    var self = this;\n    var state = self.state;\n    var props = self.props;\n    if (!props.url) {\n      state.dataURL = null;\n      state.error = null;\n      return;\n    }\n    if (props.url == state.url) {\n      return;\n    }\n    getDataURL(props, function (err, dataURL) {\n      state.url = props.url;\n      state.error = err;\n      state.dataURL = dataURL;\n      self.setState({});\n    });\n  },\n  renderError: function () {\n    var error = this.state.error;\n    if (!error) {\n      return this.props.url ? 'Generating...' : null;\n    }\n    return (\n      <div className=\"w-qrcode-error\">\n        {error.message || 'Error occurred'}\n      </div>\n    );\n  },\n  render: function () {\n    this.renderQRCode();\n    var state = this.state;\n    var props = this.props;\n    var dataURL = state.dataURL;\n    var size = { width: getSize(props.width), height: getSize(props.width) };\n\n    return (\n      <div className=\"w-qrcode-wrap\" style={size}>\n        {dataURL ? <img src={dataURL} /> : this.renderError()}\n      </div>\n    );\n  }\n});\n\nmodule.exports = QRCodeImg;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/record-btn.js",
    "content": "var events = require('./events');\n\nvar React = require('react');\nvar MenuItem = require('./menu-item');\nvar Icon = require('./icon');\nrequire('../css/record-btn.css');\n\n\nvar PAUSE_OPTION = {\n  name: 'Pause Record',\n  icon: 'minus-sign',\n  id: 'pause'\n};\nvar STOP_OPTION = {\n  name: 'Stop Record',\n  icon: 'stop',\n  id: 'stop'\n};\nvar ACTION_OPTIONS = [\n  PAUSE_OPTION,\n  {\n    name: 'Scroll To Top',\n    icon: 'arrow-up',\n    id: 'top'\n  },\n  {\n    name: 'Scroll To Bottom',\n    icon: 'arrow-down',\n    id: 'bottom'\n  }\n];\n\nvar RecordBtn = React.createClass({\n  getInitialState: function () {\n    return { stop: false };\n  },\n  onClick: function () {\n    var stop = !this.state.stop;\n    this.state.pause = false;\n    this.state.stop = stop;\n    ACTION_OPTIONS[0] = PAUSE_OPTION;\n    this.props.onClick(stop ? 'stop' : 'refresh');\n    this.setState({});\n  },\n  componentDidMount: function () {\n    events.on('toggleNetworkState', this.onClick);\n  },\n  enable: function (flag) {\n    var state = this.state;\n    var pause = state.pause;\n    var stop = state.stop;\n    if (flag === 'stop') {\n      if (stop && !pause) {\n        return;\n      }\n    } else if (flag === 'pause') {\n      if (pause) {\n        return;\n      }\n    } else {\n      if (stop || pause) {\n        this.onClick();\n      }\n      return;\n    }\n\n    this.onClickOption({ id: flag });\n  },\n  showActionOptions: function () {\n    this.setState({\n      showActionOptions: true\n    });\n  },\n  hideActionOptions: function () {\n    this.setState({\n      showActionOptions: false\n    });\n  },\n  onClickOption: function (option) {\n    if (option.id === 'pause') {\n      ACTION_OPTIONS[0] = STOP_OPTION;\n      this.state.pause = true;\n      this.state.stop = true;\n    } else if (option.id === 'stop') {\n      ACTION_OPTIONS[0] = PAUSE_OPTION;\n      this.state.pause = false;\n      this.state.stop = true;\n    }\n    this.props.onClick(option.id);\n    this.hideActionOptions();\n  },\n  render: function () {\n    var state = this.state;\n    var hide = this.props.hide;\n    var pause = state.pause;\n    var stop = state.stop;\n    var disabled = stop || pause;\n    var title = 'Click to ' + (disabled ? 'start' : 'stop') + ' record';\n\n    return (\n      <div\n        onMouseEnter={this.showActionOptions}\n        onMouseLeave={this.hideActionOptions}\n        className={\n          'w-menu-wrapper w-switch-btn w-menu-auto' +\n          (state.showActionOptions ? ' w-menu-wrapper-show' : '') +\n          (hide ? ' hide' : '')\n        }\n      >\n        <a\n          onClick={this.onClick}\n          draggable=\"false\"\n          className={'w-scroll-menu' + (disabled ? ' w-pause' : '') + (this.props.disabledRecord ? ' w-disabled' : '')}\n          title={title}\n        >\n          <Icon name={pause ? 'minus-sign' : 'stop'} />\n          Record\n        </a>\n        <MenuItem\n          options={ACTION_OPTIONS}\n          className=\"w-remove-menu-item\"\n          onClickOption={this.onClickOption}\n        />\n      </div>\n    );\n  }\n});\n\nmodule.exports = RecordBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/recycle-bin.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar message = require('./message');\nvar events = require('./events');\nvar win = require('./win');\nvar Icon = require('./icon');\nvar CloseBtn = require('./close-btn');\n\nvar TIMESTAMP_RE = /^(\\d+)\\.([\\s\\S]+)$/;\n\nfunction decode(name) {\n  try {\n    return decodeURIComponent(name);\n  } catch (e) {}\n  return name;\n}\n\nvar RecycleBinDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('rulesRecycleList', function (_, data) {\n      if (self.state.name === 'Rules') {\n        self.show(data, true);\n      }\n    });\n    events.on('valuesRecycleList', function (_, data) {\n      if (self.state.name === 'Values') {\n        self.show(data, true);\n      }\n    });\n  },\n  show: function (options, quiet) {\n    var self = this;\n    if (options.list) {\n      options.list = options.list\n        .map(function (name) {\n          if (!TIMESTAMP_RE.test(name)) {\n            return;\n          }\n          return {\n            filename: decode(RegExp.$2),\n            date: util.toLocaleString(new Date(parseInt(RegExp.$1, 10))),\n            name: name\n          };\n        })\n        .filter(util.noop);\n    }\n    self.setState(options, function () {\n      !quiet && self.refs.recycleBinDialog.show();\n    });\n  },\n  hide: function () {\n    this.refs.recycleBinDialog.hide();\n  },\n  checkFile: function (data, xhr) {\n    if (!data) {\n      util.showSystemError(xhr);\n      return;\n    }\n    if (data.ec === 3) {\n      var self = this;\n      message.error('File not found');\n      dataCenter[this.state.name.toLowerCase()].recycleList(function (\n        result,\n        xhr\n      ) {\n        if (!result) {\n          util.showSystemError(xhr);\n          return;\n        }\n        self.show(result);\n      });\n      return;\n    }\n    return true;\n  },\n  view: function (e) {\n    var self = this;\n    var name = e.target.getAttribute('data-name');\n    dataCenter[this.state.name.toLowerCase()].recycleView(\n      { name: name },\n      function (data, xhr) {\n        if (!self.checkFile(data, xhr)) {\n          return;\n        }\n        if (!data.data) {\n          return message.info('Empty content');\n        }\n        util.openEditor(data.data);\n      }\n    );\n  },\n  recover: function (item) {\n    var self = this;\n    dataCenter[self.state.name.toLowerCase()].recycleView(\n      { name: item.name },\n      function (data, xhr) {\n        if (!self.checkFile(data, xhr)) {\n          return;\n        }\n        item.data = data.data;\n        events.trigger('recover' + self.state.name, item);\n      }\n    );\n  },\n  remove: function (e) {\n    var name = e.target.getAttribute('data-name');\n    var origName = decode(name.substring(name.indexOf('.') + 1));\n    var self = this;\n    win.confirm(\n      'Do you confirm the deletion of \\'' + origName + '\\' completely?',\n      function (sure) {\n        if (sure) {\n          dataCenter[self.state.name.toLowerCase()].recycleRemove(\n            { name: name },\n            function (data, xhr) {\n              if (!data) {\n                util.showSystemError(xhr);\n                return;\n              }\n              self.show(data);\n            }\n          );\n        }\n      }\n    );\n  },\n  isVisible: function () {\n    return $(ReactDOM.findDOMNode(this.refs.recycleBinBody)).is(':visible');\n  },\n  render: function () {\n    var self = this;\n    var state = self.state;\n    var list = state.list || [];\n    return (\n      <Dialog ref=\"recycleBinDialog\" wstyle=\"w-files-dialog\">\n        <div className=\"modal-header\">\n          <h4>{state.name} Trash</h4>\n          <CloseBtn />\n        </div>\n        <div className=\"modal-body\" ref=\"recycleBinBody\">\n          <table className=\"table\">\n            <thead>\n              <th className=\"w-files-order\">#</th>\n              <th className=\"w-files-date\">Date</th>\n              <th className=\"w-files-path\">Filename</th>\n              <th className=\"w-files-operation\">Operation</th>\n            </thead>\n            <tbody className=\"w-hover-body\">\n              {list.length ? (\n                list.map(function (item, i) {\n                  return (\n                    <tr key={item.name}>\n                      <th className=\"w-files-order\">{i + 1}</th>\n                      <td className=\"w-files-date\">{item.date}</td>\n                      <td className=\"w-files-path\" title={item.filename}>\n                        {util.isGroup(item.filename) ? <Icon name=\"triangle-right\" className=\"w-list-group-icon\" /> : null}\n                        {item.filename}\n                      </td>\n                      <td className=\"w-files-operation\">\n                        <a data-name={item.name} onClick={self.view}>\n                          View\n                        </a>\n                        <a\n                          onClick={function () {\n                            self.recover(item);\n                          }}\n                        >\n                          Restore\n                        </a>\n                        <a data-name={item.name} onClick={self.remove}>\n                          Delete\n                        </a>\n                      </td>\n                    </tr>\n                  );\n                })\n              ) : (\n                <tr>\n                  <td colSpan=\"4\" style={{ textAlign: 'center' }}>\n                    Empty\n                  </td>\n                </tr>\n              )}\n            </tbody>\n          </table>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nvar RecycleBinDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (options) {\n    this.refs.recycleBinDialog.show(options);\n  },\n  hide: function () {\n    this.refs.recycleBinDialog.hide();\n  },\n  isVisible: function () {\n    return this.refs.recycleBinDialog.isVisible();\n  },\n  render: function () {\n    return <RecycleBinDialog ref=\"recycleBinDialog\" />;\n  }\n});\n\nmodule.exports = RecycleBinDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/req-data.js",
    "content": "require('../css/req-data.css');\nrequire('react-virtualized/styles.css');\n\nvar RV = require('react-virtualized/dist/umd/react-virtualized');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar $ = require('jquery');\nvar QRCodeDialog = require('./qrcode-dialog');\nvar util = require('./util');\nvar settings = require('./columns');\nvar FilterInput = require('./filter-input');\nvar ContextMenu = require('./context-menu');\nvar events = require('./events');\nvar iframes = require('./iframes');\nvar dataCenter = require('./data-center');\nvar storage = require('./storage');\nvar message = require('./message');\nvar Icon = require('./icon');\n\nvar findDOMNode = ReactDOM.findDOMNode;\nvar TREE_ROW_HEIGHT = 24;\nvar ROW_STYLE = { outline: 'none' };\nvar columnState = {};\nvar columnKeys = {};\nvar CMD_RE = /^:dump\\s+(\\d{1,15})\\s*$/;\nvar BODY_FILTER = /(^\\s*|\\s+)(content|c|b|body):/i;\nvar MAX_LEN = 64;\nvar HINTS = [\n  '<keyword or regex for URL>',\n  'd:<keyword or regex for domain>',\n  'm:<keyword or regex for HTTP method>',\n  's:<keyword or regex for HTTP status code>',\n  'h:<keyword or regex for request or response headers>',\n  'b:<keyword or regex for request or response body>'\n];\nvar contextMenuList = [\n  {\n    name: 'Open',\n    list: [\n      { name: 'New Tab' },\n      { name: 'QR Code' },\n      { name: 'Overview' },\n      { name: 'Inspectors' },\n      { name: 'Timeline' },\n      { name: 'Composer' },\n      { name: 'Preview' },\n      { name: 'Source' },\n      { name: 'Tree View', action: 'toggleView' }\n    ]\n  },\n  {\n    name: 'Copy',\n    shiftToEdit: true,\n    list: [\n      { name: 'Cell Text' },\n      { name: 'Host' },\n      { name: 'Path' },\n      { name: 'URL' },\n      { name: 'Full URL' },\n      { name: 'As cURL' },\n      { name: 'Client IP' },\n      { name: 'Server IP' },\n      { name: 'Cookie' }\n    ]\n  },\n  {\n    name: 'Remove',\n    list: [\n      { name: 'All' },\n      { name: 'One' },\n      { name: 'Others' },\n      { name: 'Selected' },\n      { name: 'Unselected' },\n      { name: 'Unmarked' },\n      { name: 'All Matching Hosts', action: 'removeAllSuchHost' },\n      { name: 'All Matching URLs', action: 'removeAllSuchURL' }\n    ]\n  },\n  {\n    name: 'Settings',\n    list: [\n      { name: 'Edit Settings' },\n      { name: 'Exclude All Matching Hosts', action: 'excludeHost' },\n      { name: 'Exclude All Matching URLs', action: 'excludeUrl' }\n    ]\n  },\n  {\n    name: 'Actions',\n    list: [\n      { name: 'Abort' },\n      { name: 'Replay' },\n      { name: 'Replay Times', action: 'replayTimes' },\n      { name: 'Edit Request' },\n      { name: 'Mark' },\n      { name: 'Unmark' }\n    ]\n  },\n  {\n    name: 'Tree',\n    list: [\n      { name: 'Expand' },\n      { name: 'Collapse' },\n      { name: 'Expand All' },\n      { name: 'Collapse All' },\n      { name: 'List View', action: 'toggleView' }\n    ]\n  },\n  { name: 'Mock' },\n  {\n    name: 'Service',\n    hide: true,\n    list: [\n      { name: 'Create API Test',  action: 'createApiTest' },\n      { name: 'Copy As Script',  action: 'copyAsScript' },\n      { name: 'Share Via URL', action: 'Export' }\n    ]\n  },\n  { name: 'Save' },\n  { name: 'Import' },\n  { name: 'Export' },\n  {\n    name: 'Others',\n    action: 'Plugins',\n    list: []\n  },\n  { name: 'Help', sep: true }\n];\n\nvar getFocusItemList = function (curItem) {\n  if (!curItem || curItem.selected) {\n    return;\n  }\n  return [curItem];\n};\n\nvar Spinner = React.createClass({\n  render: function () {\n    var order = this.props.order;\n    var desc = order == 'desc';\n    if (!desc && order != 'asc') {\n      order = null;\n    }\n    order = order ? ' spinner-' + order : '';\n    return (\n      <div className=\"w-spinner\">\n        <Icon name=\"triangle-top\" className={order} />\n        <Icon name=\"triangle-bottom\" className={order} />\n      </div>\n    );\n  }\n});\n\nfunction getColStyle(col, style) {\n  style = style ? $.extend({}, style) : {};\n  style[col.minWidth ? 'minWidth' : 'width'] = settings.getWidth(col);\n  return style;\n}\n\nfunction getRuleStyle(data) {\n  var rules = data.rules;\n  if (!rules) {\n    return '';\n  }\n  var rule = rules.rule;\n  if (rule && (rule.isLoc || rule.isSpec)) {\n    return ' w-has-local';\n  }\n  return data[dataCenter.HAS_RULES_KEY] ? ' w-has-rules' : '';\n}\n\nfunction getClassName(data) {\n  return (\n    getStatusClass(data) +\n    ' w-req-data-item' +\n    (data.isHttps ? ' w-tunnel' : '') +\n    getRuleStyle(data) +\n    (data.selected ? ' w-selected' : '') +\n    (data.isPR ? ' w-pr' : '')\n  );\n}\n\nfunction isVisible(item) {\n  return !item.hide;\n}\n\nfunction isVisibleInTree(item) {\n  var parent = item.parent;\n  return !parent || (parent.expand && parent.pExpand);\n}\n\nfunction getStatusClass(data) {\n  var type = '';\n  var headers = data.res.headers;\n  switch (util.getContentType(headers)) {\n  case 'JS':\n    type = 'warning';\n    break;\n  case 'CSS':\n    type = 'info';\n    break;\n  case 'HTML':\n    type = 'success';\n    break;\n  case 'IMG':\n    type = 'active';\n    break;\n  case 'JSON':\n    type = '_json';\n    break;\n  case 'XML':\n    type = '_xml';\n    break;\n  }\n\n  var statusCode = data.res && data.res.statusCode;\n  if (data.reqError || data.resError || (data.customData && data.customData.error)) {\n    type += ' danger w-error-status';\n  } else if (statusCode == 403) {\n    type += ' w-forbidden';\n  } else if (statusCode && (!/^\\d+$/.test(statusCode) || statusCode >= 400)) {\n    type += ' w-error-status';\n  }\n\n  if (data.mark) {\n    type += ' w-mark';\n  }\n\n  return type;\n}\n\nfunction getType(className) {\n  if (className.indexOf('w-error-status') !== -1) {\n    return 'ERROR';\n  }\n  if (className.indexOf('warning') !== -1) {\n    return 'JS';\n  }\n  if (className.indexOf('info') !== -1) {\n    return 'CSS';\n  }\n  if (className.indexOf('success') !== -1) {\n    return 'HTML';\n  }\n  if (className.indexOf('active') !== -1) {\n    return 'IMG';\n  }\n  if (className.indexOf('_json') !== -1) {\n    return 'JSON';\n  }\n  if (className.indexOf('_xml') !== -1) {\n    return 'XML';\n  }\n  return '';\n}\n\nfunction getIcon(data, className) {\n  if (className.indexOf('danger') !== -1) {\n    return <Icon name=\"remove-circle\" className=\"icon-leaf\" />;\n  }\n  if (className.indexOf('w-forbidden') !== -1) {\n    return <Icon name=\"ban-circle\" className=\"icon-leaf\" />;\n  }\n  if (data && !data.endTime && !data.lost) {\n    return <Icon name=\"hourglass\" className=\"icon-leaf\" />;\n  }\n  var type = getType(className);\n  var status = data.res && data.res.statusCode;\n  if (type !== 'ERROR' && status != 101) {\n    status = null;\n  }\n  return status || type ? <span className=\"w-type-icon\">{status || type }</span> : <Icon name=\"file\" />;\n}\n\nfunction removeHighlight(elem) {\n  elem.removeClass('highlight');\n}\n\nvar Row = React.createClass({\n  render: function () {\n    var p = this.props;\n    var order = p.order;\n    var notQuery = p.notQuery;\n    var draggable = p.draggable;\n    var columnList = p.columnList;\n    var item = p.item;\n    var style = item.style;\n\n    return (\n      <table className=\"table w-req-table\" key={p.key} style={p.style} data-id={item.id}>\n        <tbody className=\"w-hover-body\">\n          <tr\n            tabIndex=\"-1\"\n            draggable={draggable}\n            data-id={item.id}\n            className={getClassName(item) + (item.highlight ? ' w-highlight' : '')}\n            style={ROW_STYLE}\n          >\n            <th className=\"order\" scope=\"row\" style={style}>\n              {order}\n              {item.importedData ? <Icon name=\"import\" /> : null}\n              {item.fc && !item.importedData ? <Icon name=\"send\" /> : null}\n            </th>\n            {columnList.map(function (col) {\n              var name = col.name;\n              var value;\n              var url;\n              if (name === 'custom1') {\n                var key1 = dataCenter.custom1Key;\n                if (util.notEStr(key1)) {\n                  item.custom1 = util.getValue(item, key1);\n                }\n              } else if (name === 'custom2') {\n                var key2 = dataCenter.custom2Key;\n                if (util.notEStr(key2)) {\n                  item.custom2 = util.getValue(item, key2);\n                }\n              } else if (name === 'path') {\n                value = item.shortPath;\n                url = item.url;\n              }\n              value = value || util.getCellValue(item, col);\n              if (value && notQuery && name === 'path') {\n                var index = value.indexOf('?');\n                if (index !== -1) {\n                  value = value.substring(0, index);\n                }\n              }\n              var colStyle = getColStyle(col, style);\n              var icon = col.iconKey && util.getProperty(item, col.iconKey);\n              var iconElem = typeof col.getIcon === 'function' ? col.getIcon(item) : undefined;\n\n              return (\n                <td\n                  key={name}\n                  className={col.className}\n                  style={colStyle}\n                  title={col.showTitle ? (url || value) : undefined}\n                >\n                  {iconElem}\n                  {icon ? <img className=\"w-cell-img\" src={icon} /> : null}\n                  {value}\n                </td>\n              );\n            })}\n          </tr>\n        </tbody>\n      </table>\n    );\n  }\n});\n\nvar ReqData = React.createClass({\n  getInitialState: function () {\n    var dragger = settings.getDragger();\n    dragger.onDrop = dragger.onDrop.bind(this);\n    return {\n      draggable: true,\n      columns: settings.getSelectedColumns(),\n      dragger: dragger,\n      sessionsName: ''\n    };\n  },\n  componentDidUpdate: function () {\n    this.isShownBtn && events.trigger('checkAtBottom');\n    if (storage.get('disabledHNR') === '1') {\n      return;\n    }\n    var modal = this.props.modal;\n    if (!modal.isTreeView || !this.visibleList || this.startIndex == null) {\n      return;\n    }\n    var curNewIdList = dataCenter.curNewIdList;\n    if (!curNewIdList || !curNewIdList.length) {\n      return;\n    }\n    dataCenter.curNewIdList = null;\n    var leafMap = {};\n    var visibleMap = {};\n    var list = this.visibleList;\n    var lightList = [];\n    var visibleLeafMap = {};\n    var item;\n    for (var i = this.startIndex; i <= this.endIndex; i++) {\n      item = list[i];\n      if (item) {\n        if (!item.parent) {\n          $.extend(leafMap, item.map);\n          visibleMap[item.path] = 1;\n        } else if (!item.data) {\n          visibleMap[item.path] = 1;\n        } else if (isVisibleInTree(item)) {\n          visibleLeafMap[item.data.id] = 1;\n          if (curNewIdList.indexOf(item.data.id) !== -1) {\n            lightList.push(item);\n          }\n        }\n      }\n    }\n    var overflow = dataCenter.overflowCount();\n    if (overflow > 0) {\n      var hasChanged;\n      var allList = modal.list;\n      var len = allList.length;\n      for (i = 0; i < len; i++) {\n        item = allList[i];\n        if (\n          item.hide ||\n          (!item.active && !visibleLeafMap[item.id] && !leafMap[item.id])\n        ) {\n          allList.splice(i, 1);\n          --overflow;\n          --i;\n          --len;\n          hasChanged = true;\n          if (overflow <= 0) {\n            break;\n          }\n        }\n      }\n      if (overflow > 0) {\n        len = allList.length;\n        for (i = 0; i < len; i++) {\n          item = allList[i];\n          if (!item.active && !visibleLeafMap[item.id]) {\n            allList.splice(i, 1);\n            --overflow;\n            --i;\n            --len;\n            hasChanged = true;\n            if (overflow <= 0) {\n              break;\n            }\n          }\n        }\n      }\n      if (hasChanged) {\n        modal.updateTree();\n        events.trigger('updateGlobal');\n      }\n    }\n    curNewIdList.forEach(function (id) {\n      var newItem = leafMap[id];\n      if (newItem) {\n        var parent = newItem.parent;\n        while (parent) {\n          if (visibleMap[parent.path]) {\n            visibleMap[parent.path] = 0;\n            lightList.push(parent);\n          }\n          if (lightList.indexOf(newItem) === -1 && isVisibleInTree(newItem)) {\n            lightList.push(newItem);\n          }\n          parent = parent.parent;\n        }\n      }\n    });\n    var overCount = Math.floor((lightList.length - 60) / 2);\n    if (overCount > 0) {\n      lightList = lightList.slice(overCount, -overCount);\n    }\n    lightList = lightList\n      .map(function (item) {\n        var elem;\n        if (item.data) {\n          elem = $('tr[data-id=\"' + item.data.id + '\"]:not(.highlight)');\n        } else {\n          elem = $('tr[data-tree=\"' + item.path + '\"]:not(.highlight)');\n        }\n        if (elem.length) {\n          return elem.addClass('highlight');\n        }\n      })\n      .filter(util.noop);\n    if (lightList.length) {\n      setTimeout(function () {\n        lightList.forEach(removeHighlight);\n      }, 800);\n    }\n  },\n  getActivedList: function (item) {\n    var modal = this.props.modal;\n    return getFocusItemList(item) || (modal && modal.getSelectedList());\n  },\n  componentDidMount: function () {\n    var self = this;\n    var timer;\n    events.on('hashFilterChange', function () {\n      self.setState({});\n    });\n    events.on('onColumnsChanged', function () {\n      self.setState({ columns: settings.getSelectedColumns() });\n    });\n    events.on('onColumnTitleChange', function () {\n      self.setState({});\n    });\n    events.on('changeRecordState', function (_, type) {\n      self.setState({ record: type }, self.updateList);\n    });\n    events.on('selectedIndex', function (_, index) {\n      var list = self.props.modal.getList();\n      var item = list && (list[index] || list[list.length - 1]);\n      item && self.triggerActiveItem(item);\n    });\n    events.on('focusNetworkFilterInput', function() {\n      self.refs.filterInput.focus();\n    });\n    events.on('saveSessions', function (_, item) {\n      var list = self.getActivedList(item);\n      var len = list && list.length;\n      if (!len) {\n        return;\n      }\n      self._sessionsList = list;\n      self._pendingSave = false;\n      $(findDOMNode(self.refs.saveSessions)).modal('show');\n      setTimeout(function () {\n        var input = findDOMNode(self.refs.sessionsName);\n        input.focus();\n        input.select();\n      }, 500);\n    });\n    events.on('replayTreeView', function (_, dataId, count) {\n      var item = self.props.modal.getTreeNode(dataId);\n      var parent = item && item.parent;\n      if (!parent) {\n        return;\n      }\n      var list = parent.children.filter(isVisibleInTree);\n      item = list[list.length - 1];\n      item && self.scrollToRow(item, count);\n    });\n    var update = function () {\n      self.setState({});\n    };\n    var render = function () {\n      timer && clearTimeout(timer);\n      timer = setTimeout(update, 60);\n    };\n    self.container = $(findDOMNode(self.refs.container));\n    self.content = findDOMNode(self.refs.content);\n    var clickedCount = 0;\n    self.$content = $(self.content)\n      .on('dblclick', 'tr', function (e) {\n        if (clickedCount > 2) {\n          events.trigger('toggleDetailTab');\n        }\n      })\n      .on('click', 'tr', function (e) {\n        var id = this.getAttribute('data-id');\n        if (id) {\n          if (dataCenter.lastSelectedDataId != id) {\n            clickedCount = 0;\n            dataCenter.lastSelectedDataId = id;\n          }\n          ++clickedCount;\n          var item = self.props.modal.getItem(id);\n          self.onClick(e, item);\n        }\n      });\n    var toggleDraggable = function (e) {\n      var draggable = !e.shiftKey;\n      if (self.state.draggable === draggable) {\n        return;\n      }\n      self.setState({ draggable: draggable });\n    };\n    self.container\n      .on('keydown', function (e) {\n        var modal = self.props.modal;\n        toggleDraggable(e);\n        var item;\n        if (e.keyCode == 38) {\n          //up\n          if (e.ctrlKey || e.metaKey) {\n            item = modal.start();\n          } else {\n            item = modal.prev();\n          }\n        } else if (e.keyCode == 40) {\n          //down\n          if (e.ctrlKey || e.metaKey) {\n            item = modal.end();\n          } else {\n            item = modal.next();\n          }\n        }\n\n        if (item) {\n          self.onClick(e, item, true);\n          e.preventDefault();\n        }\n      })\n      .on('scroll', render)\n      .on('keyup', toggleDraggable)\n      .on('mouseover', toggleDraggable)\n      .on('mouseleave', toggleDraggable);\n\n    $(window).on('resize', render);\n    events.on('ensureSelectedItemVisible', function () {\n      var modal = self.props.modal;\n      var selected = self.props.modal.getSelectedList()[0];\n      if (selected && modal.isTreeView) {\n        selected = modal.getTreeNode(selected.id);\n      }\n      if (selected) {\n        self.scrollToRow(selected);\n      }\n    });\n    events.on('focusNetworkList', function () {\n      self.container.focus();\n    });\n    var curRemoteUrl;\n    var importRemoteUrl = function () {\n      var hash = location.hash.substring(1);\n      var index = hash.indexOf('?');\n      if (index === -1) {\n        return;\n      }\n      var sessionsUrl = util.parseQueryString(\n        hash.substring(index + 1),\n        null,\n        null,\n        decodeURIComponent\n      ).sessionsUrl;\n      if (\n        !/^https?:\\/\\/[^/]/i.test(sessionsUrl) ||\n        sessionsUrl === curRemoteUrl\n      ) {\n        return;\n      }\n      curRemoteUrl = sessionsUrl.replace(/#.*$/, '');\n      var url = curRemoteUrl;\n      if (curRemoteUrl.indexOf('&from_5b6af7b9884e1165') === -1) {\n        url += (url.indexOf('?') === -1 ? '?' : '') + '&from_5b6af7b9884e1165';\n      }\n      events.trigger('importSessionsFromUrl', url);\n    };\n    importRemoteUrl();\n    $(window).on('hashchange', importRemoteUrl);\n    var backBtn = $(findDOMNode(self.refs.backBtn));\n    var hideBackBtn = function() {\n      if (self.isShownBtn) {\n        self.isShownBtn = false;\n        backBtn.hide();\n      }\n    };\n    self.hideBackBtn = hideBackBtn;\n    self.isShownBtn = false;\n    events.on('toggleBackToBottomBtn', function(_, show) {\n      if (show) {\n        if (!self.isShownBtn) {\n          self.isShownBtn = true;\n          backBtn.show();\n        }\n      } else {\n        hideBackBtn();\n      }\n    });\n    events.on('checkViewInspectors', function(_, reqId) {\n      self._curComposerReqId = reqId && { reqId: reqId };\n      reqId && self.props.modal.getItem(reqId) && self.showViewInspectorsBtn(true);\n    });\n    events.on('setActiveSession', function(_, reqId) {\n      var item = self.props.modal.getItem(reqId);\n      item && self.triggerActiveItem(item);\n    });\n  },\n  onDragStart: function (e) {\n    var target = $(e.target).closest('.w-req-data-item');\n    var dataId = target.attr('data-id');\n    if (dataId) {\n      e.dataTransfer.setData('reqDataId', dataId);\n    }\n  },\n  getSelectedRows: function (item) {\n    var active = this.props.modal.getActive();\n    if (!active || item === active) {\n      return;\n    }\n    return [active, item];\n  },\n  onClick: function (e, item, hm) {\n    if (!item) {\n      return;\n    }\n    var self = this;\n    var modal = self.props.modal;\n    var rows;\n    var allowMultiSelect = e.ctrlKey || e.metaKey;\n    if (hm) {\n      self.clearSelection();\n      this.$content.find('tr.w-selected').removeClass('w-selected');\n      item.selected = true;\n      self.setSelected(item);\n    } else if (e.shiftKey && (rows = self.getSelectedRows(item))) {\n      this.$content.find('tr.w-selected').removeClass('w-selected');\n      modal.setSelectedList(rows[0], rows[1], self.setSelected);\n    } else {\n      if (!allowMultiSelect) {\n        this.$content.find('tr.w-selected').removeClass('w-selected');\n        self.clearSelection();\n      }\n      item.selected = !allowMultiSelect || !item.selected;\n      self.setSelected(item, true);\n    }\n\n    modal.clearActive();\n    item.active = item.selected;\n    hm && self.scrollToRow(item);\n    events.trigger('networkStateChange');\n    events.trigger('selectedSessionChange', item);\n  },\n  setSelected: function (item, unselect) {\n    if (item.selected) {\n      this.$content.find('tr[data-id=' + item.id + ']').addClass('w-selected');\n    } else if (unselect) {\n      this.$content\n        .find('tr[data-id=' + item.id + ']')\n        .removeClass('w-selected');\n    }\n  },\n  clearSelection: function () {\n    this.props.modal.clearSelection();\n  },\n  getFilterList: function () {\n    var settings = dataCenter.getFilterText();\n    if (settings.disabledExcludeText) {\n      return [];\n    }\n    return settings.excludeText.trim().split(/\\s+/g);\n  },\n  updateFilter: function (str) {\n    var settings = dataCenter.getFilterText();\n    settings.excludeText = str;\n    settings.disabledExcludeText = false;\n    dataCenter.setFilterText(settings);\n    events.trigger('filterChanged');\n  },\n  getActiveList: function (curItem) {\n    if (!curItem.selected) {\n      return [curItem];\n    }\n    return this.props.modal.getSelectedList();\n  },\n  removeAllSuchHost: function (item, justRemove) {\n    var hostList = [];\n    var list = this.getActiveList(item);\n    list.forEach(function (item) {\n      var host = item.isHttps ? item.path : item.hostname;\n      if (hostList.indexOf(host) === -1) {\n        hostList.push(host);\n      }\n    });\n    this.props.modal.removeByHostList(hostList);\n    if (!justRemove) {\n      var filterList = this.getFilterList();\n      hostList.forEach(function (host) {\n        host = 'H:' + host;\n        if (filterList.indexOf(host) === -1) {\n          filterList.unshift(host);\n        }\n      });\n      this.updateFilter(filterList.join('\\n'));\n      events.trigger('shakeSettings');\n    }\n    events.trigger('updateGlobal');\n  },\n  removeTreeNode: function (treeId, others) {\n    if (this.props.modal.removeTreeNode(treeId, others)) {\n      events.trigger('updateGlobal');\n    }\n  },\n  removeAllSuchURL: function (item, justRemove) {\n    var urlList = [];\n    var list = this.getActiveList(item);\n    list.forEach(function (item) {\n      var url = item.isHttps\n        ? item.path\n        : item.url.replace(/\\?.*$/, '').substring(0, 1024);\n      if (urlList.indexOf(url) === -1) {\n        urlList.push(url);\n      }\n    });\n    this.props.modal.removeByUrlList(urlList);\n    if (!justRemove) {\n      var filterList = this.getFilterList();\n      urlList.forEach(function (url) {\n        if (filterList.indexOf(url) === -1) {\n          filterList.unshift(url);\n        }\n      });\n      this.updateFilter(filterList.join('\\n'));\n      events.trigger('shakeSettings');\n    }\n    events.trigger('updateGlobal');\n  },\n  triggerActiveItem: function (item) {\n    this.onClick('', item, true);\n    events.trigger('networkStateChange');\n  },\n  onClickHeadMenu: function(action) {\n    var col = this.curHeadCol;\n    if (col) {\n      settings.setWidth(col.name, action);\n      this.setState({columns: settings.getSelectedColumns()});\n    }\n  },\n  onClickContextMenu: function (action, e, parentAction, name) {\n    var self = this;\n    var item = self.currentFocusItem;\n    var modal = self.props.modal;\n    var treeId = self.treeTarget;\n    var curUrl = (item && item.url) || (treeId && treeId + '/');\n    self.currentFocusItem = null;\n    switch (parentAction || action) {\n    case 'New Tab':\n      curUrl && window.open(curUrl);\n      break;\n    case 'QR Code':\n      self.refs.qrcodeDialog.show(curUrl);\n      break;\n    case 'Preview':\n      util.openPreview(item);\n      break;\n    case 'Source':\n      util.openEditor(JSON.stringify(item, null, '  '));\n      break;\n    case 'toggleView':\n      events.trigger('switchTreeView');\n      break;\n    case 'Overview':\n      self.triggerActiveItem(item);\n      events.trigger('showOverview');\n      break;\n    case 'Inspectors':\n      self.triggerActiveItem(item);\n      events.trigger('showInspectors');\n      break;\n    case 'Timeline':\n      self.triggerActiveItem(item);\n      events.trigger('showTimeline');\n      break;\n    case 'Composer':\n    case 'Edit Request':\n      events.trigger('composer', item);\n      break;\n    case 'Mark':\n    case 'Unmark':\n      var list = this.getActivedList(item);\n      if (list) {\n        var isMark = action === 'Mark';\n        list.forEach(function (item) {\n          item.mark = isMark;\n        });\n      }\n      this.setState({});\n      break;\n    case 'Replay':\n      events.trigger('replaySessions', [item, e.shiftKey]);\n      break;\n    case 'replayTimes':\n      events.trigger('replaySessions', [item, true]);\n      break;\n    case 'Export':\n      if (self.treeTarget && !self.isTreeLeafNode) {\n        events.trigger('exportSessions', [\n          modal.getListByPath(self.treeTarget)\n        ]);\n      } else {\n        events.trigger('exportSessions', item);\n      }\n      break;\n    case 'createApiTest':\n      return util.showService('createApiTest');\n    case 'copyAsScript':\n      return util.showService('copyAsScript');\n    case 'Save':\n      events.trigger('saveSessions', [item]);\n      break;\n    case 'Abort':\n      events.trigger('abortRequest', item);\n      break;\n    case 'Import':\n      events.trigger('showImportDialog');\n      break;\n    case 'Edit Settings':\n      events.trigger('filterSessions', e);\n      break;\n    case 'removeAllSuchHost':\n      curUrl && self.removeAllSuchHost(item, true);\n      break;\n    case 'removeAllSuchURL':\n      curUrl && self.removeAllSuchURL(item || curUrl, true);\n      break;\n    case 'excludeHost':\n      curUrl && self.removeAllSuchHost(item);\n      break;\n    case 'excludeUrl':\n      curUrl && self.removeAllSuchURL(item || curUrl);\n      break;\n    case 'One':\n      if (treeId) {\n        self.removeTreeNode(treeId);\n      } else {\n        events.trigger('removeIt', item);\n      }\n      break;\n    case 'All':\n      events.trigger('clearAll');\n      break;\n    case 'Others':\n      if (treeId) {\n        self.removeTreeNode(treeId, true);\n      } else {\n        events.trigger('removeOthers', item);\n      }\n      break;\n    case 'Selected':\n      events.trigger('removeSelected');\n      break;\n    case 'Unselected':\n      events.trigger('removeUnselected');\n      break;\n    case 'Unmarked':\n      events.trigger('removeUnmarked');\n      break;\n    case 'Help':\n      window.open(util.getDocUrl('gui/network.html'));\n      break;\n    case 'Plugins':\n      iframes.fork(action, {\n        port: dataCenter.getPort(),\n        type: 'network',\n        name: name,\n        activeItem: item,\n        activeList: modal.getTreeLeafs(treeId),\n        selectedList: self.props.modal.getSelectedList()\n      });\n      break;\n    case 'Expand':\n    case 'Collapse':\n      self.toggleNode(treeId);\n      break;\n    case 'Expand All':\n      self.expandAll(treeId);\n      break;\n    case 'Collapse All':\n      self.collapseAll(treeId);\n      break;\n    case 'Mock':\n      events.trigger('showMockDialog', {item: item});\n      break;\n    }\n  },\n  onHeadCtxMenu: function(e) {\n    e.preventDefault();\n    var name = $(e.target).closest('th').attr('data-name');\n    var col = settings.getColumn(name);\n    var menus = col && col.menus;\n    if (!menus) {\n      return;\n    }\n    this.curHeadCol = col;\n    var data = util.getMenuPosition(e, 130, 310);\n    data.list = menus;\n    data.radio = true;\n    data.className = 'w-ctx-radio-list';\n    this.refs.headContextMenu.show(data);\n  },\n  onContextMenu: function (e) {\n    var target = $(e.target);\n    var nodeName =  target.prop('nodeName');\n    var el = target.closest('.w-req-data-item');\n    e.preventDefault();\n    if (!el.length) {\n      el = target.closest('.w-req-table');\n    }\n    var modal = this.props.modal;\n    var dataId = el.attr('data-id');\n    clearTimeout(this._delayCtxTimer);\n    if (!modal.isTreeView && !dataId) {\n      var con = this.container.find('.ReactVirtualized__Grid:first');\n      if (con.length && document.elementFromPoint && con[0].offsetHeight < con[0].scrollHeight) {\n        var self = this;\n        var pageX = e.pageX;\n        var pageY = e.pageY;\n        this._delayCtxTimer = setTimeout(function () {\n          target = $(document.elementFromPoint(pageX, pageY)).closest('.w-req-data-item')[0];\n          target && self.onContextMenu({\n            target: target,\n            pageX: pageX,\n            pageY: pageY,\n            preventDefault: util.noop\n          });\n        }, 300);\n        return;\n      }\n    }\n    var treeId = el.attr('data-tree');\n    var item = modal.getItem(dataId);\n    var disabled = !item;\n    var cellText = item && (nodeName === 'TD' || nodeName === 'TH') && (target.text() || '').trim();\n    var treeNodeData = modal.isTreeView && modal.getTreeNode(treeId);\n    this.treeTarget = null;\n    this.currentFocusItem = item;\n    var clickBlank = disabled && !treeNodeData;\n    var list0 = contextMenuList[0].list;\n    list0[4].disabled = clickBlank || !/^https?:\\/\\//.test(treeId || item.url);\n    if (disabled || clickBlank) {\n      list0[6].disabled = true;\n    } else {\n      var type = util.getContentType(item.res.headers);\n      list0[6].disabled =\n        !item.res.base64 || (type !== 'HTML' && type !== 'IMG');\n    }\n    list0[0].disabled = clickBlank;\n    list0[1].disabled = clickBlank;\n    list0[2].disabled = disabled;\n    list0[3].disabled = disabled;\n    list0[4].disabled = disabled;\n    list0[5].disabled = disabled;\n    list0[7].disabled = disabled;\n    if (modal.isTreeView) {\n      list0[8].name = 'List View';\n    } else {\n      list0[8].name = 'Tree View';\n    }\n    contextMenuList[1].disabled = disabled && !treeId;\n    var treeUrl = treeId ? treeId + '/' : '';\n    var isTreeNode = disabled && !treeUrl;\n    contextMenuList[1].list.forEach(function (menu) {\n      menu.disabled = disabled;\n      switch (menu.name) {\n      case 'Cell Text':\n        menu.copyText = cellText;\n        menu.disabled = disabled || !cellText;\n        break;\n      case 'URL':\n        menu.copyText = util.getUrl(\n            (item && item.url.replace(/[?#].*$/, '')) || treeUrl\n          );\n        menu.disabled = isTreeNode;\n        break;\n      case 'Host':\n        menu.copyText =\n            (item && (item.isHttps ? item.path : item.hostname)) ||\n            util.getHost(treeUrl);\n        menu.disabled = isTreeNode;\n        break;\n      case 'Path':\n        menu.copyText = (item && item.path) || util.getPath(treeUrl);\n        menu.disabled = isTreeNode;\n        break;\n      case 'Full URL':\n        menu.copyText = util.getUrl((item && item.url) || treeUrl);\n        menu.disabled = isTreeNode;\n        break;\n      case 'As cURL':\n        menu.copyText = util.asCURL(item);\n        break;\n      case 'Client IP':\n        menu.copyText = item && item.clientIp;\n        break;\n      case 'Server IP':\n        var serverIp = item && util.getServerIp(item);\n        menu.disabled = !serverIp;\n        menu.copyText = serverIp;\n        break;\n      case 'Cookie':\n        var cookie = item && item.req.headers.cookie;\n        menu.disabled = !cookie;\n        menu.copyText = cookie;\n        break;\n      }\n    });\n\n    var list2 = contextMenuList[2].list;\n    contextMenuList[2].disabled = disabled;\n    for (var i = 0; i < 4; i++) {\n      list2[i].disabled = disabled;\n    }\n    if (!disabled) {\n      list2[0].disabled = !item.requestTime || !item.req.base64;\n      list2[1].disabled = !item.endTime || !item.res.base64;\n      list2[2].disabled = !item.requestTime;\n      list2[3].disabled = !item.endTime;\n    }\n\n    var selectedList = modal.getSelectedList();\n    var selectedCount = selectedList.length;\n    var hasData = modal.list.length;\n    var removeItem = contextMenuList[2].list;\n    contextMenuList[2].disabled = !hasData;\n    removeItem[0].disabled = !hasData;\n    removeItem[1].disabled = clickBlank;\n    removeItem[2].disabled = disabled || selectedCount === hasData;\n    removeItem[3].disabled = !selectedCount;\n    removeItem[4].disabled = selectedCount === hasData;\n    removeItem[5].disabled = !modal.hasUnmarked();\n    removeItem[6].disabled = clickBlank;\n    removeItem[7].disabled = clickBlank;\n\n    var filterItem = contextMenuList[3].list;\n    filterItem[1].disabled = clickBlank;\n    filterItem[2].disabled = clickBlank;\n\n    var actionItem = contextMenuList[4].list;\n    contextMenuList[4].disabled = disabled;\n    if (item) {\n      actionItem[3].disabled = false;\n      if (item.selected) {\n        actionItem[4].disabled = true;\n        actionItem[5].disabled = true;\n        selectedList.forEach(function (selectedItem) {\n          if (selectedItem.mark) {\n            actionItem[5].disabled = false;\n          } else {\n            actionItem[4].disabled = false;\n          }\n        });\n      } else {\n        var unmark = !item.mark;\n        actionItem[4].disabled = !unmark;\n        actionItem[5].disabled = unmark;\n      }\n      if (item.selected) {\n        var len = selectedList.length;\n        actionItem[0].disabled = !selectedList.filter(util.canAbort).length;\n        actionItem[1].disabled = !len;\n        actionItem[2].disabled = !len || len > 1;\n      } else {\n        actionItem[0].disabled = !util.canAbort(item);\n        actionItem[1].disabled = false;\n        actionItem[2].disabled = false;\n      }\n    } else {\n      actionItem[0].disabled = true;\n      actionItem[1].disabled = true;\n      actionItem[2].disabled = true;\n      actionItem[3].disabled = true;\n      actionItem[4].disabled = true;\n    }\n    var treeItem = contextMenuList[5];\n    var treeList = treeItem.list;\n    treeItem.hide = !modal.isTreeView;\n    treeItem.disabled = !treeNodeData && !hasData;\n    if (treeNodeData) {\n      var isLeaf = treeNodeData.data;\n      var expand = treeNodeData.expand;\n\n      this.treeTarget = treeId;\n      this.isTreeLeafNode = isLeaf;\n      treeList[0].disabled = expand || isLeaf;\n      treeList[1].disabled = !expand || isLeaf;\n      treeList[2].disabled = isLeaf;\n      treeList[3].disabled = isLeaf;\n      var count = (treeNodeData.parent || modal.root).children.length;\n      removeItem[2].disabled = count <= 1;\n    } else if (modal.isTreeView) {\n      treeList[0].disabled = true;\n      treeList[1].disabled = true;\n      treeList[2].disabled = !hasData;\n      treeList[3].disabled = !hasData;\n    }\n    var mockItem = contextMenuList[6];\n    var serviceItem = contextMenuList[7];\n    var pluginItem = contextMenuList[11];\n    serviceItem.list.forEach(function (menu, i) {\n      menu.disabled = disabled && (i < serviceItem.list.length - 1 || !treeNodeData);\n    });\n    mockItem.disabled = disabled;\n    mockItem.hide = dataCenter.hideMockMenu;\n    contextMenuList[10].disabled = clickBlank && !selectedCount;\n    contextMenuList[8].disabled = clickBlank && !selectedCount;\n    serviceItem.disabled = clickBlank;\n    serviceItem.hide = !dataCenter.whistleId;\n    util.addPluginMenus(\n      pluginItem,\n      dataCenter.getNetworkMenus(),\n      (treeItem.hide ? 9 : 10) + (serviceItem.hide ? 0 : 1) - (mockItem.hide ? 1 : 0),\n      disabled,\n      treeId,\n      item && item.url\n    );\n    var height = (treeItem.hide ? 370 : 400) - (serviceItem.hide ? 30 : 0) - (pluginItem.hide ? 30 : 0) - (mockItem.hide ? 30 : 0);\n    pluginItem.maxHeight = height;\n    var data = util.getMenuPosition(e, 110, height);\n    data.list = contextMenuList;\n    data.className = data.marginRight < 360 ? 'w-ctx-menu-left' : '';\n    this.refs.contextMenu.show(data);\n  },\n  updateList: function () {\n    this.refs.content.refs.list.forceUpdateGrid();\n    events.trigger('checkAtBottom');\n  },\n  onFilterChange: function (keyword) {\n    var self = this;\n    var filterBody = BODY_FILTER.test(keyword);\n    clearTimeout(self.networkStateChangeTimer);\n    !filterBody && self.props.modal.search(keyword);\n    self.networkStateChangeTimer = setTimeout(function () {\n      filterBody && self.props.modal.search(keyword);\n      self.setState({ filterText: keyword }, self.updateList);\n      events.trigger('networkStateChange');\n    }, 600);\n  },\n  onFilterTypeChange: function (type) {\n    var self = this;\n    var baseDom = self.container;\n    var atBottom;\n    if (baseDom) {\n      baseDom = baseDom.find('.ReactVirtualized__Grid:first');\n      var body = baseDom.find('.ReactVirtualized__Grid__innerScrollContainer')[0];\n      if (body) {\n        var height = baseDom[0].offsetHeight + 5;\n        var ctnHeight = body.offsetHeight;\n        atBottom = ctnHeight <= height || baseDom[0].scrollTop + height >= ctnHeight;\n      } else {\n        atBottom = true;\n      }\n    }\n    self.props.modal.setFilterType(type);\n    self.setState({ filterType: type }, function() {\n      self.updateList();\n      if (atBottom) {\n        self.autoRefresh();\n        self._scrollTimer = setTimeout(self.autoRefresh, 30);\n      } else {\n        clearTimeout(self._scrollTimer);\n      }\n    });\n  },\n  onFilterKeyDown: function (e) {\n    if (e.keyCode !== 13 || !CMD_RE.test(e.target.value)) {\n      return;\n    }\n    dataCenter.setDumpCount(parseInt(RegExp.$1, 10));\n    this.props.modal.clear();\n    this.refs.filterInput.clearFilterText();\n  },\n  autoRefresh: function () {\n    this.container.find('.ReactVirtualized__Grid:first')[0].scrollTop = 100000000;\n    this.hideBackBtn();\n  },\n  orderBy: function (e) {\n    var target = this.willResort && $(e.target).closest('th')[0];\n    if (!target) {\n      return;\n    }\n    var name = target.className;\n    var order;\n    if (name == 'order') {\n      columnState = {};\n      columnKeys = {};\n    } else {\n      order = columnState[name];\n      columnKeys[name] = target.getAttribute('data-key');\n      if (order == 'desc') {\n        columnState[name] = 'asc';\n      } else if (order == 'asc') {\n        columnState[name] = null;\n      } else {\n        columnState[name] = 'desc';\n      }\n    }\n\n    var sortColumns = [];\n    Object.keys(columnState).forEach(function (name) {\n      if ((order = columnState[name])) {\n        sortColumns.push({\n          name: name,\n          order: order,\n          key: columnKeys[name]\n        });\n      }\n    });\n    this.props.modal.setSortColumns(sortColumns);\n    this.setState({});\n  },\n  onColumnsResort: function () {\n    this.setState({ columns: settings.getSelectedColumns() });\n  },\n  onMouseDown: function (e) {\n    this.willResort = e.target.className !== 'w-header-drag-block';\n  },\n  onReplay: function (e) {\n    if (!e.metaKey && !e.ctrlKey) {\n      return;\n    }\n    if (e.keyCode === 13) {\n      if (!util.hasShortcut( e.shiftKey ? 'replaySelectedRequestsTimes' : 'replaySelectedRequests')) {\n        return;\n      }\n      events.trigger('replaySessions', [null, e.shiftKey]);\n    } else if (e.keyCode === 65) {\n      if (!util.hasShortcut('abortRequest')) {\n        return;\n      }\n      e.preventDefault();\n      events.trigger('abortRequest');\n    }\n  },\n  renderColumn: function (col) {\n    var name = col.name;\n    var style = getColStyle(col);\n    if (columnState[name]) {\n      style.color = 'var(--c-link)';\n    }\n    var title;\n    if (name === 'custom1' || name === 'custom2') {\n      title = dataCenter[name];\n    } else {\n      title = col.title;\n    }\n    return (\n      <th\n        onMouseDown={this.onMouseDown}\n        {...this.state.dragger}\n        data-name={name}\n        draggable={true}\n        key={name}\n        data-key={col.key}\n        className={col.className}\n        style={style}\n      >\n        {title}\n        <Spinner order={columnState[name]} />\n      </th>\n    );\n  },\n  scrollToRow: function (target, count) {\n    if (target && (target.id || (target.data && target.data.id))) {\n      var index = this.getVisibleList().indexOf(target);\n      if (index === -1) {\n        return;\n      }\n      target = index + (count > 0 ? count : 0);\n    }\n    try {\n      this.refs.content.refs.list.scrollToRow(target);\n    } catch (e) {}\n    this.container.focus();\n  },\n  getTreeNode: function (e) {\n    var modal = this.props.modal;\n    if (typeof e === 'string') {\n      return modal.getTreeNode(e);\n    }\n    var elem = $(e.target).closest('.w-req-data-item');\n    return modal.getTreeNode(elem.attr('data-tree'));\n  },\n  toggleNode: function (e) {\n    var node = this.getTreeNode(e);\n    if (node) {\n      if (node.expand) {\n        util.collapse(node);\n      } else {\n        util.expand(node);\n      }\n      this.setState({});\n    }\n  },\n  expandAll: function (e) {\n    if (!e) {\n      var root = this.props.modal.getTree();\n      root.children.forEach(util.expandAll);\n      return this.setState({});\n    }\n    var node = this.getTreeNode(e);\n    if (node) {\n      util.expandAll(node);\n      this.setState({});\n    }\n  },\n  collapseAll: function (e) {\n    if (!e) {\n      var root = this.props.modal.getTree();\n      root.children.forEach(util.collapseAll);\n      return this.setState({});\n    }\n    var node = this.getTreeNode(e);\n    if (node) {\n      util.collapseAll(node);\n      this.setState({});\n    }\n  },\n  saveSessions: function (e) {\n    var self = this;\n    if (self._pendingSave || (e && e.type !== 'click' && e.keyCode !== 13)) {\n      return;\n    }\n    var list = self._sessionsList;\n    self._pendingSave = true;\n    dataCenter.saveSessions(JSON.stringify({\n      filename: self.state.sessionsName.trim(),\n      sessions: list\n    }), function (data, xhr) {\n      self._pendingSave = false;\n      if (!data) {\n        return util.showSystemError(xhr);\n      }\n      if (data.em) {\n        return message.error(data.em);\n      }\n      $(findDOMNode(self.refs.saveSessions)).modal('hide');\n      self._sessionsList = null;\n      self.setState({ sessionsName: '' });\n      events.trigger('shakeSavedTab');\n      events.trigger('savedSessionsChanged');\n      message.success('Sessions saved successfully');\n    });\n  },\n  preventBlur: function (e) {\n    e.target.nodeName != 'INPUT' && e.preventDefault();\n  },\n  renderTreeNode: function (item, options) {\n    var draggable = this.state.draggable;\n    var style = options.style;\n    var leaf = item.data;\n    var className = leaf ? getClassName(leaf) : '';\n    var value = item.value;\n    style.marginLeft = item.depth * 32;\n    return (\n      <tr\n        key={leaf ? leaf.id : item.path}\n        style={style}\n        className={'w-req-data-item tree-node' + (leaf ? ' tree-leaf' : '') + (className ? ' ' + className : '')}\n        data-id={leaf && leaf.id}\n        data-tree={item.path}\n        draggable={leaf && draggable}\n        onClick={leaf ? null : this.toggleNode}\n        title={leaf ? util.getUrl(leaf.url) : value}\n        onKeyDown={function () {}}\n      >\n        {leaf ? (\n          getIcon(leaf, className)\n        ) : <Icon name={'triangle-' + (item.expand ? 'bottom' : 'right')} className=\"icon-fold\" />}\n        {value.length > 320 ? value.substring(0, 320) + '...' : value}\n      </tr>\n    );\n  },\n  enableRecord: function () {\n    events.trigger('enableRecord');\n  },\n  getVisibleList: function () {\n    var modal = this.props.modal;\n    return modal.isTreeView\n      ? modal.getTree().list.filter(isVisibleInTree)\n      : modal.getList().filter(isVisible);\n  },\n  filterSessionsName: function (e) {\n    this.setState({ sessionsName: util.formatFilename(e.target.value) });\n  },\n  showViewInspectorsBtn: function(visible) {\n    var composerReqId = this._curComposerReqId;\n    if (composerReqId && composerReqId.visible !== visible) {\n      events.trigger('showViewInspectorsBtn', visible);\n      composerReqId.visible = visible;\n    }\n  },\n  render: function () {\n    var self = this;\n    var state = this.state;\n    var modal = self.props.modal;\n    var isTreeView = modal.isTreeView;\n    var list = this.getVisibleList();\n    var hasKeyword = modal.hasKeyword();\n    var draggable = state.draggable;\n    var columnList = state.columns.list;\n    var colStyle = state.columns.style;\n    var filterText = (state.filterText || '').trim();\n    var record = state.record;\n    var notQuery = storage.get('urlType') === '-';\n    var composerReqId = self._curComposerReqId;\n    var len = list.length;\n    var hasChanged = !len;\n    self.startIndex = null;\n    self.endIndex = null;\n    self.visibleList = list;\n    hasChanged && self.showViewInspectorsBtn(false);\n\n    return (\n      <div className={'fill w-req-data-con v-box' + (self.props.hide ? ' hide' : '')}>\n        <div\n          className=\"w-req-data-ctn fill v-box\"\n          style={colStyle}\n        >\n          {record ? (\n            <div className=\"w-record-status\">\n              {record === 'stop' ? 'Recording stopped' : 'Recording paused'}\n              <button className=\"btn btn-primary\" onClick={self.enableRecord}>\n                Enable\n              </button>\n            </div>\n          ) : null}\n          <div className={'w-req-data-headers' + (isTreeView ? ' hide' : '')}>\n            <table className=\"table\">\n              <thead>\n                <tr onClick={self.orderBy} onContextMenu={self.onHeadCtxMenu}>\n                  <th className=\"order\" data-name=\"order\">#</th>\n                  {columnList.map(self.renderColumn)}\n                </tr>\n              </thead>\n            </table>\n          </div>\n          <div\n            ref=\"container\"\n            tabIndex=\"0\"\n            onContextMenu={self.onContextMenu}\n            onKeyDown={self.onReplay}\n            style={{\n              background:\n                dataCenter.hashFilterObj || filterText || state.filterType\n                  ? 'var(--b-filtered)'\n                  : undefined\n            }}\n            className={\n              'w-req-data-list fill' + (isTreeView ? ' w-tree-view-list' : '')\n            }\n            onDragStart={self.onDragStart}\n          >\n            <RV.AutoSizer ref=\"content\">\n              {function (size) {\n                return (\n                  <RV.List\n                    ref=\"list\"\n                    rowHeight={isTreeView ? TREE_ROW_HEIGHT : 28}\n                    width={size.width}\n                    height={size.height}\n                    rowCount={len}\n                    rowRenderer={function (options) {\n                      var index = options.index;\n                      var item = list[index];\n                      if (composerReqId && item.id === composerReqId.reqId) {\n                        hasChanged = true;\n                        self.showViewInspectorsBtn(true);\n                      }\n                      if (isTreeView) {\n                        if (self.startIndex == null) {\n                          self.startIndex = index;\n                        }\n                        self.endIndex = index;\n                        return self.renderTreeNode(item, options);\n                      }\n                      var order = hasKeyword ? index + 1 : item.order;\n                      !hasChanged && index === len - 1 && self.showViewInspectorsBtn(false);\n                      return (\n                        <Row\n                          style={options.style}\n                          key={options.key}\n                          order={order}\n                          columnList={columnList}\n                          draggable={draggable}\n                          notQuery={notQuery}\n                          item={item}\n                        />\n                      );\n                    }}\n                  />\n                );\n              }}\n            </RV.AutoSizer>\n          </div>\n          <div className={'w-back-to-the-bottom' + (isTreeView ? ' hide' : '')} ref=\"backBtn\" onClick={this.autoRefresh}>\n            <Icon name=\"arrow-down\" />\n            Back to the bottom\n          </div>\n        </div>\n        <FilterInput\n          ref=\"filterInput\"\n          onKeyDown={this.onFilterKeyDown}\n          onChange={this.onFilterChange}\n          wStyle={colStyle}\n          addonHints={HINTS}\n          onFilterTypeChange={this.onFilterTypeChange}\n          hintKey=\"networkHintList\"\n        />\n        <ContextMenu onClick={this.onClickContextMenu} ref=\"contextMenu\" />\n        <ContextMenu onClick={this.onClickHeadMenu} ref=\"headContextMenu\" />\n        <QRCodeDialog ref=\"qrcodeDialog\" />\n        <div ref=\"saveSessions\" className=\"modal fade w-choose-filte-type\">\n          <div className=\"modal-dialog\">\n            <div className=\"modal-content\">\n              <div className=\"modal-body\">\n                <label className=\"w-choose-filte-type-label w-save-sessions-label\">\n                  Save as:\n                  <input\n                    ref=\"sessionsName\"\n                    onChange={this.filterSessionsName}\n                    onKeyDown={this.saveSessions}\n                    placeholder=\"Enter filename (optional)\"\n                    className=\"form-control\"\n                    maxLength={MAX_LEN}\n                    value={state.sessionsName || ''}\n                  />\n                </label>\n              </div>\n              <div className=\"modal-footer\">\n                <button\n                  type=\"button\"\n                  className=\"btn btn-default\"\n                  data-dismiss=\"modal\"\n                >\n                  Cancel\n                </button>\n                <button\n                  type=\"button\"\n                  onKeyDown={this.saveSessions}\n                  tabIndex=\"0\"\n                  onMouseDown={this.preventBlur}\n                  className=\"btn btn-primary\"\n                  onClick={this.saveSessions}\n                >\n                  Save\n                </button>\n                </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ReqData;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/req-detail.js",
    "content": "require('../css/req-detail.css');\nvar React = require('react');\nvar Divider = require('./divider');\nvar Properties = require('./properties');\nvar util = require('./util');\nvar BtnGroup = require('./btn-group');\nvar JSONViewer = require('./json-viewer');\nvar Textarea = require('./textarea');\nvar dataCenter = require('./data-center');\nvar PluginsTabs = require('./plugins-tabs');\nvar events = require('./events');\n\nvar BTNS = [\n  { name: 'Raw' },\n  { name: 'Headers' },\n  { name: 'WebForms' },\n  { name: 'TextView', display: 'Body' },\n  { name: 'JSONView' },\n  { name: 'HexView' },\n  { name: 'Cookies' },\n  { name: 'Plugins', hide: true }\n];\n\nvar ReqDetail = React.createClass({\n  getInitialState: function () {\n    return {\n      initedHeaders: false,\n      initedTextView: false,\n      initedCookies: false,\n      initedWebForms: false,\n      initedJSONView: false,\n      initedHexView: false,\n      initedRaw: false,\n      initPlugins: false\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('reqTabsChange', function () {\n      self.setState({});\n    });\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onClickBtn: function (btn) {\n    this.selectBtn(btn);\n    this.setState({});\n  },\n  selectBtn: function (btn) {\n    btn.active = true;\n    this.state.btn = btn;\n    this.state['inited' + btn.name] = true;\n  },\n  onEdit: function () {\n    events.trigger('setComposerData', this.props.modal);\n  },\n  render: function () {\n    var state = this.state;\n    var btn = state.btn;\n    if (!btn) {\n      btn = BTNS[0];\n      this.selectBtn(btn);\n    }\n    var name = btn && btn.name;\n    var modal = this.props.modal;\n    var req,\n      headers,\n      headersStr,\n      rawHeaders,\n      cookies,\n      body,\n      raw,\n      query,\n      form,\n      tips,\n      json,\n      defaultName,\n      bin,\n      base64;\n    body = raw = '';\n    if (modal) {\n      req = modal.req;\n      rawHeaders = req.rawHeaders;\n      defaultName = util.getFilename(modal, true);\n      body = util.getBody(req, true);\n      bin = util.getHex(req);\n      base64 = req.base64;\n      headers = req.headers;\n      json = util.getJson(req, true, decodeURIComponent);\n      delete headers.Host;\n      cookies = util.parseQueryString(\n        headers.cookie,\n        /;\\s*/g,\n        null,\n        decodeURIComponent\n      );\n      var realUrl = util.getRealUrl(modal);\n      var index = realUrl.indexOf('?');\n      query = index == -1 ? '' : realUrl.substring(index + 1);\n      query = query && util.parseQueryString(\n        query,\n        null,\n        null,\n        decodeURIComponent\n      );\n      if (util.isUrlEncoded(req)) {\n        form = util.parseQueryString(\n          util.getBody(req, true),\n          null,\n          null,\n          decodeURIComponent\n        );\n        if (!window.___hasFormData) {\n          form = null;\n        }\n      } else if (util.isUploadForm(req)) {\n        form = util.parseUploadBody(req, true);\n      } else if (json && json.isJSONText) {\n        form = json;\n      }\n      headersStr = util.getReqRawHeaders(modal);\n      raw = headersStr + '\\r\\n\\r\\n' + body;\n      if (modal.frames) {\n        tips = { isFrames: true };\n      } else if (modal.isHttps) {\n        tips = { isHttps: true };\n      } else if (\n        modal.requestTime &&\n        modal.useFrames !== false &&\n        !body &&\n        !/^ws/.test(modal.url)\n      ) {\n        if (req.size < 5120) {\n          tips = { message: 'Empty request body' };\n        } else {\n          raw += '(Request body exceeds display limit)';\n          tips = { message: 'Request body exceeds display limit' };\n        }\n      }\n    }\n    state.raw = raw;\n    state.body = body;\n    base64 = base64 || '';\n    var pluginsTab = BTNS[7];\n    var tabs = dataCenter.getReqTabs();\n    var len = tabs.length;\n    pluginsTab.display = undefined;\n    pluginsTab.title = undefined;\n    pluginsTab.className = undefined;\n    pluginsTab.hide = !len;\n    if (len && len === 1) {\n      pluginsTab.display = pluginsTab.title = tabs[0].name;\n      pluginsTab.className = 'w-detail-custom-tab w-req';\n    } else {\n      pluginsTab.display = undefined;\n      pluginsTab.title = undefined;\n      pluginsTab.className = undefined;\n    }\n\n    return (\n      <div\n        className={\n          'fill v-box w-detail-ctn w-detail-request' +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <BtnGroup onClick={this.onClickBtn} btns={BTNS} />\n        {state.initedRaw ? (\n          <Textarea\n            reqData={modal}\n            onEdit={this.onEdit}\n            reqType=\"reqRaw\"\n            defaultName={defaultName}\n            value={raw}\n            headers={headersStr}\n            base64={base64}\n            className=\"fill w-detail-request-raw\"\n            hide={name != BTNS[0].name}\n          />\n        ) : undefined}\n        {state.initedHeaders ? (\n          <div\n            className={\n              'fill w-detail-request-headers' +\n              (name == BTNS[1].name ? '' : ' hide')\n            }\n          >\n            <Properties modal={rawHeaders || headers} enableViewSource=\"1\" />\n          </div>\n        ) : (\n          ''\n        )}\n        {state.initedWebForms ? (\n          <Divider\n            vertical=\"true\"\n            hideRight={!form}\n            hideLeft={!query}\n            splitRatio={0.6}\n            className={\n              'w-detail-request-webforms' +\n              (name == BTNS[2].name ? '' : ' hide')\n            }\n          >\n            <div className=\"fill v-box\">\n              <div className=\"w-detail-webforms-title\">Query</div>\n              <div className=\"fill v-box w-detail-request-query\">\n                <Properties modal={query} enableViewSource=\"1\" showJsonView=\"1\" />\n              </div>\n            </div>\n            <div className=\"fill v-box\">\n              <div className=\"w-detail-webforms-title\">Body</div>\n              <div className=\"fill v-box w-detail-request-form\">\n                {!json || !json.isJSONText ? <Properties modal={form} richKey=\"1\" enableViewSource=\"1\" showJsonView=\"1\" /> :\n                <JSONViewer reqData={modal} data={json} />}\n              </div>\n            </div>\n          </Divider>\n        ) : (\n          ''\n        )}\n        {state.initedTextView ? (\n          <Textarea\n            reqData={modal}\n            reqType=\"reqBody\"\n            defaultName={defaultName}\n            tips={tips}\n            base64={base64}\n            value={body}\n            className=\"fill w-detail-request-textview\"\n            hide={name != BTNS[3].name}\n          />\n        ) : undefined}\n        {state.initedJSONView ? (\n          <JSONViewer\n            reqData={modal}\n            reqType=\"reqRaw\"\n            defaultName={defaultName}\n            data={json}\n            tips={tips}\n            hide={name != BTNS[4].name}\n          />\n        ) : undefined}\n        {state.initedHexView ? (\n          <Textarea\n            reqData={modal}\n            reqType=\"reqBody\"\n            defaultName={defaultName}\n            tips={tips}\n            isHexView=\"1\"\n            base64={base64}\n            value={bin}\n            className=\"fill n-monospace w-detail-request-hex\"\n            hide={name != BTNS[5].name}\n          />\n        ) : undefined}\n        {state.initedCookies ? (\n          <div\n            className={\n              'fill w-detail-request-cookies' +\n              (name == BTNS[6].name ? '' : ' hide')\n            }\n          >\n            <Properties modal={cookies} enableViewSource=\"1\" />\n          </div>\n        ) : undefined}\n        {state.initedPlugins ? (\n          <PluginsTabs\n            tabs={tabs}\n            hide={name != pluginsTab.name || pluginsTab.hide}\n          />\n        ) : undefined}\n      </div>\n    );\n  }\n});\n\nmodule.exports = ReqDetail;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/res-detail.js",
    "content": "require('../css/res-detail.css');\nvar React = require('react');\nvar Table = require('./table');\nvar Properties = require('./properties');\nvar util = require('./util');\nvar BtnGroup = require('./btn-group');\nvar Textarea = require('./textarea');\nvar ImageView = require('./image-view');\nvar JSONViewer = require('./json-viewer');\nvar dataCenter = require('./data-center');\nvar PluginsTabs = require('./plugins-tabs');\nvar events = require('./events.js');\n\nvar COOKIE_HEADERS = [\n  'Name',\n  'Value',\n  'Domain',\n  'Path',\n  'Expires',\n  'Max-Age',\n  'HttpOnly',\n  'Secure',\n  'SameSite',\n  'Partitioned'\n];\n\nvar ResDetail = React.createClass({\n  getInitialState: function () {\n    return {\n      initedHeaders: false,\n      initedTrailers: false,\n      initedTextView: false,\n      initedPreview: false,\n      initedCookies: false,\n      initedJSONView: false,\n      initedHexView: false,\n      initedRaw: false,\n      initPlugins: false,\n      btns: [\n        { name: 'Raw' },\n        { name: 'Headers' },\n        { name: 'Preview' },\n        { name: 'TextView', display: 'Body' },\n        { name: 'JSONView' },\n        { name: 'HexView' },\n        { name: 'Cookies' },\n        { name: 'Trailers' },\n        { name: 'Plugins', hide: true }\n      ]\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('resTabsChange', function () {\n      self.setState({});\n    });\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onClickBtn: function (btn) {\n    this.selectBtn(btn);\n    this.setState({});\n  },\n  selectBtn: function (btn) {\n    btn.active = true;\n    this.state.btn = btn;\n    this.state['inited' + btn.name] = true;\n  },\n  render: function () {\n    var state = this.state;\n    var btns = state.btns;\n    var btn = state.btn;\n    if (!btn) {\n      btn = btns[0];\n      this.selectBtn(btn);\n    }\n    var name = btn && btn.name;\n    var modal = this.props.modal;\n    var res,\n      rawHeaders,\n      rawTrailers,\n      headersStr,\n      trailerStr,\n      headers,\n      trailers,\n      cookies,\n      body,\n      raw,\n      json,\n      tips,\n      defaultName,\n      base64,\n      bin;\n    body = raw = '';\n    if (modal) {\n      res = modal.res;\n      defaultName = util.getFilename(modal, true);\n      rawHeaders = res.rawHeaders;\n      rawTrailers = res.rawTrailers;\n      body = util.getBody(res);\n      bin = util.getHex(res);\n      base64 = res.base64;\n      headers = res.headers;\n      trailers = res.trailers;\n      json = util.getJson(res);\n      if (headers && headers['set-cookie']) {\n        cookies = headers['set-cookie'];\n        if (!Array.isArray(cookies)) {\n          cookies = typeof cookies == 'string' ? [cookies] : [];\n        }\n        cookies = cookies.map(function (cookie) {\n          cookie = util.parseQueryString(\n            cookie,\n            /;\\s*/,\n            null,\n            decodeURIComponent,\n            true\n          );\n          var row = ['', '', '', '', '', '', '', '', '', ''];\n          for (var i in cookie) {\n            switch (i.toLowerCase()) {\n            case 'domain':\n              row[2] = cookie[i];\n              break;\n            case 'path':\n              row[3] = cookie[i];\n              break;\n            case 'expires':\n              row[4] = cookie[i];\n              break;\n            case 'max-age':\n              row[5] = cookie[i];\n              break;\n            case 'httponly':\n              row[6] = '√';\n              break;\n            case 'secure':\n              row[7] = '√';\n              break;\n            case 'samesite':\n              row[8] = cookie[i];\n              break;\n            case 'partitioned':\n              row[9] = '√';\n              break;\n            default:\n              if (!row[0]) {\n                row[0] = i;\n                row[1] = cookie[i];\n              }\n            }\n          }\n\n          return row;\n        });\n      }\n      var imgSrc, data, isJson;\n      var isText = true;\n      var status = res.statusCode;\n      var showImg = name === btns[2].name;\n      if (status != null) {\n        headersStr = util.getResRawHeaders(modal);\n        trailerStr = trailers ? util.objectToString(trailers, res.rawTrailerNames) : '';\n        raw = headersStr + '\\r\\n\\r\\n' + body;\n        var rawType = !modal.resError && util.getRawType(headers);\n        var type = util.getContentType(rawType);\n        isJson = type === 'JSON';\n        // 对 SVG 做特殊处理, 利用 base64 ，图片标签展示 svg 元素\n        if (rawType === 'image/svg+xml') {\n          imgSrc = 'data:image/svg+xml;base64,' + (res.base64 || '');\n          isText = false;\n        } else if (type === 'IMG') {\n          imgSrc = body || (res.size ? modal.url : undefined);\n          isText = false;\n        } else if (showImg && res.base64 && (type === 'HTML' || (json && json.isJSONText && util.likeJson(body)))) {\n          if (json && json.isJSONText) {\n            isJson = true;\n          } else if (\n            !body ||\n            (body.indexOf('<') !== -1 && body.indexOf('>') !== -1)\n          ) {\n            data = modal;\n            isText = false;\n          }\n        }\n      }\n      if (imgSrc) {\n        data = modal;\n      }\n      if (modal.frames) {\n        tips = { isFrames: true, inComposer: modal.inComposer };\n      } else if (modal.isHttps) {\n        tips = !body && { isHttps: true };\n      } else if (\n        res.size >= 0 &&\n        headers &&\n        modal.useFrames !== false &&\n        !body &&\n        modal.endTime &&\n        !/^ws/.test(modal.url)\n      ) {\n        tips = { url: modal.url };\n        if (res.size < 5120) {\n          tips.message = 'Empty response body';\n        } else {\n          raw += '(Response body exceeds display limit)';\n          tips.message = 'Response body exceeds display limit';\n        }\n      }\n      if (trailerStr) {\n        raw += '\\r\\n\\r\\n' + trailerStr;\n      }\n    }\n\n    state.raw = raw;\n    state.body = body;\n    if (isText && name === 'Preview') {\n      showImg = false;\n      if (isJson) {\n        name = 'JSONView';\n        state.initedJSONView = true;\n      } else {\n        name = 'TextView';\n        state.initedTextView = true;\n      }\n    }\n    base64 = base64 || '';\n\n    var pluginsTab = btns[8];\n    var tabs = dataCenter.getResTabs();\n    var len = this.props.inComposer ? 0 : tabs.length;\n    pluginsTab.hide = !len;\n    if (len && len === 1) {\n      pluginsTab.display = pluginsTab.title = tabs[0].name;\n      pluginsTab.className = 'w-detail-custom-tab';\n    } else {\n      pluginsTab.display = undefined;\n      pluginsTab.title = undefined;\n      pluginsTab.className = undefined;\n    }\n    return (\n      <div\n        className={\n          'fill v-box w-detail-ctn w-detail-res' +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <BtnGroup onClick={this.onClickBtn} btns={btns} />\n        {state.initedRaw ? (\n          <Textarea\n            reqData={modal}\n            reqType=\"resRaw\"\n            defaultName={defaultName}\n            value={raw}\n            headers={headersStr}\n            base64={base64}\n            className=\"fill w-detail-res-raw\"\n            hide={name != btns[0].name}\n          />\n        ) : undefined}\n        {state.initedHeaders ? (\n          <div\n            className={\n              'fill w-detail-res-headers' +\n              (name == btns[1].name ? '' : ' hide')\n            }\n          >\n            <Properties modal={rawHeaders || headers} enableViewSource=\"1\" />\n          </div>\n        ) : undefined}\n        {state.initedPreview ? (\n          <ImageView imgSrc={imgSrc} data={data} hide={!showImg} />\n        ) : undefined}\n        {state.initedTextView ? (\n          <Textarea\n            reqData={modal}\n            reqType=\"resBody\"\n            defaultName={defaultName}\n            tips={tips}\n            base64={base64}\n            value={body}\n            className=\"fill w-detail-res-textview\"\n            hide={name != btns[3].name}\n          />\n        ) : undefined}\n        {state.initedJSONView ? (\n          <JSONViewer\n            reqData={modal}\n            reqType=\"resJson\"\n            defaultName={defaultName}\n            data={json}\n            tips={tips}\n            hide={name != btns[4].name}\n          />\n        ) : undefined}\n        {state.initedHexView ? (\n          <Textarea\n            reqData={modal}\n            reqType=\"resBody\"\n            defaultName={defaultName}\n            isHexView=\"1\"\n            base64={base64}\n            tips={tips}\n            value={bin}\n            className=\"fill n-monospace w-detail-res-hex\"\n            hide={name != btns[5].name}\n          />\n        ) : undefined}\n        {state.initedCookies ? (\n          <div\n            className={\n              'fill w-detail-res-cookies' +\n              (name == btns[6].name ? '' : ' hide')\n            }\n          >\n            {cookies && cookies.length ? (\n              <Table head={COOKIE_HEADERS} modal={cookies} />\n            ) : undefined}\n          </div>\n        ) : undefined}\n        {state.initedTrailers ? (\n          <div\n            className={\n              'fill w-detail-res-headers' +\n              (name == btns[7].name ? '' : ' hide')\n            }\n          >\n            <Properties modal={rawTrailers || trailers} enableViewSource=\"1\" />\n          </div>\n        ) : undefined}\n        {state.initedPlugins ? (\n          <PluginsTabs\n            tabs={tabs}\n            hide={name != pluginsTab.name || pluginsTab.hide}\n          />\n        ) : undefined}\n      </div>\n    );\n  }\n});\n\nmodule.exports = ResDetail;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/rule-list.js",
    "content": "var React = require('react');\nvar Divider = require('./divider');\nvar util = require('./util');\n\nvar RuleList = React.createClass({\n  getInitialState: function () {\n    return { active: 'Default', checkedList: [] };\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  onClick: function (e) {\n    this.setState({\n      active: e.target.getAttribute('data-name')\n    });\n  },\n  onChange: function (e, line) {\n    var checkedList = this.state.checkedList;\n    var index = checkedList.indexOf(line);\n\n    if (e.target.checked) {\n      if (index === -1) {\n        checkedList.push(line);\n      }\n    } else if (index !== -1) {\n      checkedList.splice(index, 1);\n    }\n    this.setState({ checkedList: checkedList });\n    this.props.onChange(checkedList);\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var state = self.state;\n    var active = state.active;\n    var modal = props.modal;\n    var checkedList = state.checkedList;\n    var list = modal.list;\n    var activeItem = modal.map[active];\n    if (!activeItem) {\n      active = list[0];\n      activeItem = modal.map[active];\n    }\n\n    return (\n      <Divider leftWidth=\"220\" className={props.hide ? ' hide' : ''}>\n        <div className=\"w-list-data w-rule-list-name\">\n        {list.map(function (item) {\n          if (util.isGroup(item.name)) {\n            return null;\n          }\n          var count = 0;\n          item.checked = false;\n          item.rules.forEach(function(r) {\n            if (checkedList.indexOf(r.join(' ')) !== -1) {\n              count++;\n              item.checked = true;\n            }\n          });\n          return (\n            <a\n              tabIndex=\"0\"\n              key={item.name}\n              draggable=\"false\"\n              data-name={item.name}\n              className={(active === item.name ? 'w-active' : '') + (count ? ' w-bold' : '')}\n              onClick={self.onClick}\n            >{item.name + (count ? ' (' + count + ')' : '')}</a>\n          );\n        })}\n        </div>\n        <div className=\"fill w-rule-list-ctn\">\n          {activeItem && activeItem.rules.map(function (rule) {\n            var line = rule.join(' ');\n            var checked = checkedList.indexOf(line) !== -1;\n\n            return (\n              <label key={line} className={'w-rule-list-item' + (checked ? ' w-bold' : '')}>\n                <span><input type=\"checkbox\" checked={checked} onChange={function(e) {\n                  self.onChange(e, line, rule, activeItem.rawValues);\n                }} /></span>\n                <div>\n                  {rule.map(function (r, i) {\n                    return <span key={i}>{r}</span>;\n                  })}\n                </div>\n              </label>\n            );\n          })}\n        </div>\n      </Divider>\n    );\n  }\n});\n\nmodule.exports = RuleList;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/rules-dialog.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar Dialog = require('./dialog');\nvar dataCenter = require('./data-center');\nvar Editor = require('./editor');\nvar storage = require('./storage');\nvar events = require('./events');\nvar util = require('./util');\nvar win = require('./win');\nvar CloseBtn = require('./close-btn');\n\nvar TEMP_FILE_RE = /\\btemp\\/current_file_hash_placeholder\\b/;\nvar TEMP_FILE_RE_G = /\\btemp\\/current_file_hash_placeholder\\b/g;\nvar LINE__RE = /^(?:[^\\n\\r\\S]*(```+)[^\\n\\r\\S]*(\\S+)[^\\n\\r\\S]*[\\r\\n]([\\s\\S]+?)[\\r\\n][^\\n\\r\\S]*\\1\\s*|[^\\r\\n]*)$/gm;\n\nfunction getName(name) {\n  name = name || storage.get('previewRulesName');\n  var rulesModal = dataCenter.getRulesModal();\n  if (name) {\n    if (rulesModal.getItem(name)) {\n      return name;\n    }\n  }\n  var names = rulesModal.getSelectedNames();\n  for (var i = 0, len = names.length; i < len; i++) {\n    name = names[i];\n    if (name !== 'Default') {\n      return name;\n    }\n  }\n  return 'Default';\n}\n\nvar RulesDialog = React.createClass({\n  getInitialState: function () {\n    return { rulesName: getName(), newRulesName: ''  };\n  },\n  show: function (rules, values) {\n    this._rules = rules;\n    this._values = values;\n    this._hasChanged = false;\n    this.setValue();\n    this.refs.rulesDialog.show();\n  },\n  onRulesChange: function(e) {\n    var name = e.target.value;\n    var self = this;\n    if (name) {\n      if (!this._hasChanged) {\n        return self.setValue(name, true);\n      }\n      win.confirm('Unsaved changes will be lost. Continue?', function(sure) {\n        if (sure) {\n          self._hasChanged = false;\n          self.setValue(name, true);\n        }\n      });\n      return;\n    }\n    self.showCreateRules();\n  },\n  showCreateRules: function() {\n    this.refs.createRules.show();\n    var input = ReactDOM.findDOMNode(this.refs.rulesName);\n    setTimeout(function() {\n      input.select();\n      input.focus();\n    }, 300);\n  },\n  onRulesValueChange: function(e) {\n    this._hasChanged = true;\n    this.setState({rulesValue: e.getValue()});\n  },\n  onNewNameChange: function(e) {\n    var name = e.target.value.replace(/\\s+/g, '');\n    this.setState({newRulesName: name});\n  },\n  createRules: function() {\n    var self = this;\n    var filename = self.state.newRulesName;\n    dataCenter.rules.add({\n      name: filename,\n      value: ''\n    }, function (result, xhr) {\n      if (result && result.ec === 0) {\n        events.trigger('addNewRulesFile', {\n          filename: filename,\n          data: ''\n        });\n        self.refs.createRules.hide();\n        self.setState({ newRulesName: '', rulesName: filename });\n        self.setValue(filename, true);\n      } else {\n        util.showSystemError(xhr);\n      }\n    });\n  },\n  createTempFile: function(cb) {\n    var self = this;\n    var state = self.state;\n    var values = self._values;\n    var rulesValue = state.rulesValue;\n    if (!values || !values.isFile) {\n      return self.saveValue(cb);\n    }\n    var hasError;\n    var createFile = function(base64, value, init) {\n      dataCenter.createTempFile(JSON.stringify({\n        clientId: dataCenter.getPageId(),\n        value: value,\n        base64: base64\n      }), function (result, xhr) {\n        if (result && result.ec === 0) {\n          init && cb(TEMP_FILE_RE.test(rulesValue) ? result.filepath : null);\n        } else if (!hasError) {\n          hasError = true;\n          util.showSystemError(xhr);\n        }\n      });\n    };\n    createFile(values.base64, values.value, true);\n    var list = values.list;\n    list = Array.isArray(list) ? list.slice(0, 10) : [];\n    list.forEach(function(base64) {\n      createFile(base64);\n    });\n  },\n  saveValue: function(cb) {\n    var self = this;\n    var values = self._values;\n    var name = values && values.name;\n    var value = values && values.value;\n    if (!name || values.isFile || !util.isString(name) || !util.isString(value)) {\n      return cb();\n    }\n    var next = function(sure) {\n      if (sure) {\n        dataCenter.values.add({\n          name: name,\n          value: value\n        }, function (data, xhr) {\n          if (data && data.ec === 0) {\n            events.trigger('addNewValuesFile', {\n              filename: name,\n              data: value\n            });\n            cb();\n          } else {\n            util.showSystemError(xhr);\n          }\n        });\n      }\n    };\n    var item = dataCenter.getValuesModal().getItem(name);\n    if (item && item.value !== value) {\n      return win.confirm('The name `' + name + '` is already in use. Overwrite?', next);\n    }\n    next(true);\n  },\n  save: function() {\n    var self = this;\n    var state = self.state;\n    var rulesValue = state.rulesValue;\n    var filename = state.rulesName;\n    self.createTempFile(function(filepath) {\n      var values;\n      if (filepath) {\n        values = null;\n        rulesValue = rulesValue.replace(TEMP_FILE_RE_G, filepath);\n        var curRules = (self._rules || '').replace(TEMP_FILE_RE_G, filepath);\n        if (curRules) {\n          var hasRule;\n          rulesValue = rulesValue.replace(LINE__RE, function(line, _, key) {\n            if (key) {\n              return line;\n            }\n            if (line === curRules || line.trim().split(/\\s+/).join(' ') === curRules) {\n              hasRule = true;\n              return '';\n            }\n            return line;\n          });\n          if (hasRule) {\n            if (dataCenter.backRulesFirst) {\n              rulesValue = rulesValue.replace(/\\s+$/, '') + '\\n\\n' + curRules + '\\n';\n            } else {\n              rulesValue = curRules + '\\n\\n' + rulesValue.replace(/^\\s+/, '');\n            }\n          }\n        }\n      } else {\n        values = self._values;\n      }\n      dataCenter.addRulesAndValues(JSON.stringify({\n        clientId: dataCenter.getPageId(),\n        rules: {\n          name: filename,\n          value: rulesValue\n        },\n        values: values\n      }), function (result, xhr) {\n        if (result && result.ec === 0) {\n          events.trigger('addNewRulesFile', {\n            filename: filename,\n            data: rulesValue\n          });\n          events.trigger('addMockRulesSuccess');\n          if (values) {\n            events.trigger('addNewValuesFile', {\n              filename: values.name,\n              data: values.value\n            });\n          }\n          self.refs.rulesDialog.hide();\n          events.trigger('hideMockDialog');\n        } else {\n          util.showSystemError(xhr);\n        }\n      });\n    });\n  },\n  setValue: function(name, immediate) {\n    if (name) {\n      storage.set('previewRulesName', name);\n    } else {\n      name = getName();\n    }\n    var rulesModal = dataCenter.getRulesModal();\n    var item = rulesModal.getItem(name);\n    var value = item && item.value || '';\n    var lf = value ? '\\n\\n' : '\\n';\n    var curRules = this._rules;\n    if (value.indexOf(curRules) !== -1 && !/[\\r\\n]/.test(curRules)) {\n      if (value.indexOf('```') === -1) {\n        value = value.split(/\\r\\n|\\r|\\n/).map(function(line) {\n          return line.trim().split(/\\s+/).join(' ') === curRules ? '' : line;\n        }).join('\\n');\n      } else {\n        value = value.replace(LINE__RE, function(line, _, key) {\n          if (key) {\n            return line;\n          }\n          return line.trim().split(/\\s+/).join(' ') === curRules ? '' : line;\n        });\n      }\n    }\n    if (dataCenter.backRulesFirst) {\n      value = value.replace(/\\s+$/, '') + lf + curRules + '\\n';\n    } else {\n      value = curRules + lf + value.replace(/^\\s+/, '');\n    }\n    var self = this;\n    var editor = this.refs.editor;\n    var handleEnd = function() {\n      dataCenter.backRulesFirst && editor._editor.scrollTo(0, 10000000);\n      self.setState({ rulesValue: value });\n    };\n    if (immediate) {\n      setTimeout(handleEnd, 100);\n    } else {\n      setTimeout(handleEnd, 360);\n    }\n    self.setState({ rulesName: getName(name) });\n  },\n  render: function () {\n    var rulesModal = dataCenter.getRulesModal();\n    var state = this.state;\n    var newRulesName = state.newRulesName;\n    var list = rulesModal.list;\n    return (\n      <Dialog ref=\"rulesDialog\" wstyle=\"w-rules-dialog\">\n        <div className=\"modal-body\">\n          <CloseBtn />\n          <div className=\"modal-title\">\n            Select Rules File:\n            <select className=\"form-control\" onChange={this.onRulesChange} value={state.rulesName}>\n              {list.map(function(name) {\n                return <option key={name} value={name}>{name}</option>;\n              })}\n              <option value=\"\">+Create</option>\n            </select>\n            <button className=\"btn btn-default\" onClick={this.showCreateRules}>+Create</button>\n          </div>\n          <Editor\n            value={state.rulesValue}\n            onChange={this.onRulesValueChange}\n            ref=\"editor\"\n            mode=\"rules\"\n            theme={storage.get('rulesTheme') || 'cobalt'}\n            fontSize={storage.get('rulesFontSize') || '14px'}\n            lineNumbers={storage.get('showRulesLineNumbers') === 'true'}\n            lineWrapping={!!storage.get('autoRulesLineWrapping')}\n          />\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Cancel\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-primary\"\n            onClick={this.save}\n          >\n            Save\n          </button>\n        </div>\n        <Dialog ref=\"createRules\" wstyle=\"w-create-rules-dialog\">\n          <div className=\"modal-body\">\n            New Rules Filename:\n            <input ref=\"rulesName\" style={{marginTop: 6}} className=\"form-control\"\n              maxLength=\"64\" onChange={this.onNewNameChange} value={newRulesName} placeholder=\"Enter name\" />\n          </div>\n          <div className=\"modal-footer\">\n            <button\n              type=\"button\"\n              className=\"btn btn-default\"\n              data-dismiss=\"modal\"\n            >\n              Cancel\n            </button>\n            <button\n              type=\"button\"\n              className=\"btn btn-primary\"\n              onClick={this.createRules}\n              disabled={!newRulesName || list.indexOf(newRulesName) !== -1}\n            >\n              Confirm\n            </button>\n          </div>\n        </Dialog>\n      </Dialog>\n    );\n  }\n});\n\nvar MockDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (text, values) {\n    this.refs.rulesDialog.show(text, values);\n  },\n  render: function () {\n    return <RulesDialog ref=\"rulesDialog\" />;\n  }\n});\n\nmodule.exports = MockDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/rules-hint.js",
    "content": "require('codemirror/addon/hint/show-hint.css');\nrequire('codemirror/addon/hint/show-hint.js');\nvar $ = require('jquery');\nvar CodeMirror = require('codemirror');\nvar protocols = require('./protocols');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\n\nvar disabledEditor = window.location.href.indexOf('disabledEditor=1') !== -1;\nvar NON_SPECAIL_RE = /[^:/]/;\nvar PLUGIN_NAME_RE = /^((?:whistle\\.)?([a-z\\d_-]+:))(\\/?$|\\/\\/)/;\nvar MAX_HINT_LEN = 512;\nvar MAX_VAR_LEN = 100;\nvar AT_RE = /^@/;\nvar P_RE = /^%/;\nvar P_VAR_RE = /^%([a-z\\d_-]+)([=.])/;\nvar VAL_RE = /^([a-z\\d_.-]+:\\/\\/)?(`)?\\{([^\\s]*?)(?:\\}\\1?)?$/i;\nvar PROTOCOL_RE = /^([^\\s:]+):\\/\\//;\nvar VALUE_RE = /^\\s*```/;\nvar HINT_TIMEOUT = 120;\nvar curHintMap = {};\nvar curHintProto,\n  curFocusProto,\n  curHintValue,\n  curHintList,\n  hintTimer,\n  curHintPos,\n  curHintOffset;\nvar hintUrl, hintCgi, waitingRemoteHints;\nvar extraKeys = { 'Alt-/': 'autocomplete' };\nvar FILTERS = [\n  '<keyword or regex for URL>',\n  'm:<keyword or regex for HTTP method>',\n  'b:<keyword or regex for request body>',\n  's:<keyword or regex for response status code>',\n  'clientIp:<keyword or regex for client IP address>',\n  'serverIp:<keyword or regex for server IP address>',\n  'chance:<probability between 0 and 1>',\n  'reqH.header-key:<keyword or regex for request header value>',\n  'resH.header-key:<keyword or regex for response header value>'\n];\nvar HEADERS = [\n  'reqH.header-key:(keyword|regex)=<replacement>',\n  'resH.header-key:(keyword|regex)=<replacement>',\n  'trailer.header-key:(keyword|regex)=<replacement>'\n];\nvar DEL_HINTS = [\n  'pathname.<index>',\n  'urlParams.<param-key>',\n  'reqHeaders.<header-key>',\n  'resHeaders.<header-key>',\n  'reqCookies.<cookie-key>',\n  'resCookies.<cookie-key>',\n  'reqBody.<key.path>',\n  'resBody.<key.path>',\n  'pathname',\n  'urlParams',\n  'reqType',\n  'resType',\n  'reqCharset',\n  'resCharset',\n  'reqBody',\n  'resBody'\n];\n\nvar LINE_PROPS_HINTS = ['important', 'safeHtml', 'strictHtml', 'disableAutoCors', 'disableUserLogin', 'enableUserLogin',\n  'internal', 'internalOnly', 'internalProxy', 'proxyFirst', 'proxyHost', 'proxyHostOnly', 'proxyTunnel', 'weakRule', 'enableBigData'];\n\nvar ENABLE_HINTS = ['abort', 'abortReq', 'abortRes', 'authCapture', 'auto2http', 'bigData', 'br', 'gzip', 'deflate',\n  'capture', 'captureIp', 'captureStream', 'clientCert', 'clientId', 'clientIp', 'customParser', 'flushHeaders', 'forHttp', 'forHttps',\n  'forceReqWrite', 'forceResWrite', 'h2', 'http2', 'httpH2', 'hide', 'hideComposer', 'hideCaptureError', 'showHost', 'ignoreSend', 'ignoreReceive',\n  'pauseSend', 'pauseReceive', 'inspect', 'interceptConsole', 'internalProxy', 'proxyFirst', 'proxyHost', 'proxyTunnel', 'keepCSP', 'keepAllCSP', 'keepCache',\n  'keepAllCache', 'keepClientId', 'safeHtml', 'strictHtml', 'multiClient', 'reqMergeBigData', 'resMergeBigData', 'requestWithMatchedRules', 'responseWithMatchedRules', 'tunnelHeadersFirst',\n  'useLocalHost', 'useSafePort', 'userLogin', 'weakRule', 'socket', 'websocket'];\nvar DISABLE_HINTS = ['301', 'abort', 'abortReq', 'abortRes', 'authCapture', 'auto2http', 'autoCors',  'ajax', 'bigData', 'capture', 'captureIp', 'captureStream',\n  'clientCert', 'clientId', 'clientIp', 'customParser', 'cache', 'dnsCache', 'csp', 'cookies', 'reqCookies', 'resCookies', 'flushHeaders', 'forHttp', 'forHttps', 'forceReqWrite',\n  'forceResWrite', 'gzip', 'h2', 'http2', 'httpH2', 'hide', 'hideComposer', 'hideCaptureError', 'interceptConsole', 'internalProxy', 'proxyFirst',\n  'proxyHost', 'proxyTunnel', 'keepCSP', 'keepAllCSP', 'keepCache', 'keepAllCache', 'keepAlive', 'keepClientId', 'keepH2Session', 'safeHtml', 'strictHtml',\n  'multiClient', 'proxyConnection', 'ua', 'proxyUA', 'referer', 'rejectUnauthorized', 'reqMergeBigData', 'resMergeBigData', 'requestWithMatchedRules', 'responseWithMatchedRules', 'secureOptions', 'servername',\n  'timeout', 'trailerHeader', 'trailers', 'tunnelAuthHeader', 'tunnelHeadersFirst', 'useLocalHost', 'useSafePort', 'userLogin', 'weakRule'];\nvar CHARS = [\n  '-',\n  '\"_\"',\n  'Shift-2',\n  '.',\n  ',',\n  'Shift-,',\n  'Shift-.',\n  'Shift-;',\n  '/',\n  'Shift-/',\n  'Shift-1',\n  'Shift-4',\n  'Shift-5',\n  'Shift-6',\n  'Shift-7',\n  'Shift-8',\n  '=',\n  'Shift-=',\n  '\\'',\n  'Shift-\\'',\n  ';',\n  'Shift-;',\n  '\\\\',\n  'Shift-\\\\',\n  'Shift-`',\n  '[',\n  ']',\n  'Shift-[',\n  'Shift-]',\n  'Shift-9',\n  'Shift-0'\n];\nfor (var i = 0; i < 10; i++) {\n  CHARS.push('\\'' + i + '\\'');\n}\nfor (var a = 'a'.charCodeAt(), z = 'z'.charCodeAt(); a <= z; a++) {\n  var ch = String.fromCharCode(a);\n  CHARS.push('\\'' + ch.toUpperCase() + '\\'');\n  CHARS.push('\\'' + ch + '\\'');\n}\n\n$(window).on('hashchange', function () {\n  var disabled = window.location.href.indexOf('disabledEditor=1') !== -1;\n  if (disabled !== disabledEditor) {\n    disabledEditor = disabled;\n  }\n});\n\nfunction isExactMatch(curWord, list) {\n  var item = list && list.length === 1 && list[0];\n  return item && (item.text || item) === curWord;\n}\n\nvar curKeys;\nvar curRules;\n\nfunction getInlineKeys() {\n  var rulesModal = dataCenter.rulesModal;\n  var list = rulesModal && rulesModal.getSelectedList();\n  var active = rulesModal && rulesModal.getActive();\n  if (active) {\n    if (!list) {\n      list = [active];\n    } else if (list.indexOf(active) === -1) {\n      list.push(active);\n    }\n  }\n  var value = list && list.map(function(item) {\n    return item.value;\n  }).join('\\n');\n  if (!value) {\n    return;\n  }\n  if (curRules !== value) {\n    curRules = value;\n    var values = {};\n    util.resolveInlineValues(curRules, values);\n    curKeys = Object.keys(values);\n    if (!curKeys.length) {\n      curKeys = null;\n    }\n  }\n  return curKeys;\n}\n\nfunction getHintCgi(plugin, pluginVars) {\n  var moduleName = plugin.moduleName;\n  var url = (pluginVars && pluginVars.hintUrl) || plugin.hintUrl || '';\n  var pluginName = 'plugin.' + util.getSimplePluginName(plugin);\n  if (url.indexOf(moduleName) !== 0 && url.indexOf(pluginName) !== 0) {\n    url = pluginName + '/' + url;\n  }\n  if (hintUrl !== url) {\n    if (hintCgi) {\n      hintCgi.hasDestroyed = true;\n    }\n    hintUrl = url;\n    hintCgi = dataCenter.createCgi(url, true);\n  }\n  return hintCgi;\n}\n\nfunction getHints(keyword) {\n  if (disabledEditor) {\n    return [];\n  }\n  var allRules = protocols.getAllRules();\n  if (!keyword) {\n    return allRules;\n  }\n  keyword = keyword.toLowerCase();\n  var list = allRules.filter(function (name) {\n    if ((name === 'socks://' || name === 'xsocks://') && 'proxy'.indexOf(keyword) !== -1) {\n      return true;\n    }\n    name = name.toLowerCase();\n    return name.indexOf(keyword) !== -1 || (name === 'tlsoptions://' && 'cipher://'.indexOf(keyword) !== -1);\n  });\n  list.sort(function (cur, next) {\n    var curIndex = cur.toLowerCase().indexOf(keyword);\n    var nextIndex = next.toLowerCase().indexOf(keyword);\n    if (curIndex === nextIndex) {\n      return 0;\n    }\n    return curIndex < 0 || (curIndex > nextIndex && nextIndex >= 0) ? 1 : -1;\n  });\n  if (keyword === 'csp') {\n    list.push('disable://csp');\n  } else if ('upstream'.indexOf(keyword) !== -1) {\n    list.push('proxy://', 'xproxy://');\n  } else if ('xupstream'.indexOf(keyword) !== -1) {\n    list.push('xproxy://');\n  } else if ('extend'.indexOf(keyword) !== -1) {\n    list.push('reqMerge://', 'resMerge://');\n  }\n  var index1 = list.indexOf('redirect://');\n  var index2 = list.indexOf('locationHref://');\n  if (index1 !== -1) {\n    if (index2 !== -1) {\n      if (index1 > index2) {\n        list.splice(index1, 1);\n        list.splice(index2 + 1, 0, 'redirect://');\n      } else {\n        list.splice(index2, 1);\n        list.splice(index1 + 1, 0, 'locationHref://');\n      }\n    } else {\n      list.splice(index1 + 1, 0, 'locationHref://');\n    }\n  } else if (index2 !== -1) {\n    list.splice(index2 + 1, 0, 'redirect://');\n  }\n  return list;\n}\n\nfunction getFilterHint(filter) {\n  var index = filter.indexOf('<');\n  var text = index === -1 ? filter : filter.substring(0, index);\n  return {\n    text: text.replace('.header-key', '._headerKey_').replace('(keyword|regex)=', '_keywordOrRegEx_='),\n    displayText: filter\n  };\n}\n\nfunction getFilterHints(keyword, filter1, filter2) {\n  keyword = keyword.toLowerCase();\n  if (/[:=]/.test(keyword)) {\n    return [];\n  }\n  var filters1 = [];\n  var filters2 = [];\n  FILTERS.forEach(function(filter, i) {\n    if (!keyword || (i && filter.toLowerCase().indexOf(keyword) !== -1)) {\n      filters1.push(getFilterHint(filter1 + filter));\n      if (filter2) {\n        filters2.push(getFilterHint(filter2 + filter));\n      }\n    }\n  });\n  return filters1.concat(filters2);\n}\n\nfunction getSpecHints(keyword, protocol, hints) {\n  var getHint = function(hint) {\n    return getFilterHint(protocol + hint);\n  };\n  if (!keyword) {\n    return hints.map(getHint);\n  }\n  var result = [];\n  keyword = keyword.toLowerCase();\n  hints.forEach(function (hint) {\n    if (hint.toLowerCase().indexOf(keyword) !== -1) {\n      result.push(getHint(hint));\n    }\n  });\n  return result;\n}\n\nfunction getAtValueList(keyword) {\n  keyword = keyword.substring(1);\n  try {\n    var getList = window.parent.getAtValueListForWhistle;\n    if (typeof getList !== 'function') {\n      return;\n    }\n    var list = getList(keyword);\n    if (Array.isArray(list)) {\n      var result = [];\n      var len = 60;\n      list.forEach(function (item) {\n        if (!item || len < 1) {\n          return;\n        }\n        if (typeof item === 'string') {\n          --len;\n          result.push(item);\n          return;\n        }\n        var value = item.value;\n        if (!value || typeof value !== 'string') {\n          return;\n        }\n        --len;\n        var label = item.label;\n        if (!label || typeof label !== 'string') {\n          result.push(value);\n        } else {\n          result.push({\n            text: value,\n            displayText: label\n          });\n        }\n      });\n      return result;\n    }\n  } catch (e) {}\n}\n\nfunction getSpecHint(name, specProto) {\n  return {\n    text: name,\n    displayText: name + '(' + (specProto === 'pipe' ? 'pipe' : 'sni') + 'Value)'\n  };\n}\n\nfunction getPluginVarHints(keyword, specProto) {\n  var originalKeyword = keyword;\n  var list;\n  if (specProto) {\n    keyword = keyword.substring(specProto.length + 3);\n    list = protocols.getAllPluginNameList().map(function (name) {\n      return specProto + '://' + name;\n    });\n  } else {\n    keyword = keyword.substring(1);\n    list = protocols.getPluginVarList();\n  }\n  if (!keyword) {\n    return specProto ? list.map(function(name) {\n      return getSpecHint(name, specProto);\n    }) : list;\n  }\n  keyword = keyword.toLowerCase();\n  if (specProto) {\n    keyword = specProto + '://' + keyword;\n    var result = [];\n    list.filter(function (name) {\n      if (name.indexOf(keyword) !== -1) {\n        result.push(getSpecHint(name, specProto));\n      }\n    });\n    return result.length === 1 && result[0].text === originalKeyword ? [] : result;\n  }\n  return list.filter(function (name) {\n    return name.indexOf(keyword) !== -1;\n  });\n}\n\nfunction getAtHelpUrl(name, options) {\n  try {\n    var _getAtHelpUrl = window.parent.getAtHelpUrlForWhistle;\n    if (typeof _getAtHelpUrl === 'function') {\n      var url = _getAtHelpUrl(name, options);\n      if (url === false || typeof url === 'string') {\n        return url;\n      }\n    }\n  } catch (e) {}\n  return util.getDocUrl('rules/@.html');\n}\n\nfunction getRuleHelp(plugin, helpUrl) {\n  if (typeof helpUrl !== 'string') {\n    helpUrl = '';\n  }\n  return (\n    helpUrl || plugin.homepage || util.getDocUrl('extensions/usage.html')\n  );\n}\n\nfunction getHintText(protoName, text, isVar, isKey) {\n  if (!isVar) {\n    return protoName + '://' + text;\n  }\n  return protoName + (isKey ? '.' : '=') + text;\n}\n\nfunction handleRemoteHints(data, editor, plugin, protoName, value, cgi, isVar, curWord) {\n  curHintList = [];\n  curHintMap = {};\n  curHintPos = null;\n  curHintOffset = 0;\n  if (\n    !data ||\n    cgi.hasDestroyed ||\n    (!Array.isArray(data) && !Array.isArray(data.list))\n  ) {\n    curHintValue = curHintProto = null;\n    return;\n  }\n  curHintValue = value;\n  curHintProto = protoName;\n  var len = 0;\n  if (!Array.isArray(data)) {\n    curHintPos = data.position;\n    curHintOffset = parseInt(data.offset, 10) || 0;\n    data = data.list;\n  }\n  var maxLen = isVar ? MAX_VAR_LEN : MAX_HINT_LEN;\n  data.forEach(function (item) {\n    if (len >= 60) {\n      return;\n    }\n    if (typeof item === 'string') {\n      item = getHintText(protoName, item.trim(), isVar);\n      if (item.length < maxLen && !curHintMap[item]) {\n        ++len;\n        curHintList.push(item);\n        curHintMap[item] = getRuleHelp(plugin);\n      }\n    } else if (item) {\n      var label = item.label || item.displayText || item.display;\n      var curVal = item.value || item.text;\n      label = typeof label === 'string' ? label.trim() : '';\n      curVal = typeof curVal === 'string' ? curVal.trim() : '';\n      if (curVal) {\n        curVal = getHintText(protoName, curVal, isVar, item.isKey);\n      }\n      if (curVal && curVal.length < maxLen && !curHintMap[label || curVal]) {\n        ++len;\n        curHintList.push(\n          label && label !== curVal\n            ? {\n              displayText: label,\n              text: curVal\n            }\n            : curVal\n        );\n        curHintMap[label || curVal] = getRuleHelp(plugin, item.help);\n      }\n    }\n  });\n  if (isExactMatch(curWord, curHintList)) {\n    curHintList = [];\n    len = 0;\n  }\n  if (waitingRemoteHints && len) {\n    editor._byPlugin = true;\n    editor.execCommand('autocomplete');\n  }\n}\n\nfunction isFilterProtocol(name) {\n  return name === 'includeFilter://' || name === 'excludeFilter://';\n}\n\nfunction getSpecProto(keyword) {\n  if (!keyword) {\n    return;\n  }\n  if (!keyword.indexOf('pipe://')) {\n    return 'pipe';\n  }\n  if (!keyword.indexOf('sniCallback://')) {\n    return 'sniCallback';\n  }\n  if (keyword.indexOf('://') !== -1) {\n    return;\n  }\n  keyword = keyword.toLowerCase();\n  var isPipe = 'pipe://'.indexOf(keyword) !== -1;\n  if (!isPipe && 'snicallback://'.indexOf(keyword) === -1) {\n    return;\n  }\n  var allRules = protocols.getAllRules();\n  var curProto = isPipe ? 'pipe://' : 'sniCallback://';\n  for (var i = 0, len = allRules.length; i < len; i++) {\n    var rule = allRules[i];\n    if (rule !== curProto && rule.toLowerCase().indexOf(keyword) !== -1) {\n      return;\n    }\n  }\n\n  return isPipe ? 'pipe' : 'sniCallback';\n}\n\nfunction getSpecHintOptions(list) {\n  var len = list.length;\n  var rule = len === 1 ? list[0] : '';\n  if (!rule) {\n    return;\n  }\n  if (rule.indexOf('headerReplace://') === 0) {\n    return {\n      isHeader: true,\n      protocol: 'headerReplace://',\n      hints: HEADERS\n    };\n  }\n  if (rule.indexOf('delete://') === 0) {\n    return {\n      isDelete: true,\n      protocol: 'delete://',\n      hints: DEL_HINTS\n    };\n  }\n  if (rule.indexOf('lineProps://') === 0) {\n    return {\n      isLineProps: true,\n      protocol: 'lineProps://',\n      hints: LINE_PROPS_HINTS\n    };\n  }\n  if (rule.indexOf('enable://') === 0) {\n    return {\n      isEnable: true,\n      protocol: 'enable://',\n      hints: ENABLE_HINTS\n    };\n  }\n  if (rule.indexOf('disable://') === 0) {\n    return {\n      isDisable: true,\n      protocol: 'disable://',\n      hints: DISABLE_HINTS\n    };\n  }\n}\n\nvar WORD = /\\S+/;\nvar HTTP_RE = /^https?:\\/?\\/?/;\nvar SPEC_HINT_RE = /^(headerReplace|includeFilter|excludeFilter|delete|lineProps|enable|disable):\\/\\//;\nvar showAtHint;\nvar showVarHint;\nvar canShowHint;\nvar toValKey = function(key, tpl) {\n  return tpl + '{' + key + '}' + tpl;\n};\nCodeMirror.registerHelper('hint', 'rulesHint', function (editor) {\n  showAtHint = false;\n  showVarHint = false;\n  waitingRemoteHints = false;\n  curFocusProto = null;\n  var hasShownHint = canShowHint;\n  canShowHint = false;\n  var byDelete = editor._byDelete || editor._byPlugin;\n  var byEnter = editor._byEnter;\n  editor._byDelete = editor._byPlugin = editor._byEnter = false;\n  var cur = editor.getCursor();\n  var curLine = editor.getLine(cur.line);\n  if (VALUE_RE.test(curLine)) {\n    return;\n  }\n  var end = cur.ch,\n    start = end,\n    list;\n  var commentIndex = curLine.indexOf('#');\n  if (commentIndex !== -1 && commentIndex < start) {\n    return;\n  }\n  while (start && WORD.test(curLine.charAt(start - 1))) {\n    --start;\n  }\n  var curWord = start == end ? '' : curLine.substring(start, end);\n  var isAt = AT_RE.test(curWord);\n  var plugin;\n  var pluginName;\n  var value;\n  var pluginVars;\n  var sep;\n  var specProto = getSpecProto(curWord);\n  var isPluginVar = P_RE.test(curWord);\n  var isPluginKey;\n  if (isPluginVar && P_VAR_RE.test(curWord)) {\n    pluginName = RegExp.$1;\n    sep = RegExp.$2;\n    plugin = pluginName && dataCenter.getPlugin(pluginName + ':');\n    pluginVars = plugin && plugin.pluginVars;\n    if (!pluginVars) {\n      return;\n    }\n    value = curWord.substring(pluginName.length + 2);\n    isPluginVar = sep === '.';\n    isPluginKey = isPluginVar;\n  }\n\n  if (isAt || specProto || isPluginVar) {\n    if (!byEnter || isPluginKey || /^(?:pipe|sniCallback):\\/\\/$/.test(curWord)) {\n      list = isAt\n        ? getAtValueList(curWord)\n        : getPluginVarHints(curWord, specProto);\n    }\n    var varLen = list && list.length;\n    var onlyOne = isExactMatch(curWord, list);\n    var noHint = !varLen || onlyOne;\n    if (isPluginKey) {\n      if (onlyOne && /=./.test(value)) {\n        return;\n      }\n    } else if (noHint) {\n      return;\n    }\n    if (isAt) {\n      showAtHint = true;\n    } else if (isPluginVar) {\n      showVarHint = true;\n    }\n    if (!noHint) {\n      return {\n        list: list,\n        from: CodeMirror.Pos(cur.line, start),\n        to: CodeMirror.Pos(cur.line, end)\n      };\n    }\n    if (onlyOne) {\n      byEnter = false;\n    }\n  }\n  if (curWord) {\n    if (VAL_RE.test(curWord)) {\n      var protoLen = RegExp.$1.length;\n      var tplStart = RegExp.$2;\n      var valKeyword = RegExp.$3;\n      var valuesModal = dataCenter.getValuesModal();\n      var valuesKeys = valuesModal && valuesModal.list;\n      var inlineKyes = getInlineKeys();\n      if (inlineKyes) {\n        if (valuesKeys) {\n          valuesKeys.forEach(function(key) {\n            if (inlineKyes.indexOf(key) === -1) {\n              inlineKyes.push(key);\n            }\n          });\n        }\n        valuesKeys = inlineKyes;\n      }\n      if (valKeyword.slice(-2) === '}`') {\n        valKeyword = valKeyword.slice(0, -2);\n      }\n      if (valuesKeys && valuesKeys.length) {\n        if (valKeyword) {\n          list = [];\n          var lowerKey = valKeyword.toLowerCase();\n          valuesKeys.forEach(function(key) {\n            var lk = key.toLowerCase();\n            if (lk === lowerKey) {\n              list.unshift(toValKey(key, tplStart));\n            } else if (lk.indexOf(lowerKey) !== -1) {\n              list.push(toValKey(key, tplStart));\n            }\n          });\n        } else {\n          list = valuesKeys.map(function(key) {\n            return toValKey(key, tplStart);\n          });\n        }\n        var valLen = list && list.length;\n        if (valLen) {\n          if (isExactMatch(curWord.substring(protoLen), list)) {\n            return;\n          }\n          curLine = curLine.substring(start).split(/\\s/, 1)[0] || '';\n          return {\n            list: list,\n            from: CodeMirror.Pos(cur.line, start + protoLen),\n            to: CodeMirror.Pos(cur.line, start + curLine.length)\n          };\n        }\n      }\n    }\n    if (plugin || PLUGIN_NAME_RE.test(curWord)) {\n      plugin = plugin || dataCenter.getPlugin(RegExp.$2);\n      var pluginConf = pluginVars || plugin;\n      if (\n        plugin &&\n        (typeof pluginConf.hintUrl === 'string' || pluginConf.hintList)\n      ) {\n        if (!pluginVars) {\n          value = RegExp.$3 || '';\n          value =\n            value.length === 2\n              ? curWord.substring(curWord.indexOf('//') + 2)\n              : '';\n          if (value && (value.length > MAX_HINT_LEN || byEnter)) {\n            return;\n          }\n        } else if (value && (byEnter || value.length > MAX_VAR_LEN)) {\n          return;\n        }\n        clearTimeout(hintTimer);\n        var protoName = pluginVars ? '%' + pluginName : RegExp.$1.slice(0, -1);\n        if (pluginConf.hintList) {\n          if (value) {\n            value = value.toLowerCase();\n            curHintList = pluginConf.hintList.filter(function (item) {\n              if (typeof item === 'string') {\n                return item.toLowerCase().indexOf(value) !== -1;\n              }\n              if (item.text.toLowerCase().indexOf(value) !== -1) {\n                return true;\n              }\n              return (\n                item.displayText &&\n                item.displayText.toLowerCase().indexOf(value) !== -1\n              );\n            });\n          } else {\n            curHintList = pluginConf.hintList;\n          }\n          if (!curHintList.length) {\n            return;\n          }\n          curHintMap = {};\n          curHintList = curHintList.map(function (item) {\n            var hint;\n            var text;\n            if (typeof item === 'string') {\n              text = getHintText(protoName, item, pluginVars);\n            } else {\n              text = getHintText(protoName, item.text, pluginVars, item.isKey);\n              if (item.displayText) {\n                hint = {\n                  text: text,\n                  displayText: item.displayText\n                };\n                text = item.displayText;\n              }\n            }\n            curHintMap[text] = getRuleHelp(plugin, item.help);\n            return hint || text;\n          });\n          curHintPos = '';\n          curHintOffset = 0;\n          curHintProto = protoName;\n          value = curHintValue;\n        }\n        if (\n          curHintList &&\n          curHintList.length &&\n          curHintProto === protoName &&\n          value === curHintValue\n        ) {\n          if (commentIndex !== -1) {\n            curLine = curLine.substring(0, commentIndex);\n          }\n          curLine = curLine.substring(start).split(/\\s/, 1)[0] || '';\n          curFocusProto = protoName;\n          end = start + curLine.trim().length;\n          var from = CodeMirror.Pos(cur.line, start);\n          var to = CodeMirror.Pos(cur.line, end);\n          var hintList = curHintList;\n          var isCursorPos = curHintPos === 'cursor';\n          if (curHintOffset || isCursorPos) {\n            hintList = hintList.map(function (item) {\n              var hint = {\n                from: from,\n                to: to\n              };\n              if (typeof item === 'string') {\n                hint.text = item;\n                hint.displayText = item;\n              } else {\n                hint.text = item.text;\n                hint.displayText = item.displayText;\n              }\n              return hint;\n            });\n            if (isCursorPos) {\n              from = cur;\n            }\n          }\n          if (curHintPos === 'tail') {\n            var temp = from;\n            from = to;\n            to = temp;\n          }\n          if (curHintOffset) {\n            start = Math.max(start, from.ch + curHintOffset);\n            if (start > end) {\n              start = end;\n            }\n            from = CodeMirror.Pos(cur.line, start);\n          }\n          return { list: hintList, from: from, to: to };\n        }\n        waitingRemoteHints = true;\n        hintTimer = setTimeout(function () {\n          var getRemoteHints = getHintCgi(plugin, pluginConf);\n          if (!editor._bindedHintEvents) {\n            editor._bindedHintEvents = true;\n            editor.on('blur', function () {\n              waitingRemoteHints = false;\n            });\n          }\n          var hintOpts = {\n            protocol: protoName,\n            value: value\n          };\n          if (sep) {\n            hintOpts.sep = sep;\n          }\n          getRemoteHints(hintOpts,\n            function (data) {\n              handleRemoteHints(\n                data,\n                editor,\n                plugin,\n                protoName,\n                value,\n                getRemoteHints,\n                pluginVars,\n                curWord\n              );\n            }\n          );\n        }, HINT_TIMEOUT);\n      }\n    }\n    var isSpecHint = SPEC_HINT_RE.test(curWord);\n    if (value || (isSpecHint ? (byEnter && hasShownHint) : curWord.indexOf('//') !== -1) || !NON_SPECAIL_RE.test(curWord)) {\n      return;\n    }\n  } else if (byDelete) {\n    return;\n  }\n  var filterName;\n  if (isSpecHint) {\n    var slashIdx = curWord.indexOf('://') + 3;\n    value = curWord.substring(slashIdx);\n    filterName = curWord.substring(0, slashIdx);\n  } else {\n    value = '';\n  }\n  list = getHints(filterName || curWord);\n  var hintOpts = getSpecHintOptions(list);\n  var len = list.length;\n  var isFilter = (len === 2 && (isFilterProtocol(list[0]) || isFilterProtocol(list[1]))) || (len === 1 && isFilterProtocol(list[0]));\n  isSpecHint = hintOpts || isFilter;\n  if (isSpecHint) {\n    list = hintOpts ? getSpecHints(value, hintOpts.protocol, hintOpts.hints) : getFilterHints(value, list[0], list[1]);\n    len = list.length;\n  }\n  if (!len) {\n    return;\n  }\n  canShowHint = isSpecHint;\n  var last = end;\n  var nextCh = curLine[last];\n  while (nextCh && WORD.test(nextCh)) {\n    nextCh = curLine[++last];\n  }\n  var curItem = curLine.substring(start, last);\n  if (isSpecHint) {\n    end = last;\n    if (len === 1 && isExactMatch(curItem, list)) {\n      return;\n    }\n  } else {\n    var index = curItem.indexOf(':');\n    if (index !== -1 && !HTTP_RE.test(curItem.substring(end))) {\n      end = start + index + 1;\n      if (curLine[end] === '/') {\n        end++;\n        if (curLine[end] === '/') {\n          end++;\n        }\n      }\n    }\n  }\n  return {\n    list: list,\n    from: CodeMirror.Pos(cur.line, start),\n    to: CodeMirror.Pos(cur.line, end)\n  };\n});\n\nCodeMirror.commands.autocomplete = function (cm) {\n  cm.showHint({\n    hint: CodeMirror.hint.rulesHint,\n    completeSingle: false\n  });\n};\n\nfunction completeAfter(cm, pred) {\n  if (!pred || pred())\n    setTimeout(function () {\n      if (!cm.state.completionActive) {\n        cm.showHint({\n          hint: CodeMirror.hint.rulesHint,\n          completeSingle: false\n        });\n      }\n    }, 100);\n  return CodeMirror.Pass;\n}\n\nCHARS.forEach(function (ch) {\n  extraKeys[ch] = completeAfter;\n});\nvar curValue;\nfunction getFocusRuleName(editor) {\n  curValue = null;\n  var name;\n  var activeHint = $('li.CodeMirror-hint-active');\n  if (activeHint.is(':visible')) {\n    name = activeHint.text();\n    if (showAtHint) {\n      name = '@' + name;\n    } else if (showVarHint) {\n      name = '%' + name;\n    } else {\n      var index = name.indexOf(':');\n      curValue = name;\n      if (index !== -1) {\n        name = name.substring(0, index);\n      }\n    }\n  } else {\n    var cur = editor.getCursor();\n    var end = cur.ch;\n    var curLine = editor.getLine(cur.line).replace(/(#.*|\\s+)$/, '');\n    var len = curLine.length;\n    if (end <= len) {\n      var start = end;\n      while (--start >= 0) {\n        if (/\\s/.test(curLine[start])) {\n          break;\n        }\n      }\n      ++start;\n      while (++end <= len) {\n        if (/\\s/.test(curLine[end])) {\n          break;\n        }\n      }\n      curLine = curLine.slice(start, end);\n      if (AT_RE.test(curLine) || P_RE.test(curLine)) {\n        name = curLine;\n      } else if (PROTOCOL_RE.test(curLine)) {\n        name = RegExp.$1;\n      }\n    }\n  }\n  return name;\n}\n\nexports.getExtraKeys = function () {\n  return extraKeys;\n};\n\nexports.getHelpUrl = function (editor, options) {\n  var name = getFocusRuleName(editor);\n  var url;\n  if (AT_RE.test(name) && (url = getAtHelpUrl(name.substring(1), options))) {\n    return url;\n  }\n  if (url === false) {\n    return false;\n  }\n  if (\n    curValue &&\n    (name === curHintProto || curFocusProto === curHintProto) &&\n    (url = curHintMap[curValue])\n  ) {\n    return url;\n  }\n  if (P_VAR_RE.test(name)) {\n    name = name.substring(1, RegExp.$1.length + 1);\n    var plugin = name && protocols.getPlugin(name);\n    plugin = plugin && plugin.homepage;\n    return (\n      plugin || util.getDocUrl('extensions/usage.html')\n    );\n  }\n  return protocols.getHelpUrl(name);\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/rules-mode.js",
    "content": "var CodeMirror = require('codemirror');\nvar events = require('./events');\nvar protocols = require('./protocols');\nvar forwardRules = protocols.getForwardRules();\nvar pluginRules = protocols.getPluginRules();\nvar DOT_PATTERN_RE = /^\\.[\\w-]+(?:[?$]|$)/;\nvar DOT_DOMAIN_RE = /^\\.[^./?]+\\.[^/?]/;\nvar IPV4_PORT_RE =\n  /^(?:::(?:ffff:)?)?(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(?:\\:(\\d+))?$/;\nvar FULL_IPV6_RE = /^[\\da-f]{1,4}(?::[\\da-f]{1,4}){7}$/;\nvar SHORT_IPV6_RE = /^[\\da-f]{1,4}(?::[\\da-f]{1,4}){0,6}$/;\nvar IP_WITH_PORT_RE = /^\\[([:\\da-f.]+)\\](?::(\\d+))?$/i;\nvar PLUGIN_VAR_RE = /^%[a-z\\d_\\-]+[=.]/;\n\nevents.on('updatePlugins', function () {\n  forwardRules = protocols.getForwardRules();\n  pluginRules = protocols.getPluginRules();\n});\n\nfunction notPort(port) {\n  return port && (port == 0 || port > 65535);\n}\n\nCodeMirror.defineMode('rules', function () {\n  function isIP(str) {\n    var port;\n    if (IP_WITH_PORT_RE.test(str)) {\n      str = RegExp.$1;\n      port = RegExp.$2;\n      if (notPort(port)) {\n        return false;\n      }\n    }\n    if (IPV4_PORT_RE.test(str)) {\n      return !port && notPort(RegExp.$1) ? false : true;\n    }\n    var index = str.indexOf('::');\n    if (index !== -1) {\n      if (str === '::' || str.indexOf('::', index + 1) !== -1) {\n        return false;\n      }\n      str = str.split('::', 2);\n      str = str[0] && str[1] ? str.join(':') : str[0] || str[1];\n      return SHORT_IPV6_RE.test(str);\n    }\n    return FULL_IPV6_RE.test(str);\n  }\n  function isHost(str) {\n    return /^x?hosts?:\\/\\//.test(str);\n  }\n  function isHead(str) {\n    return /^head:\\/\\//.test(str);\n  }\n\n  function isWeinre(str) {\n    return /^weinre:\\/\\//.test(str);\n  }\n\n  function isReq(str) {\n    return /^(?:referer|auth|ua|forwardedFor|reqCookies|reqDelay|reqSpeed|reqCors|reqHeaders|method|reqType|reqCharset|reqBody|reqPrepend|reqAppend|reqReplace|reqWrite|reqWriteRaw):\\/\\//.test(\n      str\n    );\n  }\n\n  function isRes(str) {\n    return /^(?:resScript|frameScript|resRules|responseFor|resCookies|resHeaders|trailers|replaceStatus|resDelay|resSpeed|resCors|resType|resCharset|cache|attachment|download|resBody|resPrepend|resAppend|css(?:Append|Prepend|Body)?|html(?:Append|Prepend|Body)?|js(?:Append|Prepend|Body)?|resReplace|resMerge|resWrite|resWriteRaw):\\/\\//.test(\n      str\n    );\n  }\n\n  function isUrl(str) {\n    return /^(?:https?|wss?|tunnel):\\/\\//i.test(str);\n  }\n\n  function isRule(str) {\n    return /^[\\w\\.-]+:\\/\\//i.test(str);\n  }\n\n  function notExistRule(str) {\n    str = str.substring(0, str.indexOf(':'));\n    return forwardRules.indexOf(str) == -1 && str !== 'status';\n  }\n\n  function notExistPlugin(str) {\n    str = str.substring(0, str.indexOf(':'));\n    return pluginRules.indexOf(str) == -1;\n  }\n\n  function isRegExp(str) {\n    return /^\\/[^/](.*)\\/i?$/.test(str) || /^\\$/.test(str);\n  }\n\n  function isParams(str) {\n    return /^(?:urlParams|params|reqMerge|urlReplace|pathReplace):\\/\\//.test(\n      str\n    );\n  }\n\n  function isLog(str) {\n    return /^log:\\/\\//.test(str);\n  }\n\n  function isStyle(str) {\n    return /^style:\\/\\//.test(str);\n  }\n\n  function isFilter(str) {\n    return /^(?:excludeFilter|filter):\\/\\//.test(str);\n  }\n\n  function isLineProps(str) {\n    return /^lineProps:\\/\\//.test(str);\n  }\n\n  function isPlugin(str) {\n    return (\n      /^(?:pipe|sniCallback):\\/\\//.test(str) ||\n      (/^(?:plugin|whistle)\\.[a-z\\d_\\-]+:\\/\\//.test(str) &&\n        !notExistPlugin(str))\n    );\n  }\n\n  function isRulesFile(str) {\n    return /^(?:rules?(?:File|Script)|reqScript|reqRules):\\/\\//.test(str);\n  }\n\n  function isDisable(str) {\n    return /^disable:\\/\\//.test(str);\n  }\n\n  function isCipher(str) {\n    return /^(?:cipher|tlsOptions):\\/\\//.test(str);\n  }\n\n  function isIgnore(str) {\n    return /^(?:ignore|skip):\\/\\//.test(str);\n  }\n\n  function isEnable(str) {\n    return /^(?:includeFilter|enable):\\/\\//.test(str);\n  }\n\n  function isDelete(str) {\n    return /^delete:\\/\\//.test(str);\n  }\n\n  function isHeaderReplace(str) {\n    return /^headerReplace:\\/\\//.test(str);\n  }\n\n  function isProxy(str) {\n    return /^x?(?:proxy|https?-proxy|http2https-proxy|https2http-proxy|internal-proxy|internal-https?-proxy):\\/\\//.test(\n      str\n    );\n  }\n\n  function isSocks(str) {\n    return /^x?socks:\\/\\//.test(str);\n  }\n\n  function isPac(str) {\n    return /^pac:\\/\\//.test(str);\n  }\n\n  function isLocalPath(str) {\n    return /^[a-z]:(?:\\\\|\\/(?!\\/))/i.test(str) || /^\\/[^/]/.test(str);\n  }\n\n  function isPortPattern(str) {\n    return /^:\\d{1,5}$/.test(str);\n  }\n\n  function isWildcard(str) {\n    if (!/^(?:\\$?(?:https?:|wss?:|tunnel:)?\\/\\/)?([^/?]+)/.test(str)) {\n      return false;\n    }\n    var domain = RegExp.$1;\n    return (\n      domain.indexOf('*') !== -1 ||\n      domain.indexOf('~') !== -1 ||\n      DOT_DOMAIN_RE.test(domain)\n    );\n  }\n\n  function isPluginVar(str) {\n    return PLUGIN_VAR_RE.test(str);\n  }\n\n  function isRegUrl(url) {\n    return /^\\^/.test(url) || DOT_PATTERN_RE.test(url);\n  }\n\n  return {\n    token: function (stream, state) {\n      if (stream.eatSpace()) {\n        return null;\n      }\n\n      var ch = stream.next();\n      if (ch == '#') {\n        stream.eatWhile(function (ch) {\n          return true;\n        });\n        return 'comment';\n      }\n\n      var not = ch === '!';\n      var str = not ? stream.next() : ch;\n      var type = '';\n      var pre, isHttpUrl;\n      stream.eatWhile(function (ch) {\n        if (/\\s/.test(ch) || ch == '#') {\n          return false;\n        }\n        if (str === 'line' && ch === '`') {\n          type = 'keyword js-keyword';\n          return false;\n        }\n        str += ch;\n        if (!type && ch == '/' && pre == '/') {\n          if (isHost(str)) {\n            type = 'number js-number js-type';\n          } else if (isHead(str)) {\n            type = 'header js-head js-type';\n          } else if (isWeinre(str)) {\n            type = 'atom js-weinre js-type';\n          } else if (isProxy(str)) {\n            type = 'tag js-proxy js-type';\n          } else if (isReq(str)) {\n            type = 'variable-2 js-req js-type';\n          } else if (isRes(str)) {\n            type = 'positive js-res js-type';\n          } else if (isParams(str)) {\n            type = 'meta js-params js-type';\n          } else if (isLog(str)) {\n            type = 'atom js-log js-type';\n          } else if (isStyle(str)) {\n            type = 'atom js-style js-type';\n          } else if (isPlugin(str)) {\n            type = 'variable-2 js-plugin js-type';\n          } else if (isHeaderReplace(str)) {\n            type = 'variable-2 js-headerReplace js-type';\n          } else if (isFilter(str)) {\n            type = 'negative js-filter js-type';\n          } else if (isLineProps(str)) {\n            type = 'negative js-line-props js-type';\n          } else if (isIgnore(str)) {\n            type = 'negative js-ignore js-type';\n          } else if (isEnable(str)) {\n            type = 'atom js-enable js-type';\n          } else if (isDisable(str)) {\n            type = 'negative js-disable js-type';\n          } else if (isCipher(str)) {\n            type = 'atom js-cipher js-tls-options js-type';\n          } else if (isDelete(str)) {\n            type = 'negative js-delete js-type';\n          } else if (isProxy(str)) {\n            type = 'variable-2 js-proxy js-type';\n          } else if (isSocks(str)) {\n            type = 'variable-2 js-socks js-type';\n          } else if (isPac(str)) {\n            type = 'variable-2 js-pac js-type';\n          } else if (isRulesFile(str)) {\n            type = 'variable-2 js-rulesFile js-type';\n          } else if (isUrl(str)) {\n            isHttpUrl = true;\n            type =\n              'string-2 js-url js-type' +\n              (str[0] === 'h' ? ' js-http-url' : '');\n          } else if (isWildcard(str)) {\n            type = 'attribute js-attribute';\n          } else if (isRule(str)) {\n            type =\n              'builtin js-rule js-type' +\n              (notExistRule(str) ? ' error-rule' : '');\n          }\n        }\n        pre = ch;\n        return true;\n      });\n      if (!str) {\n        return;\n      }\n      if (!type) {\n        if (isRegExp(str) || isRegUrl(str) || isPortPattern(str)) {\n          return 'attribute js-attribute';\n        }\n        if (/^@/.test(str)) {\n          type = 'atom js-at js-type';\n        } else if (isPluginVar(str)) {\n          type = 'variable-2 js-plugin-var js-type';\n        } else if (isWildcard(str)) {\n          type = 'attribute js-attribute';\n        } else if (isIP(str)) {\n          type = 'number js-number';\n        } else if (\n          /^\\{.*\\}$/.test(str) ||\n          /^<.*>$/.test(str) ||\n          /^\\(.*\\)$/.test(str)\n        ) {\n          type = 'builtin js-rule js-type';\n        } else if (isLocalPath(str)) {\n          type = 'builtin js-rule js-type';\n        }\n      } else if (isHttpUrl && isWildcard(str)) {\n        return 'attribute js-attribute';\n      }\n      return not ? type + ' error-rule' : type || 'js-http-url';\n    }\n  };\n});\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/saved.js",
    "content": "var React = require('react');\nvar OrderTable = require('./order-table');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar events = require('./events');\nvar message = require('./message');\nvar win = require('./win');\n\nvar COLS = [\n  { name: 'date', title: 'Date', width: 200 },\n  { name: 'displayName', title: 'Filename (Count)' },\n  { name: 'operation', title: 'Operation', width: 170 }\n];\n\nvar descSorter = function(a, b) {\n  var flag = b.time - a.time;\n  if (flag === 0) {\n    return 0;\n  }\n  return flag > 0 ? 1 : -1;\n};\n\nvar Saved = React.createClass({\n  getInitialState: function () {\n    return {\n      rows: [],\n      loading: true\n    };\n  },\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    if (hide != util.getBool(nextProps.hide)) {\n      !hide && this.loadDebounce();\n      return true;\n    }\n    return !hide;\n  },\n  componentDidMount: function () {\n    this.loadData();\n    events.on('savedSessionsChanged', this.loadData);\n  },\n  onImport: function(e) {\n    var self = this;\n    var index = e.target.parentNode.getAttribute('data-index');\n    var item = self.state.rows[index];\n    self.loadSessions(item, function(sessions) {\n      dataCenter.addNetworkList(sessions);\n      message.success('Sessions imported successfully');\n    });\n  },\n  onExport: function(e) {\n    var index = e.target.parentNode.getAttribute('data-index');\n    var item = this.state.rows[index];\n    item && this.loadSessions(item, function(sessions) {\n      events.trigger('exportSessions', [sessions, item.filename]);\n    });\n  },\n  onRemove: function(e) {\n    var self = this;\n    var target = e.target;\n    var index = target.parentNode.getAttribute('data-index');\n    var rows = self.state.rows;\n    var item = rows[index];\n    item && win.confirm('Do you confirm the deletion of the saved sessions' +\n      (item.filename ? ' \\'' + item.filename + '\\'' : '') + '?', function(sure) {\n      if (!sure) {\n        return;\n      }\n      dataCenter.removeSavedSessions(JSON.stringify({\n        filename: item.filename,\n        time: item.time,\n        count: item.count\n      }), function (data, xhr) {\n        if (!data) {\n          return util.showSystemError(xhr);\n        }\n        if (data.ec) {\n          return message.error(data.em || 'Error occurred when removing saved sessions');\n        }\n        message.success('Saved sessions removed successfully');\n        index = rows.indexOf(item);\n        if (index !== -1) {\n          rows.splice(index, 1);\n        }\n        self.setState({ rows: rows });\n        self.loadDebounce();\n      });\n    });\n  },\n  loadSessions: function(item, callback) {\n    item && dataCenter.getSavedSessions({\n      filename: item.filename,\n      time: item.time,\n      count: item.count\n    }, function (data, xhr) {\n      if (!data) {\n        return util.showSystemError(xhr);\n      }\n      if (!Array.isArray(data)) {\n        return message.error('Error occurred when loading saved sessions');\n      }\n      callback(data);\n    });\n  },\n  loadDebounce: function() {\n    var self = this;\n    clearTimeout(this.loadTimer);\n    this.loadTimer = setTimeout(function() {\n      self.loadData(true);\n    }, 600);\n  },\n  loadData: function(debounce) {\n    var self = this;\n    dataCenter.getSavedListSafe(function(list) {\n      self.setState({ rows: list.sort(descSorter).map(function(item, i) {\n        var filename = item.filename || '';\n        item.date = util.toLocaleString(new Date(item.time));\n        item.key = filename + '_' + item.count + '_' + item.time;\n        item.displayName = (filename ? filename + ' ' : '') + '(' + item.count + ')';\n        item.operation = (<div className=\"w-order-table-operation\" data-index={i}>\n          <a onClick={self.onImport}>Import</a>\n          <a onClick={self.onExport}>Export</a>\n          <a onClick={self.onRemove} className=\"w-delete\">Delete</a>\n        </div>);\n        return item;\n      }), loading: false }, debounce === true ? null : function() {\n        self.refs.orderTable.scrollToTop();\n      });\n    });\n  },\n  render: function () {\n    var props = this.props;\n    return <OrderTable ref=\"orderTable\" cols={COLS} rows={this.state.rows} hide={props.hide} loading={this.state.loading} />;\n  }\n});\n\nmodule.exports = Saved;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/server-log.js",
    "content": "var $ = require('jquery');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar ExpandCollapse = require('./expand-collapse');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar FilterInput = require('./filter-input');\nvar RecordBtn = require('./record-btn');\nvar events = require('./events');\nvar DropDown = require('./dropdown');\nvar win = require('./win');\n\nvar MAX_COUNT = dataCenter.MAX_LOG_LENGTH;\nvar MAX_FILE_SIZE = 1024 * 1024 * 2;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar ServerLog = React.createClass({\n  getInitialState: function () {\n    return {\n      scrollToBottom: true,\n      levels: [\n        {\n          value: '',\n          text: 'All Levels'\n        },\n        {\n          value: 'debug',\n          text: 'Debug'\n        },\n        {\n          value: 'info',\n          text: 'Info/Log'\n        },\n        {\n          value: 'warn',\n          text: 'Warn'\n        },\n        {\n          value: 'error',\n          text: 'Error'\n        },\n        {\n          value: 'fatal',\n          text: 'Fatal'\n        }\n      ]\n    };\n  },\n  componentDidMount: function () {\n    var self = this;\n    var svrContainer = (this.container = findDOMNode(\n      self.refs.svrContainer\n    ));\n    var svrContent = (this.content = findDOMNode(\n      self.refs.svrContent\n    ));\n\n    var updateLogs = function (_, svrLogs) {\n      var state = self.state;\n      var curLogs = state.logs;\n      if (curLogs !== svrLogs && Array.isArray(curLogs)) {\n        svrLogs.push.apply(svrLogs, curLogs);\n      }\n      state.logs = util.filterLogList(svrLogs, self.keyword, true);\n      if (self.props.hide) {\n        return;\n      }\n      var atBottom = util.scrollAtBottom(svrContainer, svrContent);\n      if (atBottom) {\n        var len = svrLogs.length - MAX_COUNT;\n        len > 9 && util.trimLogList(svrLogs, len, self.keyword);\n      }\n      self.setState({});\n    };\n    if (dataCenter.uploadLogs) {\n      updateLogs(null, dataCenter.uploadLogs);\n      dataCenter.uploadLogs = null;\n    }\n    events.on('uploadLogs', function (_, result) {\n      if (self.props.hide) {\n        return;\n      }\n      var logs = result.logs;\n      var curLogs = self.state.logs;\n      if (curLogs) {\n        curLogs.push.apply(curLogs, logs);\n        var overflow = curLogs.length - MAX_COUNT;\n        overflow > 19 && util.trimLogList(curLogs, overflow, self.keyword);\n      } else {\n        curLogs = logs;\n      }\n      updateLogs(null, curLogs);\n    });\n    dataCenter.on('log', updateLogs);\n\n    var svrTimeout;\n    $(svrContainer).on('scroll', function () {\n      var data = self.state.logs;\n      svrTimeout && clearTimeout(svrTimeout);\n      if (\n        data &&\n        (self.state.scrollToBottom = util.scrollAtBottom(\n          svrContainer,\n          svrContent\n        ))\n      ) {\n        svrTimeout = setTimeout(function () {\n          var len = data.length - MAX_COUNT;\n          if (len > 9) {\n            util.trimLogList(data, len, self.keyword);\n            self.setState({});\n          }\n        }, 2000);\n      }\n    });\n\n    events.on('serverImportFile', function (_, file) {\n      self.importFile(file);\n    });\n    events.on('serverImportData', function (_, data) {\n      self.importData(data);\n    });\n  },\n  clearLogs: function () {\n    dataCenter.clearedSvrLogs = true;\n    dataCenter.clearSvgLogList();\n    this.setState({ logs: [] });\n  },\n  stopAutoRefresh: function () {\n    if (util.scrollAtBottom(this.container, this.content)) {\n      this.container.scrollTop = this.container.scrollTop - 10;\n    }\n  },\n  scrollTop: function () {\n    this.container.scrollTop = 0;\n  },\n  autoRefresh: function () {\n    this.container.scrollTop = 10000000;\n  },\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    var toggleHide = hide != util.getBool(nextProps.hide);\n    if (toggleHide || !hide) {\n      if (!toggleHide && !hide) {\n        this.state.scrollToBottom = util.scrollAtBottom(\n          this.container,\n          this.content\n        );\n      }\n      clearTimeout(this.filterTimer);\n      return true;\n    }\n    return false;\n  },\n  componentDidUpdate: function () {\n    if (!this.props.hide && this.state.scrollToBottom) {\n      this.container.scrollTop = 10000000;\n    }\n  },\n  onServerFilterChange: function (keyword) {\n    var self = this;\n    keyword = keyword.trim();\n    var serverKeyword = util.parseKeyword(keyword);\n    var logs = self.state.logs;\n    self.keyword = keyword && serverKeyword;\n    util.filterLogList(logs, serverKeyword);\n    if (!keyword) {\n      var len = logs && logs.length - MAX_COUNT;\n      len > 9 && logs.splice(0, len);\n    }\n    clearTimeout(self.filterTimer);\n    self.filterTimer = setTimeout(function () {\n      self.filterTimer = null;\n      self.setState({});\n    }, 500);\n  },\n  selectFile: function () {\n    events.trigger('showImportDialog', 'server');\n  },\n  changeLevel: function (option) {\n    this.setState({ level: option.value });\n  },\n  importFile: function (file) {\n    if (!file || !/\\.log$/i.test(file.name)) {\n      return win.alert('Only .log files are supported');\n    }\n    if (file.size > MAX_FILE_SIZE) {\n      return win.alert('Maximum file size: 2MB');\n    }\n    util.readFileAsText(file, this.importData);\n  },\n  importData: function (logs) {\n    logs = util.parseLogs(logs);\n    logs && events.trigger('uploadLogs', { logs: logs });\n  },\n  handleAction: function (type) {\n    if (type === 'top') {\n      return this.scrollTop();\n    }\n    if (type === 'bottom') {\n      return this.autoRefresh();\n    }\n    if (type === 'pause') {\n      dataCenter.pauseServerLogRecord();\n      return;\n    }\n    var refresh = type === 'refresh';\n    dataCenter.stopServerLogRecord(!refresh);\n    if (refresh) {\n      return this.autoRefresh();\n    }\n  },\n  export: function () {\n    var logs = [];\n    this.state.logs.forEach(function (log) {\n      if (!log.hide) {\n        logs.push({\n          id: log.id,\n          text: log.text,\n          level: log.level,\n          date: log.date\n        });\n      }\n    });\n    events.trigger('showExportDialog', ['server', logs]);\n  },\n  render: function () {\n    var state = this.state;\n    var logs = state.logs || [];\n    var level = state.level;\n    var disabled = !util.hasVisibleLog(logs);\n    var index = 0;\n\n    return (\n      <div\n        className={\n          'fill v-box w-textarea w-server-log' +\n          (this.props.hide ? ' hide' : '')\n        }\n      >\n        <div className=\"w-log-action-bar\">\n          <DropDown onChange={this.changeLevel} options={state.levels} />\n          <div className=\"w-textarea-bar\">\n            <RecordBtn onClick={this.handleAction} />\n            <a onClick={this.selectFile} draggable=\"false\">\n              Import\n            </a>\n            <a\n              className={disabled ? ' w-disabled' : ''}\n              onClick={disabled ? undefined : this.export}\n              draggable=\"false\"\n            >\n              Export\n            </a>\n            <a\n              className={'w-clear' + (disabled ? ' w-disabled' : '')}\n              onClick={disabled ? undefined : this.clearLogs}\n              draggable=\"false\"\n            >\n              Clear\n            </a>\n          </div>\n        </div>\n        <div ref=\"svrContainer\" className=\"fill w-log-ctn\">\n          <ul ref=\"svrContent\">\n            {logs.map(function (log) {\n              var upper = log.level.toUpperCase();\n              var text =\n                'Date: ' +\n                util.toLocaleString(new Date(log.date)) + ' (Level: ' + upper + ')' +\n                '\\r\\n' +\n                log.text;\n              var hide =\n                log.hide || (level && !hide && log.level !== level)\n                  ? ' hide'\n                  : '';\n              if (!hide) {\n                ++index;\n              }\n              return (\n                <li\n                  key={log.id}\n                  title={upper}\n                  className={'w-' + log.level + hide}\n                >\n                  <pre>\n                    <strong>#{index}</strong>\n                    {text && text.length >= 2100 ? (\n                      <ExpandCollapse text={text} />\n                    ) : (\n                      text\n                    )}\n                  </pre>\n                </li>\n              );\n            })}\n          </ul>\n        </div>\n        <FilterInput onChange={this.onServerFilterChange} />\n      </div>\n    );\n  }\n});\n\nmodule.exports = ServerLog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/service-btn.js",
    "content": "var React = require('react');\nvar dataCenter = require('./data-center');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar ServiceBtn = React.createClass({\n  showService: function () {\n    util.showService();\n  },\n  render: function () {\n    if (!dataCenter.whistleId) {\n      return null;\n    }\n    var hasToken = dataCenter.hasWhistleToken;\n    return (\n      <a\n        onClick={this.showService}\n        className=\"w-plugins-menu w-service-btn\"\n        draggable=\"false\"\n        title={hasToken ? 'Whistle Service' : 'Whistle Service (not logged in)'}\n      >\n        <Icon name=\"cloud\" className={hasToken ? '' : 'w-disabled'} />\n        Service\n      </a>\n    );\n  }\n});\n\nmodule.exports = ServiceBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/service-dialog.js",
    "content": "require('../css/service.css');\nvar React = require('react');\nvar $ = require('jquery');\nvar events = require('./events');\nvar LargeDialog = require('./large-dialog');\nvar dataCenter = require('./data-center');\nvar getServiceBridge = require('./bridge').getServiceBridge;\nvar util = require('./util');\n\nvar LOGIN_PATH_RE = /^\\/?login(?:\\?|$)/;\nvar bridgeApi;\nvar serviceApi;\n\nvar ServiceDialog = React.createClass({\n  componentDidMount: function () {\n    var self = this;\n    events.on('showService', self.showService);\n    events.on('hideService', self.hideService);\n    events.on('whistleIdChanged', function () {\n      var onWhistleIdChange = self.getServiceFunc('onWhistleIdChange');\n      onWhistleIdChange && onWhistleIdChange(dataCenter.whistleId);\n    });\n    events.on('hasWhistleTokenChanged', function () {\n      var onHasWhistleTokenChange = self.getServiceFunc('onHasWhistleTokenChange');\n      if (onHasWhistleTokenChange) {\n        var hasToken = dataCenter.hasWhistleToken;\n        var dialogElem = $('.w-service-dialog');\n        if (hasToken && dialogElem.hasClass('w-login-dialog')) {\n          self.showService();\n        }\n        onHasWhistleTokenChange(hasToken);\n      }\n      self.setState({});\n    });\n  },\n  getServiceFunc: function (fn) {\n    serviceApi = serviceApi || util.getServiceApi(this.refs.serviceDialog.getWindow(), bridgeApi);\n    if (!fn || !serviceApi) {\n      return serviceApi;\n    }\n    fn = serviceApi[fn];\n    return typeof fn === 'function' ? fn.bind(serviceApi) : null;\n  },\n  showService: function (_, path) {\n    var self = this;\n    var dialog = self.refs.serviceDialog;\n    var dialogElem = $('.w-service-dialog');\n    if (LOGIN_PATH_RE.test(path)) {\n      var showLoginDialog = self.getServiceFunc('showLoginDialog');\n      if (showLoginDialog) {\n        showLoginDialog();\n        dialogElem.removeClass('w-login-dialog');\n        return dialog.show();\n      }\n      dialogElem.addClass('w-login-dialog');\n    } else {\n      dialogElem.removeClass('w-login-dialog');\n    }\n    bridgeApi = bridgeApi || getServiceBridge(self.hideService);\n    dialog.show(util.getServiceUrl(dialog.getWindow(), path, bridgeApi));\n  },\n  hideService: function () {\n    this.refs.serviceDialog.hide();\n  },\n  render: function () {\n    var className = 'w-service-dialog' + (dataCenter.hasWhistleToken ? '' : ' w-login-dialog');\n    return <LargeDialog className={className} ref=\"serviceDialog\" hideButton=\"1\" />;\n  }\n});\n\nmodule.exports = ServiceDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/share-via-url-btn.js",
    "content": "var React = require('react');\nvar dataCenter = require('./data-center');\nvar message = require('./message');\nvar util = require('./util');\nvar Icon = require('./icon');\n\nvar ShareBtn = React.createClass({\n  save: function() {\n    var props = this.props;\n    var getData = props.getData;\n    if (typeof getData === 'function') {\n      return getData(this.handleData);\n    }\n    var data = props.data;\n    if (typeof data === 'function') {\n      data = data();\n    }\n    this.handleData(data);\n  },\n  handleData: function(data) {\n    var props = this.props;\n    var onComplete = props.onComplete;\n    var type = props.type + 'Share';\n    dataCenter.saveToService({\n      type: type,\n      filename: typeof props.getFilename === 'function' ? props.getFilename() : props.filename,\n      data: data,\n      isShare: true\n    }, function(data) {\n      var hasError = !data || data.ec !== 0;\n      if (typeof onComplete === 'function') {\n        onComplete(hasError, data);\n      }\n      if (hasError) {\n        message.error((data && data.em) || 'Sharing failed');\n      } else {\n        message.success('Shared successfully');\n        util.showService(type, true);\n      }\n    });\n  },\n  render: function () {\n    if (!dataCenter.whistleId) {\n      return null;\n    }\n    return (\n      <button\n        onClick={this.save}\n        className=\"btn btn-warning w-save-to-service-btn\"\n        draggable=\"false\"\n        disabled={this.props.disabled}\n      >\n        <Icon name=\"cloud\" />\n        Share Via URL\n      </button>\n    );\n  }\n});\n\nmodule.exports = ShareBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/shortcuts-settings.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar CloseBtn = require('./close-btn');\n\n\nvar CMD = 'Ctrl[Command]';\nvar SETTINGS = [\n  {\n    'category': 'Network',\n    'list': [\n      [\n        'importNetwork',\n        CMD + ' + I',\n        'Import network sessions'\n      ],\n      [\n        'exportNetwork',\n        CMD + ' + E',\n        'Export network sessions'\n      ],\n      [\n        'saveNetwork',\n        CMD + ' + S',\n        'Save network sessions'\n      ],\n      [\n        'toggleNetworkState',\n        CMD + ' + O',\n        'Turn captured requests ON or OFF'\n      ],\n      [\n        'toggleNetworkPanelLayout',\n        CMD + ' + L',\n        'Toggle Network Panel layout: Left-right or top-bottom'\n      ],\n      [\n        'openNetworkSettings',\n        CMD + ' + .',\n        'Open network settings'\n      ],\n      [\n        'removeNetworkSessions',\n        CMD + ' + D',\n        'Remove selected network sessions'\n      ],\n      [\n        'switchNetworkView',\n        CMD + ' + B',\n        'Switch between tree and list view of network sessions'\n      ],\n      [\n        'replaySelectedRequests',\n        CMD + ' + Enter',\n        'Replay selected requests'\n      ],\n      [\n        'replaySelectedRequestsTimes',\n        CMD + ' + Shift + Enter',\n        'Set the number of times to replay the selected requests'\n      ],\n      [\n        'abortRequest',\n        CMD + ' + A',\n        'Abort requests'\n      ],\n      [\n        'clearNetworkSessions',\n        CMD + ' + X',\n        'Clear network sessions'\n      ],\n      [\n        'focusNetworkSearchBox',\n        '/',\n        'Focus the network search box at the bottom'\n      ]\n    ]\n  },\n  {\n    'category': 'Frames',\n    'list': [\n      [\n        'replaySelectedFrame',\n        CMD + ' + Enter',\n        'Replay selected frames'\n      ],\n      [\n        'clearNetworkFrames',\n        CMD + ' + X',\n        'Clear frames'\n      ]\n    ]\n  },\n  {\n    'category': 'Rules',\n    'list': [\n      [\n        'importRules',\n        CMD + ' + I',\n        'Import rules'\n      ],\n      [\n        'exportRules',\n        CMD + ' + E',\n        'Export rules'\n      ],\n      [\n        'saveRulesChanges',\n        CMD + ' + S',\n        'Save rules changes'\n      ],\n      [\n        'toggleRules',\n        CMD + ' + O',\n        'Turn rules ON or OFF'\n      ],\n      [\n        'toggleRulesNum',\n        CMD + ' + L',\n        'Toggle line numbers'\n      ],\n      [\n        'openRulesSettings',\n        CMD + ' + .',\n        'Open rules settings'\n      ],\n      [\n        'focusRulesSearchBox',\n        '/',\n        'Focus the rules search box at the bottom'\n      ]\n    ]\n  },\n  {\n    'category': 'Values',\n    'list': [\n      [\n        'importValues',\n        CMD + ' + I',\n        'Import values'\n      ],\n      [\n        'exportValues',\n        CMD + ' + E',\n        'Export values'\n      ],\n      [\n        'saveValuesChanges',\n        CMD + ' + S',\n        'Save values changes'\n      ],\n      [\n        'toggleValuesNum',\n        CMD + ' + L',\n        'Toggle line numbers'\n      ],\n      [\n        'openValuesSettings',\n        CMD + ' + .',\n        'Open values settings'\n      ],\n      [\n        'focusValuesSearchBox',\n        '/',\n        'Focus the values search box at the bottom'\n      ]\n    ]\n  },\n  {\n    'category': 'Plugins',\n    'list': [\n      [\n        'openInstallPlugins',\n        CMD + ' + I',\n        'Open the plugin installation dialog box'\n      ],\n      [\n        'togglePlugins',\n        CMD + ' + O',\n        'Turn all plugins ON or OFF'\n      ]\n    ]\n  },\n  {\n    'category': 'Others',\n    'list': [\n      [\n        'switchTabReverse',\n        CMD + ' + <--',\n        'Toggle Network, Rules, Values, and Plugins in reverse order'\n      ],\n      [\n        'switchTab',\n        CMD + ' + -->',\n        'Toggle Network, Rules, Values, and Plugins'\n      ],\n      [\n        'openService',\n        CMD + ' + J',\n        'Open service dialog'\n      ]\n    ]\n  }\n];\n\nvar ShortcutsSettings = React.createClass({\n  getInitialState: function() {\n    return { settings: util.shortcutsSettings };\n  },\n  show: function() {\n    this.refs.dialog.show();\n  },\n  hide: function() {\n    this.refs.dialog.hide();\n  },\n  onChange: function(e) {\n    var target = e.target;\n    if (target.nodeName !== 'INPUT') {\n      return;\n    }\n    var checked = target.checked;\n    var name = target.getAttribute('data-name');\n    var settings = this.state.settings;\n    settings[name] = checked;\n    this.setState({ settings: settings });\n    util.saveShortcutsSettings();\n  },\n  render: function() {\n    var settings = this.state.settings;\n\n    return (\n      <Dialog ref=\"dialog\" wstyle=\"w-shortcuts\">\n        <div>\n          <div className=\"modal-header\">\n            <h4>\n              Shortcuts Settings\n            </h4>\n            <CloseBtn />\n          </div>\n          <div className=\"modal-body\" onChange={this.onChange}>\n            {SETTINGS.map(function(setting) {\n              return (\n                <div>\n                  <h5 key={setting.category}>{setting.category}</h5>\n                  {setting.list.map(function(item) {\n                    if (!dataCenter.whistleId && item[0] === 'openService') {\n                      return null;\n                    }\n                    return <label key={item[0]}>\n                      <input type=\"checkbox\" data-name={item[0]} checked={settings[item[0]] !== false} /> <strong>{item[1]} :</strong> {item[2]}\n                    </label>;\n                  })}\n                </div>\n              );\n            })}\n          </div>\n      </div>\n      <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n    </Dialog>\n  );\n  }\n});\n\nmodule.exports = ShortcutsSettings;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/storage.js",
    "content": "var PREFIX = location.href\n  .replace(/[?#].*$/, '')\n  .replace(/\\/index.html$/i, '/');\nvar cache = {};\n\nfunction getKey(key) {\n  return PREFIX + '?' + key;\n}\n\nexports.set = function (key, value) {\n  key = getKey(key);\n  if (value == null) {\n    value = '';\n  } else {\n    value += '';\n  }\n  cache[key] = value;\n  try {\n    localStorage[key] = value;\n  } catch (e) {}\n};\n\nexports.get = function (key, noCache) {\n  key = getKey(key);\n  try {\n    return noCache ? localStorage[key] : cache[key] || localStorage[key];\n  } catch (e) {}\n  return cache[key];\n};\n\nexports.remove = function(key) {\n  try {\n    localStorage.removeItem(getKey(key));\n  } catch (e) {}\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/sync-dialog.js",
    "content": "require('../css/sync-dialog.css');\nvar React = require('react');\nvar Dialog = require('./dialog');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar KVDialog = require('./kv-dialog');\nvar Icon = require('./icon');\n\nvar showLoading = function(time) {\n  return time && (Date.now() - time > 800);\n};\n\nvar addHistory = function(url, history) {\n  return history ? url + (url.indexOf('?') === -1 ? '?' : '&') + 'history=' + encodeURIComponent(history) : url;\n};\n\nvar SyncDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  show: function (plugin, rulesModal, valuesModal, cb) {\n    var self = this;\n    self.rulesModal = rulesModal;\n    self.valuesModal = valuesModal;\n    self.plugin = plugin;\n    if (!util.isString(plugin.rulesUrl)) {\n      plugin.rulesUrl = null;\n    }\n    if (!util.isString(plugin.valuesUrl)) {\n      plugin.valuesUrl = null;\n    }\n    if (plugin.rulesUrl || plugin.valuesUrl) {\n      self.setState(plugin, typeof cb === 'function' ? cb : function () {\n        self.refs.syncDialog.show();\n      });\n    }\n  },\n  _syncRules: function(history) {\n    var self = this;\n    var rulesUrl = self.state.rulesUrl;\n    if (self.loadingRules || !util.isString(rulesUrl)) {\n      return;\n    }\n    self.loadingRules = Date.now() || 1;\n    rulesUrl = addHistory(rulesUrl, history);\n    var loadRules = dataCenter.createCgi(\n      util.getPluginCgiUrl(self.state.moduleName, rulesUrl)\n    );\n    loadRules(function (data, xhr) {\n      self.loadingRules = false;\n      self.setState({});\n      if (!data) {\n        return util.showSystemError(xhr);\n      }\n      self.plugin.selectedRulesHistory = history;\n      self.refs.kvDialog.show(data, self.rulesModal, self.valuesModal, false, history);\n    });\n    self.setState({});\n  },\n  _syncValues: function (history) {\n    var self = this;\n    var valuesUrl = self.state.valuesUrl;\n    if (self.loadingValues || !util.isString(valuesUrl)) {\n      return;\n    }\n    self.loadingValues = Date.now() || 1;\n    valuesUrl = addHistory(valuesUrl, history);\n    var loadValues = dataCenter.createCgi(\n      util.getPluginCgiUrl(self.state.moduleName, valuesUrl)\n    );\n    loadValues(function (data, xhr) {\n      self.loadingValues = false;\n      self.setState({});\n      if (!data) {\n        return util.showSystemError(xhr);\n      }\n      self.plugin.selectedValuesHistory = history;\n      self.refs.kvDialog.show(data, self.rulesModal, self.valuesModal, true, history);\n    });\n    self.setState({});\n  },\n  showKVDialog: function (data, rulesModal, valuesModal, isValues) {\n    this.refs.kvDialog.show(data, rulesModal, valuesModal, isValues);\n  },\n  syncRules: function () {\n    this._syncRules(this.plugin.selectedRulesHistory);\n  },\n  syncValues: function () {\n    this._syncValues(this.plugin.selectedValuesHistory);\n  },\n  onHistoryChange: function(history, isValues) {\n    if (isValues) {\n      this._syncValues(history);\n    } else {\n      this._syncRules(history);\n    }\n  },\n  render: function () {\n    var state = this.state;\n    var loadingRules = this.loadingRules;\n    var loadingValues = this.loadingValues;\n    return (\n      <Dialog ref=\"syncDialog\" wstyle=\"w-sync-dialog\">\n        <div className=\"modal-body\">\n          <button\n            onClick={this.syncRules}\n            disabled={loadingRules || !util.isString(state.rulesUrl)}\n            type=\"button\"\n            className=\"btn btn-primary\"\n          >\n            <Icon name=\"list\" />{' '}\n            {showLoading(loadingRules) ? 'Loading' : 'Sync'} Rules\n          </button>\n          <button\n            onClick={this.syncValues}\n            disabled={loadingValues || !util.isString(state.valuesUrl)}\n            type=\"button\"\n            className=\"btn btn-default\"\n          >\n            <Icon name=\"folder-close\" />{' '}\n            {showLoading(loadingValues) ? 'Loading' : 'Sync'} Values\n          </button>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n        </div>\n        <KVDialog onHistoryChange={this.onHistoryChange} ref=\"kvDialog\" />\n      </Dialog>\n    );\n  }\n});\n\nvar SyncDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (plugin, rulesModal, valuesModal, cb) {\n    this.refs.syncDialog.show(plugin, rulesModal, valuesModal, cb);\n  },\n  syncRules: function() {\n    this.refs.syncDialog.syncRules();\n  },\n  syncValues: function() {\n    this.refs.syncDialog.syncValues();\n  },\n  showKVDialog: function (data, rulesModal, valuesModal, isValues) {\n    this.refs.syncDialog.showKVDialog(data, rulesModal, valuesModal, isValues);\n  },\n  render: function () {\n    return <SyncDialog ref=\"syncDialog\" />;\n  }\n});\n\nmodule.exports = SyncDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tab-frame.js",
    "content": "var React = require('react');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar events = require('./events');\nvar getBridge = require('./bridge');\nvar IFrame = require('./iframe');\n\nvar modal = dataCenter.networkModal;\n\nfunction onWhistleInspectorCustomTabReady(init, win) {\n  if (typeof init === 'function') {\n    init(getBridge(win));\n  }\n}\n\nvar TabFrame = React.createClass({\n  getInitialState: function () {\n    var url = this.props.src;\n    url += url.indexOf('?') === -1 ? '?' : '&';\n    return {\n      url:\n        url + '???_WHISTLE_PLUGIN_INSPECTOR_TAB_' + dataCenter.getPort() + '???'\n    };\n  },\n  componentDidMount: function () {\n    events.on('selectedSessionChange', this.handlePush);\n  },\n  componentWillUnmount: function () {\n    events.off('selectedSessionChange', this.handlePush);\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  compose: function (item) {\n    this.handlePush(null, null, item);\n  },\n  handlePush: function (_, item, comItem) {\n    try {\n      var win = this.refs.iframe.getWindow();\n      if (\n        win &&\n        typeof win.__pushWhistle5b6af7b9884e1165SessionActive__ === 'function'\n      ) {\n        if (comItem) {\n          win.__pushWhistle5b6af7b9884e1165SessionActive__(null, null, comItem);\n          comItem = null;\n        } else if (this.props.hide) {\n          win.__pushWhistle5b6af7b9884e1165SessionActive__(null, true);\n        } else {\n          win.__pushWhistle5b6af7b9884e1165SessionActive__(\n            item || modal.getActive()\n          );\n        }\n      }\n    } catch (e) {}\n    this.composeItem = comItem;\n  },\n  componentDidUpdate: function () {\n    this.handlePush();\n  },\n  onLoad: function () {\n    if (this.composeItem) {\n      this.handlePush(null, null, this.composeItem);\n      this.composeItem = null;\n    }\n  },\n  render: function () {\n    var display = this.props.hide ? 'none' : undefined;\n    // 防止被改\n    window.onWhistleInspectorCustomTabReady = onWhistleInspectorCustomTabReady;\n    return (\n      <IFrame\n        onLoad={this.onLoad}\n        ref=\"iframe\"\n        src={this.state.url}\n        style={{ display: display }}\n      />\n    );\n  }\n});\n\nmodule.exports = TabFrame;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tab-mgr.js",
    "content": "var React = require('react');\nvar util = require('./util');\nvar TabFrame = require('./tab-frame');\nvar events = require('./events');\n\nvar MAX_IFRAME_COUNT = 6;\n\nvar TabMgr = React.createClass({\n  getInitialState: function () {\n    this.initedTabs = {};\n    return {};\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  componentDidMount: function () {\n    var self = this;\n    events.on('setComposer', function () {\n      var modal = !self.props.hide && self.props.modal;\n      if (modal) {\n        var elem = self.refs[self.props.active];\n        elem && elem.compose(modal);\n      }\n    });\n  },\n  isInited: function (tab) {\n    var cache = this.initedTabs;\n    var action = tab.action;\n    var exists = cache[action] != null;\n    if (this.props.active !== tab.plugin) {\n      return exists;\n    }\n    if (exists) {\n      cache[action] = Date.now();\n      return true;\n    }\n    var keys = Object.keys(cache);\n    if (keys.length >= MAX_IFRAME_COUNT) {\n      var minTime;\n      var destroyKey;\n      keys.forEach(function (key) {\n        var time = cache[key];\n        if (minTime == null || minTime > time) {\n          minTime = time;\n          destroyKey = key;\n        }\n      });\n      if (destroyKey) {\n        delete cache[destroyKey];\n      }\n    }\n    this.initedTabs[action] = Date.now();\n    return true;\n  },\n  render: function () {\n    var self = this;\n    var props = self.props;\n    var tabs = props.tabs;\n    var hide = props.hide;\n    var active = props.active;\n    var hideAll = true;\n    tabs = tabs.map(function (tab) {\n      var hideTab = hide || active !== tab.plugin;\n      if (!hideTab) {\n        hideAll = false;\n      }\n      return (\n        self.isInited(tab) && (\n          <TabFrame\n            ref={tab.plugin}\n            key={tab.plugin}\n            src={tab.action}\n            hide={hideTab}\n          />\n        )\n      );\n    });\n    return (\n      <div\n        className={\n          'fill v-box ' +\n          (props.className || '') +\n          (hideAll ? ' hide' : '')\n        }\n      >\n        {tabs}\n      </div>\n    );\n  }\n});\n\nmodule.exports = TabMgr;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/table.js",
    "content": "require('../css/table.css');\nvar React = require('react');\n\nvar Table = React.createClass({\n  render: function () {\n    var head = this.props.head;\n    var hasHead = Array.isArray(head) && head.length;\n    var modal = this.props.modal || [];\n\n    return (\n      <table className=\"table w-table\">\n        {hasHead ? (\n          <thead>\n            <tr>\n              {head.map(function (head) {\n                return <th key={head}>{head}</th>;\n              })}\n            </tr>\n          </thead>\n        ) : (\n          ''\n        )}\n        <tbody className=\"w-hover-body\">\n          {modal.map(function (list, i) {\n            return (\n              <tr key={i}>\n                {list.map(function (value, j) {\n                  return <td key={i + '.' + j}>{value}</td>;\n                })}\n              </tr>\n            );\n          })}\n        </tbody>\n      </table>\n    );\n  }\n});\n\nmodule.exports = Table;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tabs.js",
    "content": "var React = require('react');\nvar Icon = require('./icon');\n\nvar Tabs = React.createClass({\n  render: function() {\n    var tabs = this.props.tabs || [];\n    var onChange = this.props.onChange;\n\n    return (\n      <ul className=\"nav nav-tabs w-tabs\">\n        {\n          tabs.map(function(tab, index) {\n            var handleClick = function() {\n              if (!tab.active && onChange) {\n                onChange(tab);\n              }\n            };\n            return (\n              <li key={index} className={tab.active ? 'active' : ''}>\n                <a draggable=\"false\" onClick={handleClick}>\n                  {tab.icon ? <Icon name={tab.icon} /> : null}\n                  {tab.name}\n                </a>\n              </li>\n            );\n          })\n        }\n      </ul>\n    );\n  }\n});\n\nmodule.exports = Tabs;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/teleport.js",
    "content": "var $ = (window.jQuery = require('jquery'));\nvar React = require('react');\nvar ReactDOM = require('react-dom');\n\nvar Portal = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  componentDidMount: function () {\n    var con = $(document.createElement('div'));\n    this.container = con;\n    con.hide();\n    document.body.appendChild(con[0]);\n    this.componentDidUpdate();\n  },\n  componentDidUpdate: function () {\n    ReactDOM.unstable_renderSubtreeIntoContainer(\n      this,\n      this.props.children,\n      this.container[0]\n    );\n    var className = this.props.className;\n    if (this._className !== className) {\n      this._className = className;\n      this.container.removeClass();\n      className && this.container.addClass(className);\n    }\n  },\n  componentWillUnmount: function () {\n    ReactDOM.unmountComponentAtNode(this.container[0]);\n    document.body.removeChild(this.container[0]);\n  },\n  show: function () {\n    if (this.isVisible()) {\n      return;\n    }\n    this.container.show('fast');\n  },\n  isVisible: function () {\n    return this.container.is(':visible');\n  },\n  hide: function () {\n    this.container.hide('fast');\n  },\n  destroy: function () {\n    this.hide();\n    this.container && this.componentWillUnmount();\n  },\n  render: function () {\n    return null;\n  }\n});\n\nmodule.exports = Portal;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/text-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar Textarea = require('./textarea');\nvar CloseBtn = require('./close-btn');\n\nvar TextDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  show: function (value, base64, name) {\n    if (value) {\n      var self = this;\n      self.setState({ value: value, base64: base64, name: name }, function () {\n        self.refs.textDialog.show();\n      });\n    }\n  },\n  render: function () {\n    var state = this.state;\n    var value = state.value;\n    return (\n      <Dialog ref=\"textDialog\" wstyle=\"w-text-dialog\">\n        <div className=\"modal-body\">\n          <CloseBtn />\n          <div\n            className=\"v-box\"\n            style={{ width: 860, height: 560, marginTop: 22 }}\n          >\n            <Textarea\n              className=\"fill\"\n              value={value}\n              base64={state.base64}\n              defaultName={state.name}\n            />\n          </div>\n        </div>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          <button\n            type=\"button\"\n            className=\"btn btn-primary w-copy-text-with-tips\"\n            data-clipboard-text={value}\n          >\n            Copy\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nvar TextDialogWrap = React.createClass({\n  shouldComponentUpdate: function () {\n    return false;\n  },\n  show: function (value, base64, name) {\n    this.refs.textDialog.show(value, base64, name);\n  },\n  render: function () {\n    return <TextDialog ref=\"textDialog\" />;\n  }\n});\n\nmodule.exports = TextDialogWrap;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/textarea.js",
    "content": "require('../css/textarea.css');\nvar React = require('react');\nvar ReactDOM = require('react-dom');\nvar TextView = require('./textview');\nvar CopyBtn = require('./copy-btn');\nvar util = require('./util');\nvar dataCenter = require('./data-center');\nvar message = require('./message');\nvar win = require('./win');\nvar events = require('./events');\nvar Tips = require('./panel-tips');\nvar Icon = require('./icon');\n\nvar MAX_LENGTH = 1024 * 6;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar Textarea = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    var nextHide = util.getBool(nextProps.hide);\n    if (this._isCaptured !== dataCenter.isCapture) {\n      this._isCaptured = dataCenter.isCapture;\n      return true;\n    }\n    if (hide !== nextHide || !this.props.value) {\n      return true;\n    }\n    if (hide) {\n      return false;\n    }\n    return this.props.value !== nextProps.value;\n  },\n  preventBlur: function (e) {\n    e.target.nodeName != 'INPUT' && e.preventDefault();\n  },\n  edit: function () {\n    util.openEditor(this.props.value);\n  },\n  showMockDialog: function(e) {\n    var self = this;\n    var props = self.props;\n    var reqData = props.reqData;\n    if (reqData) {\n      return events.trigger('showMockDialog', {\n        type: props.reqType,\n        item: reqData\n      });\n    }\n    self.showNameInput(e);\n  },\n  showNameInput: function (e) {\n    var self = this;\n    self.state.showDownloadInput = /w-download/.test(e.target.className);\n    self.state.showNameInput = true;\n    self.forceUpdate(function () {\n      var nameInput = findDOMNode(self.refs.nameInput);\n      var defaultName = !nameInput.value && self.props.defaultName;\n      if (defaultName) {\n        nameInput.value = defaultName;\n      }\n      nameInput.select();\n      nameInput.focus();\n    });\n  },\n  download: function () {\n    var target = findDOMNode(this.refs.nameInput);\n    var name = target.value.trim();\n    target.value = '';\n    var base64 = this.props.base64;\n    findDOMNode(this.refs.filename).value = name;\n    findDOMNode(this.refs.type).value = base64 ? 'base64' : '';\n    findDOMNode(this.refs.headers).value = this.props.headers || '';\n    findDOMNode(this.refs.content).value =\n      base64 != null ? base64 : this.props.value || '';\n    findDOMNode(this.refs.downloadForm).submit();\n    this.hideNameInput();\n  },\n  submit: function (e) {\n    if (e.keyCode != 13 && e.type != 'click') {\n      return;\n    }\n    var modal = dataCenter.valuesModal;\n    if (!modal) {\n      return;\n    }\n    var target = findDOMNode(this.refs.nameInput);\n    var name = target.value.trim();\n    var self = this;\n    if (self.state.showDownloadInput) {\n      self.download();\n      return;\n    }\n    if (!name) {\n      message.error('The key is required');\n      return;\n    }\n\n    if (/\\s/.test(name)) {\n      message.error('Spaces are not allowed in the key');\n      return;\n    }\n    var handleSubmit = function (sure) {\n      if (!sure) {\n        return;\n      }\n      var value = (self.props.value || '').replace(/\\r\\n|\\r/g, '\\n');\n      dataCenter.values.add({ name: name, value: value }, function (data, xhr) {\n        if (data && data.ec === 0) {\n          modal.add(name, value);\n          target.value = '';\n          target.blur();\n        } else {\n          util.showSystemError(xhr);\n        }\n      });\n    };\n    if (!modal.exists(name)) {\n      return handleSubmit(true);\n    }\n    win.confirm(\n      'The key \\'' + name + '\\' is already in use. Overwrite?',\n      handleSubmit\n    );\n  },\n  hideNameInput: function () {\n    this.state.showNameInput = false;\n    this.forceUpdate(function () {\n      var nameInput = findDOMNode(this.refs.nameInput);\n      var defaultName = this.props.defaultName;\n      if (defaultName === nameInput.value) {\n        nameInput.value = '';\n      }\n    });\n  },\n  render: function () {\n    var props = this.props;\n    var value = props.value || '';\n    var exceed = value.length - MAX_LENGTH;\n    if (exceed > 512) {\n      value = value.substring(0, MAX_LENGTH) +\n        '...\\r\\n\\r\\n(' +\n        exceed +\n        ' characters remaining (click View All in top-right corner)\\r\\n';\n    }\n    var isHexView = props.isHexView;\n    this.state.value = value;\n    return (\n      <div\n        className={\n          'fill v-box w-textarea' +\n          (props.hide ? ' hide' : '')\n        }\n      >\n        <Tips data={props.tips} />\n        <div className={'w-textarea-bar' + (value ? '' : ' hide')}>\n          {props.reqType === 'reqRaw' ? <a onClick={props.onEdit}>\n            <Icon name=\"send\" />\n            Edit\n          </a> : undefined}\n          <CopyBtn value={props.value} />\n          {isHexView ? (\n            <CopyBtn name=\"AsHex\" value={util.getHexText(props.value)} />\n          ) : undefined}\n          <a\n            onDoubleClick={this.download}\n            onClick={this.showNameInput}\n            draggable=\"false\"\n          >\n            Download\n          </a>\n          <a style={{display: dataCenter.hideMockMenu ? 'none' : null}} className=\"w-add\" onClick={this.showMockDialog} draggable=\"false\">\n            { props.reqData ? 'Mock' : '+Key' }\n          </a>\n          <a onClick={this.edit} draggable=\"false\">\n            ViewAll\n          </a>\n          <div\n            onMouseDown={this.preventBlur}\n            style={{ display: this.state.showNameInput ? 'block' : 'none' }}\n            className=\"w-shadow w-textarea-input\"\n          >\n            <input\n              ref=\"nameInput\"\n              onKeyDown={this.submit}\n              onBlur={this.hideNameInput}\n              type=\"text\"\n              maxLength=\"64\"\n              placeholder={\n                this.state.showDownloadInput\n                  ? 'Enter filename'\n                  : 'Enter key name'\n              }\n            />\n            <button\n              type=\"button\"\n              onClick={this.submit}\n              className=\"btn btn-primary\"\n            >\n              {this.state.showDownloadInput ? 'OK' : '+Key'}\n            </button>\n          </div>\n        </div>\n        <TextView className={props.className || ''} value={value} />\n        <form\n          ref=\"downloadForm\"\n          action=\"cgi-bin/download\"\n          style={{ display: 'none' }}\n          method=\"post\"\n          target=\"downloadTargetFrame\"\n        >\n          <input ref=\"filename\" name=\"filename\" type=\"hidden\" />\n          <input ref=\"type\" name=\"type\" type=\"hidden\" />\n          <input ref=\"headers\" name=\"headers\" type=\"hidden\" />\n          <input ref=\"content\" name=\"content\" type=\"hidden\" />\n        </form>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Textarea;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/textview.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar util = require('./util');\n\nvar TextView = React.createClass({\n  componentDidMount: function () {\n    this.updateValue();\n  },\n  componentDidUpdate: function () {\n    this.updateValue();\n  },\n  shouldComponentUpdate: function (nextProps) {\n    if (this.props.value !== nextProps.value) {\n      this.props.value = nextProps.value;\n      this.updateValue();\n    }\n    return this.props.className !== nextProps.className;\n  },\n  updateValue: function () {\n    var self = this;\n    var value = self.props.value || '';\n    var textarea = ReactDOM.findDOMNode(self.refs.textarea);\n    if (self.props.hide) {\n      textarea.value = '';\n      self.curValue = '';\n      clearTimeout(self._timeout);\n      return;\n    }\n    if (value === self.curValue) {\n      return;\n    }\n    clearTimeout(self._timeout);\n    if (textarea.value === value) {\n      return;\n    }\n    if (value.length < 10240) {\n      textarea.value = value;\n      self.curValue = value;\n      return;\n    }\n    self.curValue = value;\n    self._timeout = setTimeout(function () {\n      textarea.value = value;\n    }, 360);\n  },\n  render: function () {\n    return (\n      <textarea\n        ref=\"textarea\"\n        onKeyDown={util.preventDefault}\n        readOnly=\"readonly\"\n        className={this.props.className || ''}\n      />\n    );\n  }\n});\n\nmodule.exports = TextView;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/timeline.js",
    "content": "require('../css/timeline.css');\nvar React = require('react');\nvar util = require('./util');\nvar TOTAL_RATE = 82;\n\nvar Timeline = React.createClass({\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  render: function () {\n    var modal = this.props.modal;\n    var data = this.props.data;\n    var list = data ? [data] : modal ? modal.getSelectedList() : [];\n    var maxTotal = 1;\n    var startTime;\n\n    list.forEach(function (item) {\n      if (!startTime || item.startTime < startTime) {\n        startTime = item.startTime;\n      }\n    });\n\n    list.forEach(function (item) {\n      var total =\n        (item.endTime ||\n          Math.max(item.responseTime || 0, item.requestTime || 0) ||\n          item.dnsTime) - startTime;\n      if (total > maxTotal) {\n        maxTotal = total;\n      }\n    });\n\n    var len = list.length;\n    return (\n      <div\n        className={\n          'fill v-box w-detail-ctn w-timeline' +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <ul>\n          {list.map(function (item) {\n            var stalled = item.startTime - startTime;\n            var stalledRate,\n              ttfb,\n              ttfbRate,\n              dns,\n              dnsRate,\n              request,\n              requestRate,\n              response,\n              responseRate,\n              load,\n              loadRate;\n            if (item.dnsTime) {\n              stalled = item.startTime - startTime;\n              stalledRate = (stalled * TOTAL_RATE) / maxTotal + '%';\n              stalled += 'ms';\n            } else {\n              stalled = '-';\n              stalledRate = 0;\n            }\n\n            if (item.ttfb >= 0) {\n              ttfb = item.ttfb;\n              ttfbRate = (ttfb * TOTAL_RATE) / maxTotal + '%';\n              ttfb += 'ms';\n            } else {\n              ttfb = '-';\n            }\n\n            if (item.dnsTime) {\n              dns = item.dnsTime - item.startTime;\n              dnsRate = (dns * TOTAL_RATE) / maxTotal + '%';\n              dns += 'ms';\n            } else {\n              dns = '-';\n              dnsRate = 0;\n            }\n\n            var isStream;\n            if (item.responseTime) {\n              isStream =\n                !item.requestTime || item.requestTime > item.responseTime;\n              response =\n                item.responseTime -\n                (isStream ? item.dnsTime : item.requestTime);\n              responseRate = (response * TOTAL_RATE) / maxTotal + '%';\n              response += 'ms';\n            } else {\n              response = '-';\n              responseRate = 0;\n            }\n\n            if (item.requestTime) {\n              request = item.requestTime - item.dnsTime;\n              requestRate = (request * TOTAL_RATE) / maxTotal + '%';\n              request += 'ms';\n              var protocol = item.protocol;\n              if (\n                typeof protocol === 'string' &&\n                protocol.indexOf('>') !== -1\n              ) {\n                var diffTime = item.httpsTime - item.startTime;\n                if (diffTime > 0) {\n                  request +=\n                    ' - ' +\n                    diffTime +\n                    'ms(' +\n                    protocol +\n                    ') = ' +\n                    (item.requestTime - item.httpsTime) +\n                    'ms';\n                }\n              }\n            } else {\n              request = '-';\n              requestRate = 0;\n            }\n\n            if (item.endTime) {\n              load = item.endTime - item.responseTime;\n              loadRate = (load * TOTAL_RATE) / maxTotal + '%';\n              load += 'ms';\n            } else {\n              load = '-';\n              loadRate = 0;\n            }\n\n            var total = item.endTime\n              ? item.endTime - item.startTime + 'ms'\n              : '-';\n\n            if (len === 1) {\n              return (\n                <li\n                  key=\"w-timeline-one\"\n                  className=\"w-timeline-one\"\n                >\n                  <ul>\n                    <li>\n                      <span className=\"w-timeline-url\">URL:</span>\n                      <span\n                        className=\"w-timeline-full-url\"\n                        title={item.url}\n                      >\n                        {item.url}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">TTFB:</span>\n                      <span\n                        style={{ width: ttfbRate }}\n                        className=\"w-timeline-ttfb\"\n                      />\n                      <span title={title} className=\"w-timeline-time\">\n                        {ttfb}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">DNS:</span>\n                      <span\n                        style={{ width: dnsRate }}\n                        className=\"w-timeline-dns\"\n                      />\n                      <span title={title} className=\"w-timeline-time\">\n                        {dns}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">\n                        Request:\n                      </span>\n                      <span style={{ width: dnsRate }} />\n                      <span\n                        style={{ width: requestRate }}\n                        className=\"w-timeline-request\"\n                      >\n                        {' '}\n                      </span>\n                      <span title={title} className=\"w-timeline-time\">\n                        {request}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">\n                        Response:\n                      </span>\n                      <span style={{ width: dnsRate }} />\n                      {isStream ? null : (\n                        <span style={{ width: requestRate }} />\n                      )}\n                      <span\n                        style={{ width: responseRate }}\n                        className=\"w-timeline-response\"\n                      />\n                      <span title={title} className=\"w-timeline-time\">\n                        {response}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">\n                        Download:\n                      </span>\n                      <span style={{ width: dnsRate }} />\n                      {isStream ? null : (\n                        <span style={{ width: requestRate }} />\n                      )}\n                      <span style={{ width: responseRate }} />\n                      <span\n                        style={{ width: loadRate }}\n                        className=\"w-timeline-load\"\n                      />\n                      <span title={title} className=\"w-timeline-time\">\n                        {load}\n                      </span>\n                    </li>\n                    <li>\n                      <span className=\"w-timeline-url\">Total Duration:</span>\n                      <span title={title} className=\"w-timeline-time\">\n                        {total}\n                      </span>\n                    </li>\n                  </ul>\n                </li>\n              );\n            }\n            var title =\n              'URL: ' +\n              item.url +\n              '\\nStalled: ' +\n              stalled +\n              '\\nTFFB: ' +\n              ttfb +\n              '\\nDNS: ' +\n              dns +\n              '\\nRequest: ' +\n              request +\n              '\\nResponse: ' +\n              response +\n              '\\nContent: ' +\n              load +\n              '\\nTotal: ' +\n              total;\n            var reqStream = isStream && requestRate;\n            var resStream = isStream && responseRate;\n\n            return (\n              <li\n                key={item.id}\n                title={title}\n                className={'w-timeline-multi' + (resStream ? ' w-timeline-stream' : '')}\n              >\n                <span title={item.url} className=\"w-timeline-url\">\n                  {util.getFilename(item)}\n                </span>\n                <span\n                  style={{ width: stalledRate }}\n                  className=\"w-timeline-stalled\"\n                />\n                <span\n                  style={{ width: dnsRate }}\n                  className=\"w-timeline-dns\"\n                />\n                <span\n                  style={{\n                    width: requestRate,\n                    marginBottom: resStream ? '5px' : undefined\n                  }}\n                  className=\"w-timeline-request\"\n                />\n                <span\n                  style={{\n                    width: responseRate,\n                    marginLeft:\n                      reqStream ? '-' + requestRate : undefined,\n                    marginBottom: reqStream ? '-5px' : undefined,\n                    height: reqStream ? '15px' : undefined\n                  }}\n                  className=\"w-timeline-response\"\n                />\n                <span\n                  style={{\n                    width: loadRate,\n                    marginLeft:\n                      resStream ? '-' + responseRate : undefined,\n                    marginBottom: reqStream ? '-5px' : undefined,\n                    height: reqStream ? '15px' : undefined\n                  }}\n                  className=\"w-timeline-load\"\n                />\n                <span title={title} className=\"w-timeline-time\">\n                  {total}\n                </span>\n              </li>\n            );\n          })}\n        </ul>\n      </div>\n    );\n  }\n});\n\nmodule.exports = Timeline;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tips-dialog.js",
    "content": "var React = require('react');\nvar Dialog = require('./dialog');\nvar CloseBtn = require('./close-btn');\n\nvar TipsDialog = React.createClass({\n  getInitialState: function () {\n    return {};\n  },\n  show: function (data) {\n    this._hideDialog = false;\n    this.setState(data);\n    this.refs.tipsDialog.show();\n  },\n  hide: function () {\n    this.refs.tipsDialog.hide();\n    this._hideDialog = true;\n  },\n  shouldComponentUpdate: function () {\n    return this._hideDialog === false;\n  },\n  render: function () {\n    var state = this.state;\n    return (\n      <Dialog ref=\"tipsDialog\" wstyle=\"w-dns-servers-dialog w-tips-dialog\">\n        <div className=\"modal-header\">\n        <h4>{state.title}</h4>\n          <CloseBtn />\n        </div>\n        <pre className=\"modal-body\">{state.tips}</pre>\n        <div className=\"modal-footer\">\n          <button\n            type=\"button\"\n            className=\"btn btn-default\"\n            data-dismiss=\"modal\"\n          >\n            Close\n          </button>\n          <button\n            type=\"button\"\n            data-dismiss=\"modal\"\n            className=\"btn btn-primary w-copy-text-with-tips\"\n            data-clipboard-text={state.dir}\n          >\n            Copy directory\n          </button>\n        </div>\n      </Dialog>\n    );\n  }\n});\n\nmodule.exports = TipsDialog;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tool-box.js",
    "content": "var React = require('react');\nvar ReactDOM = require('react-dom');\nvar util = require('./util');\nvar QRCodeDialog = require('./qrcode-dialog');\nvar TextDialog = require('./text-dialog');\nvar storage = require('./storage');\nvar win = require('./win');\nvar events = require('./events');\nvar Icon = require('./icon');\n\nvar URL_RE = /^(?:(?:[\\w.-]+:)?\\/\\/)?([\\w.-]+)/i;\nvar NOT_EMPTY_RE = /[^\\s]/;\nvar MAX_QRCODE_LEN = 2048;\nvar MAX_JSON_LEN = 32768;\nvar MAX_SAVE_LEN = 5120;\nvar MAX_TEXT_LEN = 5120;\nvar MAX_IMAGE_SIZE = 1024 * 1024 * 3;\nvar findDOMNode = ReactDOM.findDOMNode;\n\nvar ToolBox = React.createClass({\n  getInitialState: function () {\n    return {\n      qrcodeValue: util\n        .toString(storage.get('qrcodeValue'))\n        .substring(0, MAX_QRCODE_LEN),\n      jsonValue: util\n        .toString(storage.get('jsonValue'))\n        .substring(0, MAX_SAVE_LEN),\n      codecText: util\n        .toString(storage.get('codecText'))\n        .substring(0, MAX_TEXT_LEN)\n    };\n  },\n  _saveQRCodeValue: function () {\n    storage.set('qrcodeValue', this.state.qrcodeValue);\n  },\n  saveQRCodeValue: function () {\n    clearTimeout(this.qrcodeTimer);\n    this.qrcodeTimer = setTimeout(this._saveQRCodeValue, 1000);\n  },\n  _saveJSONValue: function () {\n    var value = this.state.jsonValue;\n    if (value.length <= MAX_SAVE_LEN) {\n      storage.set('jsonValue', value);\n    }\n  },\n  _saveCodecText: function () {\n    var value = this.state.codecText;\n    if (value.length <= MAX_TEXT_LEN) {\n      storage.set('codecText', value);\n    }\n  },\n  saveCodecText: function () {\n    clearTimeout(this.codecTimer);\n    this.codecTimer = setTimeout(this._saveCodecText, 1000);\n  },\n  onJSONChange: function (e) {\n    this.setState(\n      {\n        jsonValue: e.target.value\n      },\n      this._saveJSONValue\n    );\n  },\n  onForamt: function (e) {\n    util.handleFormat(e, this.formatJSON);\n    util.handleTab(e);\n  },\n  onCodecChange: function (e) {\n    this.setState(\n      {\n        codecText: e.target.value\n      },\n      this._saveCodecText\n    );\n  },\n  generageQRCode: function () {\n    this.refs.qrcodeDialog.show(this.state.qrcodeValue);\n  },\n  parseJSON: function () {\n    events.trigger('showJsonViewDialog', this.state.jsonValue);\n  },\n  formatJSON: function() {\n    var value = this.state.jsonValue;\n    value = value && util.parseRawJson(value);\n    if (!value) {\n      return;\n    }\n    this.setState({\n      jsonValue: JSON.stringify(value, null, '  ')\n    }, this._saveJSONValue);\n  },\n  encode: function () {\n    try {\n      var value = util.toBase64(this.state.codecText);\n      this.refs.textDialog.show(value);\n    } catch (e) {\n      win.alert(e.message);\n    }\n  },\n  decode: function () {\n    try {\n      var value = util.decodeBase64(this.state.codecText).text;\n      this.refs.textDialog.show(value);\n    } catch (e) {\n      win.alert(e.message);\n    }\n  },\n  uploadFile: function () {\n    findDOMNode(this.refs.uploadFile).click();\n  },\n  readFile: function () {\n    var self = this;\n    var file = new FormData(findDOMNode(this.refs.uploadFileForm)).get('file');\n    if (!(file.size <= MAX_IMAGE_SIZE)) {\n      return win.alert('Maximum file size: 3MB');\n    }\n    var type = 'data:' + file.type + ';base64,';\n    util.readFileAsBase64(file, function (base64) {\n      findDOMNode(self.refs.uploadFile).value = '';\n      self.refs.textDialog.show(type + base64, base64, file.name);\n    });\n  },\n  onQRCodeChange: function (e) {\n    this.setState(\n      {\n        qrcodeValue: e.target.value\n      },\n      this.saveQRCodeValue\n    );\n  },\n  onDomainChange: function (e) {\n    this.setState({\n      domainValue: e.target.value\n    });\n  },\n  generateCert: function () {\n    window.open(\n      'cgi-bin/create-cert?domain=' + encodeURIComponent(this.state.domainValue),\n      'downloadTargetFrame'\n    );\n  },\n  shouldComponentUpdate: util.shouldComponentUpdate,\n  render: function () {\n    var state = this.state;\n    var qrcodeValue = state.qrcodeValue;\n    var jsonValue = state.jsonValue;\n    var domainValue = state.domainValue;\n    var codecText = state.codecText;\n    var emptyJson = !NOT_EMPTY_RE.test(jsonValue);\n    var emptyCodec = !NOT_EMPTY_RE.test(codecText);\n\n    return (\n      <div\n        className={\n          'fill v-box w-tool-box ' +\n          (this.props.hide ? 'hide' : '')\n        }\n      >\n        <div className=\"w-detail-inspectors-title\">\n          <Icon name=\"qrcode\" />QRCode\n          <button\n            className=\"btn btn-primary\"\n            disabled={!NOT_EMPTY_RE.test(qrcodeValue)}\n            onClick={this.generageQRCode}\n          >\n            Show\n          </button>\n        </div>\n        <textarea\n          onChange={this.onQRCodeChange}\n          onKeyDown={util.handleTab}\n          value={qrcodeValue}\n          className=\"w-tool-box-ctn\"\n          maxLength={MAX_QRCODE_LEN}\n          placeholder=\"Enter URL or text\"\n        />\n        <div className=\"w-detail-inspectors-title\">\n          <Icon name=\"pencil\" />JSON\n          <button\n            className=\"btn btn-primary\"\n            disabled={emptyJson}\n            onClick={this.parseJSON}\n            style={{marginLeft: 10}}\n          >\n            Inspect\n          </button>\n          <button\n            className=\"btn btn-default\"\n            disabled={emptyJson}\n            onClick={this.formatJSON}\n          >\n            Format\n          </button>\n        </div>\n        <textarea\n          onChange={this.onJSONChange}\n          value={jsonValue}\n          className=\"w-tool-box-ctn\"\n          maxLength={MAX_JSON_LEN}\n          placeholder=\"Enter JSON text\"\n          onKeyDown={this.onForamt}\n        />\n        <div className=\"w-detail-inspectors-title\" style={{ height: 20 }}>\n          <Icon name=\"eye-close\" />Base64\n          <button\n            className=\"btn btn-primary\"\n            style={{marginLeft: 10}}\n            onClick={this.uploadFile}\n          >\n            Upload\n          </button>\n          <button\n            className=\"btn btn-default\"\n            style={{marginLeft: 10}}\n            disabled={emptyCodec}\n            onClick={this.encode}\n          >\n            Encode\n          </button>\n          <button\n            className=\"btn btn-default\"\n            disabled={emptyCodec}\n            onClick={this.decode}\n          >\n            Decode\n          </button>\n        </div>\n        <textarea\n          onChange={this.onCodecChange}\n          onKeyDown={util.handleTab}\n          value={codecText}\n          className=\"w-tool-box-ctn\"\n          maxLength={MAX_TEXT_LEN}\n          placeholder=\"Enter text\"\n        />\n        <div className=\"w-detail-inspectors-title\">\n          <Icon name=\"certificate\" />Certificate\n        </div>\n        <div className=\"box w-generate-cert\">\n          <input\n            className=\"fill\"\n            maxLength=\"256\"\n            placeholder=\"Enter certificate domain name\"\n            value={domainValue}\n            onChange={this.onDomainChange}\n          />\n          <button\n            className=\"btn btn-primary\"\n            disabled={!domainValue || !URL_RE.test(domainValue)}\n            onClick={this.generateCert}\n          >\n            Download\n          </button>\n        </div>\n        <QRCodeDialog ref=\"qrcodeDialog\" />\n        <TextDialog ref=\"textDialog\" />\n        <form\n          ref=\"uploadFileForm\"\n          encType=\"multipart/form-data\"\n          style={{ display: 'none' }}\n        >\n          <input\n            ref=\"uploadFile\"\n            onChange={this.readFile}\n            name=\"file\"\n            type=\"file\"\n          />\n        </form>\n      </div>\n    );\n  }\n});\n\nmodule.exports = ToolBox;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/tools.js",
    "content": "require('../css/tools.css');\nvar React = require('react');\nvar Console = require('./console');\nvar ServerLog = require('./server-log');\nvar ToolBox = require('./tool-box');\nvar events = require('./events');\nvar LazyInit = require('./lazy-init');\nvar dataCenter = require('./data-center');\nvar TabMgr = require('./tab-mgr');\n\nvar BtnGroup = require('./btn-group');\nvar util = require('./util');\n\nvar BTNS = [\n  {\n    name: 'Console',\n    icon: 'console',\n    active: true\n  },\n  {\n    name: 'Server',\n    icon: 'file'\n  },\n  {\n    name: 'Toolbox',\n    icon: 'briefcase'\n  }\n];\n\nvar Tools = React.createClass({\n  getInitialState: function () {\n    return { name: 'Console' };\n  },\n  componentDidMount: function() {\n    var self = this;\n    events.on('toolTabsChange', function () {\n      self.changeTab = true;\n      self.setState({});\n    });\n  },\n  shouldComponentUpdate: function (nextProps) {\n    var hide = util.getBool(this.props.hide);\n    if (hide != util.getBool(nextProps.hide)) {\n      return true;\n    }\n    if (hide) {\n      return false;\n    }\n    var changeTab = this.changeTab;\n    this.changeTab = false;\n    return changeTab === true;\n  },\n  toggleTabs: function (btn) {\n    this.changeTab = true;\n    this.setState({ name: btn.name, plugin: null });\n  },\n  clearLogs: function () {\n    if (BTNS[0].active) {\n      this.refs.console.clearLogs();\n    } else if (BTNS[1].active) {\n      this.refs.serverLog.clearLogs();\n    }\n  },\n  onDoubleClickBar: function () {\n    if (BTNS[0].active) {\n      if (this.refs.console.container.scrollTop < 5) {\n        this.refs.console.autoRefresh();\n      } else {\n        this.refs.console.scrollTop();\n      }\n    } else if (BTNS[1].active) {\n      if (this.refs.serverLog.container.scrollTop < 5) {\n        this.refs.serverLog.autoRefresh();\n      } else {\n        this.refs.serverLog.scrollTop();\n      }\n    }\n  },\n  isActive: function (name) {\n    var plugin = this.state.plugin;\n    return plugin && plugin.plugin === name;\n  },\n  getStyle: function (name) {\n    return 'btn btn-default' + (this.isActive(name) ? ' w-spec-active' : '');\n  },\n  showCustomTab: function(tab) {\n    this.changeTab = true;\n    this.refs.tabs.clearSelection();\n    this.setState({ name: null, plugin: tab });\n  },\n  createCustomTabs: function() {\n    var self = this;\n    var tabs = dataCenter.getToolTabs();\n    if (!tabs.length) {\n      return;\n    }\n    return (\n      <div className=\"fill w-custom-tabs\">\n        {\n          tabs.map(function(tab) {\n            var pluginName = tab.plugin;\n            var icon = util.getTabIcon(tab);\n            return (\n              <button\n                key={'_' + pluginName}\n                onClick={function () {\n                  self.showCustomTab(tab);\n                }}\n                className={'w-custom-tab-btn ' + self.getStyle(pluginName)}\n                title={pluginName}\n              >\n              {icon ? <img className=\"w-tab-icon\" src={icon} /> : null}\n              {tab.name}\n              </button>\n            );\n          })\n        }\n      </div>\n    );\n  },\n  render: function () {\n    var state = this.state;\n    var name = state.name;\n    return (\n      <div\n        className={\n          'fill v-box w-tools' +\n          (util.getBool(this.props.hide) ? ' hide' : '')\n        }\n      >\n        <BtnGroup\n          ref=\"tabs\"\n          onDoubleClickBar={this.onDoubleClickBar}\n          onClick={this.toggleTabs}\n          onDoubleClick={this.clearLogs}\n          btns={BTNS}\n          appendTabs={this.createCustomTabs()}\n        />\n        <LazyInit inited={name === BTNS[0].name}>\n          <Console ref=\"console\" hide={!BTNS[0].active} />\n        </LazyInit>\n        <LazyInit inited={name === BTNS[1].name}>\n        <ServerLog ref=\"serverLog\" hide={!BTNS[1].active} />\n        </LazyInit>\n        <LazyInit inited={name === BTNS[2].name}>\n          <ToolBox hide={!BTNS[2].active} />\n        </LazyInit>\n        <TabMgr\n          active={state.plugin && state.plugin.plugin}\n          hide={util.getBool(this.props.hide)}\n          tabs={dataCenter.getToolTabs()}\n          className=\"w-custom-tab-panel\"\n        />\n      </div>\n    );\n  }\n});\n\nmodule.exports = Tools;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/update-all-btn.js",
    "content": "var React = require('react');\nvar events = require('./events');\nvar Icon = require('./icon');\n\nvar UpdateAllBtn = React.createClass({\n  getInitialState: function () {\n    return { disabled: true };\n  },\n  componentDidMount: function () {\n    var self = this;\n    events.on('setUpdateAllBtnState', function (_, hasNewPlugin) {\n      self.setState({ disabled: !hasNewPlugin });\n    });\n  },\n  updateAllPlugins: function () {\n    !this.state.disabled && events.trigger('updateAllPlugins');\n  },\n  render: function () {\n    var hide = this.state.disabled || this.props.hide;\n    return (\n      <a\n        onClick={this.updateAllPlugins}\n        className={'w-plugins-menu w-plugin-update-btn' + (hide ? ' hide' : '')}\n        draggable=\"false\"\n      >\n        <Icon name=\"refresh\" />\n        Update\n      </a>\n    );\n  }\n});\n\nmodule.exports = UpdateAllBtn;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/util.js",
    "content": "var $ = require('jquery');\nvar base64JS = require('base64-js');\nvar jsBase64 = require('js-base64').Base64;\nvar json2 = require('./components/json');\nvar events = require('./events');\nvar isUtf8 = require('./is-utf8');\nvar message = require('./message');\nvar win = require('./win');\nvar storage = require('./storage');\n\nvar toByteArray = base64JS.toByteArray;\nvar fromByteArray = base64JS.fromByteArray;\nvar base64Decode = jsBase64.decode;\nvar base64Encode = jsBase64.encode;\nvar toBase64 = jsBase64.toBase64;\nvar CRLF_RE = /\\r\\n|\\r|\\n/g;\nvar COMMENT_RE = /#[^\\r\\n]*/g;\nvar BIG_NUM_RE = /[:\\[][\\s\\n\\r]*-?[\\d.]{16,}[\\s\\n\\r]*[,\\}\\]]/;\nvar dragCallbacks = {};\nvar dragTarget, dragOffset, dragCallback;\nvar logTempId = 0;\nvar RAW_CRLF_RE = /\\\\n|\\\\r/g;\nvar LEVELS = ['fatal', 'error', 'warn', 'info', 'debug'];\nvar MAX_CURL_BODY = 1024 * 72;\nvar search = (window.location.search || '').substring(1);\nvar useCustomEditor = search.indexOf('useCustomEditor') !== -1;\nvar JSON_RE = /^\\s*(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])\\s*$/;\nvar isJSONText;\nvar MAX_SAFE_INTEGER = (Number.MAX_SAFE_INTEGER || '9007199254740991') + '';\nvar MIN_SAFE_INTEGER = Math.abs(Number.MIN_SAFE_INTEGER || '9007199254740991') + '';\nvar DIG_RE = /^([+-]?)([1-9]\\d{0,15})$/;\nvar SOURCE_SEP = '# (From ';\nvar SOURCE_SEP_LEN = SOURCE_SEP.length;\nvar BASE_SERVICE_URL = 'service/';\n\nexports.SOURCE_SEP = SOURCE_SEP;\nexports.CRLF_RE = CRLF_RE;\nexports.isElectron = /Electron\\//i.test(window.navigator.userAgent);\nexports.EDITOR_THEMES = [\n  'default',\n  'neat',\n  'elegant',\n  'erlang-dark',\n  'night',\n  'monokai',\n  'cobalt',\n  'eclipse',\n  'rubyblue',\n  'lesser-dark',\n  'xq-dark',\n  'xq-light',\n  'ambiance',\n  'blackboard',\n  'vibrant-ink',\n  'solarized dark',\n  'solarized light',\n  'twilight',\n  'midnight'\n];\n\nfunction isSafeNumStr(str) {\n  if (str == '0') {\n    return true;\n  }\n  if (!DIG_RE.test(str)) {\n    return false;\n  }\n  var sign = RegExp.$1 === '-';\n  var num = RegExp.$2;\n  if (num.length < 16) {\n    return true;\n  }\n  return num <= (sign ? MIN_SAFE_INTEGER : MAX_SAFE_INTEGER);\n}\n\nfunction replaceCrLf(char) {\n  return char === '\\\\r' ? '\\r' : '\\n';\n}\n\nfunction noop(_) {\n  return _;\n}\n\nexports.noop = noop;\n\nfunction formatPath(path) {\n  if (!path) {\n    return '/';\n  }\n  return path[0] === '/' ? path : '/' + path;\n}\n\nfunction getServiceApi(win, bridgeApi) {\n  if (!win || !bridgeApi) {\n    return;\n  }\n  try {\n    var serviceApi = win.__whistleServiceApi;\n    if (!serviceApi && typeof win.getServiceApiForWhistle === 'function') {\n      serviceApi = win.getServiceApiForWhistle(bridgeApi) || {};\n      win.__whistleServiceApi = serviceApi;\n    }\n  } catch (e) {}\n  return serviceApi;\n}\n\nexports.getServiceApi = getServiceApi;\n\nexports.getServiceUrl = function (win, path, bridgeApi) {\n  var serviceApi = getServiceApi(win, bridgeApi);\n  var complete = serviceApi;\n  if (!serviceApi) {\n    try {\n      complete = win.location.href.indexOf(BASE_SERVICE_URL) !== -1 && win.document.readyState === 'complete';\n    } catch (e) {}\n  }\n  return !path && serviceApi ? null : BASE_SERVICE_URL + (complete && !serviceApi ? '?t=' + Date.now() : '') + '#' + formatPath(path);\n};\n\nexports.getQuery = function() {\n  if (typeof search === 'string') {\n    search = parseQueryString(search);\n  }\n  return search;\n};\n\nfunction likeJson(str) {\n  return str && JSON_RE.test(str);\n}\n\nexports.likeJson = likeJson;\n\nfunction compare(v1, v2) {\n  return v1 == v2 ? 0 : v1 > v2 ? -1 : 1;\n}\n\nfunction comparePlugin(p1, p2) {\n  return (\n    compare(p1.priority, p2.priority) ||\n    compare(p2.mtime, p1.mtime) ||\n    (p1._key > p2._key ? 1 : (p1._key == p2._key ? 0 : -1))\n  );\n}\n\nfunction getPluginComparator(plugins) {\n  return function (a, b) {\n    var p1 = plugins[a];\n    var p2 = plugins[b];\n    p1._key = a;\n    p2._key = b;\n    return comparePlugin(p1, p2);\n  };\n}\n\nexports.compare = compare;\nexports.comparePlugin = comparePlugin;\nexports.getPluginComparator = getPluginComparator;\n\nfunction isString(str) {\n  return typeof str === 'string';\n}\n\nexports.isString = isString;\n\nfunction getString(str) {\n  return isString(str) ? str : '';\n}\n\nexports.getString = getString;\n\nfunction notEStr(str) {\n  return str && typeof str === 'string';\n}\n\nexports.notEStr = notEStr;\n\nfunction isBool(b) {\n  return typeof b === 'boolean';\n}\n\nexports.isBool = isBool;\n\nexports.parseLogs = function (str) {\n  if (typeof str === 'string') {\n    try {\n      str = JSON.parse(str);\n    } catch (e) {}\n  }\n  if (!Array.isArray(str)) {\n    return;\n  }\n  var result;\n  var count = 0;\n  for (var i = 0, len = str.length; i < len; i++) {\n    var item = str[i];\n    if (item && item.text !== undefined) {\n      result = result || [];\n      item.id = ++logTempId;\n      item.date = item.date > 0 ? item.date : 0;\n      if (notEStr(item.level)) {\n        item.level = item.level.toLowerCase();\n        if (LEVELS.indexOf(item.level) === -1) {\n          item.level = 'info';\n        }\n      } else {\n        item.level = 'info';\n      }\n      result.push(item);\n      if (++count >= 100) {\n        return result;\n      }\n    }\n  }\n  return result;\n};\n\nexports.copyText = function(text, tips) {\n  var btn = $('#copyTextBtn');\n  btn.attr('data-clipboard-text', text);\n  btn.removeClass().addClass('w-copy-text' + (tips ? '-with-tips' : ''));\n  btn.trigger('click');\n};\n\nexports.preventDefault = function(e) {\n  e.keyCode == 8 && e.preventDefault();\n};\n\nexports.preventBlur = function(e) {\n  e.preventDefault();\n};\n\nfunction showService(path, delay) {\n  if (delay) {\n    setTimeout(function() {\n      events.trigger('showService', path);\n    }, 100);\n  } else {\n    events.trigger('showService', path);\n  }\n}\nexports.showService = showService;\n\nexports.hideService = function() {\n  events.trigger('hideService');\n};\n\n\nfunction getSelectedText(x, y) {\n  try {\n    var selection = window.getSelection();\n    for (var i = 0, len = selection.rangeCount; i < len; i++) {\n      var range = selection.getRangeAt(i);\n      var pos = range.getBoundingClientRect();\n      x -= pos.x;\n      y -= pos.y;\n      if (x >= 0 && y >= 0 && x <= pos.width && y <= pos.height) {\n        return selection.toString();\n      }\n    }\n    selection.removeAllRanges();\n  } catch (e) {}\n}\n\nexports.getSelectedText = getSelectedText;\n\nvar LOCAL_UI_HOST_LIST = [\n  'local.whistlejs.com',\n  'local.wproxy.org',\n  'rootca.pro'\n];\n\nexports.getCAHash = function(server, urlList) {\n  var ipv4 = server && server.ipv4;\n  var port = (server && server.port) || 8899;\n  var result = [port];\n  var len = 0;\n\n  if (Array.isArray(ipv4)) {\n    ipv4.forEach(function(ip) {\n      if (ip && typeof ip === 'string') {\n        result.push(ip);\n        urlList && urlList.push(ip);\n        len += ip.length + 1;\n      }\n    });\n  }\n  if (LOCAL_UI_HOST_LIST.indexOf(location.hostname) === -1) {\n    var url = location.href.replace(/[?#].*$/, '').replace();\n    var index = url.lastIndexOf('/');\n    if (index) {\n      url = url.substring(0, index);\n    }\n    urlList && urlList.push(url);\n    url = encodeURIComponent(url);\n    len += url.length + 1;\n    result.push(url);\n  }\n  return len && len <= 120 ? '#p=' + result.join() : '';\n};\n\nexports.download = function(data, filename) {\n  var a = document.createElement('a');\n  document.body.appendChild(a);\n  a.style = 'display: none';\n  var blob = new Blob([JSON.stringify(data)], {type: 'octet/stream'});\n  var url = window.URL.createObjectURL(blob);\n  a.href = url;\n  a.download = filename || 'data_' + formatDate() + '.txt';\n  a.click();\n  window.URL.revokeObjectURL(url);\n  document.body.removeChild(a);\n};\n\nvar PROPS_MENUS = [\n  {\n    name: 'Copy'\n  },\n  {\n    name: 'Copy Key'\n  },\n  {\n    name: 'Copy Value'\n  }\n];\n\nexports.handlePropsContextMenu = function(e, ctxMenu) {\n  var target = $(e.target);\n  var row = target.closest('tr');\n  if (!row.length) {\n    return;\n  }\n  var text = getSelectedText(e.clientX, e.clientY);\n  var key = row.attr('data-name');\n  var value = row.attr('data-value');\n  if (!text && !key && !value) {\n    return;\n  }\n  PROPS_MENUS[0].hide = !text;\n  PROPS_MENUS[1].hide = !key;\n  PROPS_MENUS[2].hide = !value;\n  PROPS_MENUS[0].copyText = text;\n  PROPS_MENUS[1].copyText = key;\n  PROPS_MENUS[2].copyText = value;\n  var height = 10 + (text ? 30 : 0) + (key ? 30 : 0) + (value ? 30 : 0);\n  var data = getMenuPosition(e, 110, height);\n  var list = PROPS_MENUS;\n  if (!target.closest('th').length) {\n    list = list.map(noop);\n    list[1] = PROPS_MENUS[2];\n    list[2] = PROPS_MENUS[1];\n  }\n  data.list = list;\n  e.preventDefault();\n  ctxMenu.show(data);\n};\n\nvar NO_HEX_RE = /[^\\da-f]+/ig;\n\nexports.getBase64FromHexText = function (str) {\n  str = str && str.trim().replace(NO_HEX_RE, '');\n  if (!str) {\n    return '';\n  }\n  var len = str.length;\n  if (len % 2) {\n    str += '0';\n  }\n  var result = [];\n  for (var i = 0; i < len; i += 2) {\n    result.push(parseInt(str.substring(i, i + 2), 16));\n  }\n  try {\n    return fromByteArray(result);\n  } catch (e) {}\n  return '';\n};\n\nfunction stopDrag() {\n  dragCallback = dragTarget = dragOffset = null;\n  events.trigger('stopDrag');\n}\n\n\n$(document)\n  .on('mousedown', function (e) {\n    stopDrag();\n    var target = $(e.target);\n    Object.keys(dragCallbacks).some(function (selector) {\n      dragTarget = target.closest(selector);\n      if (dragTarget.length) {\n        dragCallback = dragCallbacks[selector];\n        return true;\n      }\n      dragTarget = null;\n    });\n\n    if (!dragTarget || !dragCallback) {\n      return;\n    }\n    dragOffset = e;\n    e.preventDefault();\n  })\n  .on('mousemove', function (e) {\n    if (!dragTarget) {\n      return;\n    }\n    dragCallback.forEach(function (callback) {\n      callback(\n        dragTarget,\n        e.clientX - dragOffset.clientX,\n        e.clientY - dragOffset.clientY,\n        dragOffset.clientX,\n        dragOffset.clientY\n      );\n    });\n    dragOffset = e;\n  })\n  .on('mouseup', stopDrag)\n  .on('mouseout', function (e) {\n    !e.relatedTarget && stopDrag();\n  });\n\nfunction addDragEvent(selector, callback) {\n  if (\n    !selector ||\n    typeof callback != 'function' ||\n    typeof selector != 'string' ||\n    !(selector = selector.trim())\n  ) {\n    return;\n  }\n  var callbacks = (dragCallbacks[selector] = dragCallbacks[selector] || []);\n  if ($.inArray(callback, callbacks) == -1) {\n    callbacks.push(callback);\n  }\n}\n\nfunction removeDragEvent(selector, callback) {\n  var callbacks = dragCallbacks[selector];\n  if (!callbacks) {\n    return;\n  }\n  if (typeof callback == 'function') {\n    var index = $.inArray(callback, callbacks);\n    if (index != -1) {\n      callbacks.splice(index, 1);\n    }\n    return;\n  }\n  delete dragCallbacks[selector];\n}\n\nexports.addDragEvent = addDragEvent;\nexports.removeDragEvent = removeDragEvent;\n\nvar keyIndex = 1;\n\nexports.getKey = function getKey() {\n  return 'w-reactkey-' + keyIndex++;\n};\n\nfunction getProperty(obj, name, defaultValue) {\n  if (obj && (name || name !== '')) {\n    if (typeof name == 'string') {\n      name = name.split('.');\n    }\n    for (var i = 0, len = name.length - 1; i <= len; i++) {\n      var prop = name[i];\n      if (prop in obj) {\n        obj = obj[prop];\n        if (i == len) {\n          return obj;\n        }\n        if (!obj) {\n          return defaultValue;\n        }\n      } else {\n        return defaultValue;\n      }\n    }\n  }\n\n  return defaultValue;\n}\n\nexports.getProperty = getProperty;\n\nfunction getServerIp(modal) {\n  var ip = modal.hostIp;\n  if (!ip) {\n    return modal.serverIp;\n  }\n  if (modal.realIp) {\n    modal.serverIp = modal.realIp + ', ' + ip;\n    delete modal.realIp;\n  } else if (!modal.serverIp) {\n    var res = modal.res || '';\n    if (res.phost && res.phost != ip) {\n      ip = res.phost + ', ' + ip;\n    }\n    var realEnv = decodeURIComponentSafe(\n      getProperty(res, 'headers.x-whistle-response-for')\n    );\n    if (realEnv) {\n      if (\n        realEnv !== ip &&\n        realEnv\n          .trim()\n          .split(/\\s*,\\s*/)\n          .indexOf(ip) === -1\n      ) {\n        ip = realEnv + ', ' + ip;\n      } else {\n        ip = realEnv;\n      }\n      modal.serverIp = ip\n        .trim()\n        .split(/\\s*,\\s*/)\n        .filter(noop)\n        .join(', ');\n    }\n  }\n  return modal.serverIp || ip;\n}\n\nfunction getCellValue(item, col, name) {\n  name = name || col.name;\n  if (name === 'hostIp') {\n    return getServerIp(item);\n  }\n  return col.key ? getProperty(item, col.key) : item[name];\n}\n\nexports.getCellValue = getCellValue;\n\nexports.getServerIp = getServerIp;\n\nfunction getBool(val) {\n  return !(!val || val === 'false');\n}\n\nexports.getBool = getBool;\n\nexports.shouldComponentUpdate = function (nextProps) {\n  var hide = getBool(this.props.hide);\n  return hide != getBool(nextProps.hide) || !hide;\n};\n\nexports.stopPropagation = function(e) {\n  e.stopPropagation();\n};\n\nfunction showSystemError(xhr, useToast) {\n  xhr = xhr || {};\n  var status = xhr.status;\n  var showTips = useToast ? message.error : win.alert;\n  if (!status) {\n    if (xhr.errMsg === 'timeout') {\n      return showTips('Request timeout');\n    }\n    return showTips('Please check whether the proxy and server are available');\n  }\n  var msg = xhr.responseText || STATUS_CODES[status];\n  if (msg) {\n    return showTips('[' + status + '] ' + String(msg).substring(0, 1024));\n  }\n  showTips('[' + status + '] Unknown error, try again later');\n}\n\nexports.showSystemError = showSystemError;\n\nexports.getClasses = function getClasses(obj) {\n  var classes = [];\n  for (var i in obj) {\n    obj[i] && classes.push(i);\n  }\n  return classes.join(' ');\n};\n\nfunction getRawType(type) {\n  if (type && typeof type != 'string') {\n    type = type['content-type'] || type.contentType;\n  }\n  return typeof type === 'string'\n    ? type.split(';')[0].trim().toLowerCase()\n    : '';\n}\n\nexports.getRawType = getRawType;\n\nexports.getExtension = function (headers) {\n  var suffix = getContentType(headers);\n  var type;\n  if (suffix === 'XML') {\n    type = getRawType(headers);\n    if (type.indexOf('image/') === 0) {\n      suffix = 'IMG';\n    }\n  }\n  if (suffix !== 'IMG') {\n    return suffix\n      ? '.' + (suffix === 'TEXT' ? 'txt' : suffix.toLowerCase())\n      : '';\n  }\n  type = type || getRawType(headers);\n  type = type.substring(type.indexOf('/') + 1).toLowerCase();\n  return /\\w+/.test(type) ? '.' + RegExp['$&'] : '';\n};\n\nfunction getContentType(type) {\n  type = getRawType(type);\n  if (type) {\n    if (type.indexOf('javascript') != -1) {\n      return 'JS';\n    }\n    if (type.indexOf('css') != -1) {\n      return 'CSS';\n    }\n    if (type.indexOf('html') != -1) {\n      return 'HTML';\n    }\n    if (type.indexOf('json') != -1) {\n      return 'JSON';\n    }\n    if (type.indexOf('xml') != -1) {\n      return 'XML';\n    }\n    if (type.indexOf('text/') != -1) {\n      return 'TEXT';\n    }\n    if (type.indexOf('image/') != -1) {\n      return 'IMG';\n    }\n  }\n\n  return null;\n}\n\nexports.getContentType = getContentType;\n\nfunction isText(contentType) {\n  if (!contentType) {\n    return true;\n  }\n  contentType = getContentType(contentType);\n  return contentType && contentType !== 'IMG';\n}\n\nexports.isText = isText;\n\nfunction getHost(url) {\n  if (!url) {\n    return '';\n  }\n  var start = url.indexOf('://');\n  start = start == -1 ? 0 : start + 3;\n  var end = url.indexOf('/', start);\n  url = end == -1 ? url.substring(start) : url.substring(start, end);\n  if (url && (url.indexOf('?') !== -1 || url.indexOf('#') !== -1)) {\n    url = url.replace(/[?#].*$/, '');\n  }\n  return url;\n}\n\nvar HEAD_RE = /^head$/i;\nexports.hasBody = function hasBody(res, req) {\n  if (req && HEAD_RE.test(req.method)) {\n    return false;\n  }\n  var statusCode = res.statusCode;\n  return !(\n    statusCode == 204 ||\n    (statusCode >= 300 && statusCode < 400) ||\n    (100 <= statusCode && statusCode <= 199)\n  );\n};\n\nexports.getHostname = function getHostname(url) {\n  url = getHost(url);\n  var end = url.lastIndexOf(':');\n  return end == -1 ? url : url.substring(0, end);\n};\n\nexports.getHost = getHost;\n\nexports.getProtocol = function getProtocol(url) {\n  var index = url.indexOf('://');\n  return index == -1 ? 'TUNNEL' : url.substring(0, index).toUpperCase();\n};\n\n\nexports.getTransProto = function(req) {\n  var headers = req.headers;\n  var proto = headers && headers['x-whistle-transport-protocol'];\n  if (!proto || typeof proto !== 'string' || proto.length > 33) {\n    return;\n  }\n  try {\n    return decodeURIComponent(proto).toUpperCase();\n  } catch (e) {}\n  return proto.toUpperCase();\n};\n\nexports.ensureVisible = function (elem, container, init) {\n  elem = $(elem);\n  container = $(container);\n  var top = elem.offset().top - container.offset().top;\n  if (!top) {\n    return;\n  }\n  var conHeight = container[0].offsetHeight;\n  var elemHeight = elem[0].offsetHeight;\n  var scrollTop;\n  if (top < 0) {\n    if (init) {\n      scrollTop = Math.ceil((conHeight - elemHeight) / 2);\n      scrollTop = Math.max(0, container.scrollTop() + top - scrollTop);\n      container.scrollTop(scrollTop);\n    } else {\n      container.scrollTop(container.scrollTop() + top - 2);\n    }\n    return;\n  }\n\n  top += elemHeight - conHeight;\n  if (top > 0) {\n    if (init) {\n      scrollTop = Math.ceil(conHeight / 2);\n      scrollTop = Math.max(0, container.scrollTop() + top + scrollTop);\n      container.scrollTop(scrollTop);\n    } else {\n      container.scrollTop(container.scrollTop() + top + 2);\n    }\n  }\n};\n\nfunction parseQueryString(str, delimiter, seperator, decode, donotAllowRepeat) {\n  var result = {};\n  window.___hasFormData = false;\n  if (!str || !(str = (str + '').trim())) {\n    return result;\n  }\n  delimiter = delimiter || '&';\n  seperator = seperator || '=';\n  str.split(delimiter).forEach(function (pair) {\n    pair = pair.split(seperator);\n    var key = pair[0];\n    var value = pair.slice(1).join('=');\n    if (key || value) {\n      var val = value;\n      var k = key;\n      if (decode == decodeURIComponent) {\n        decode = decodeURIComponentSafe;\n      }\n      try {\n        value = decode ? decode(val) : value;\n      } catch (e) {}\n      try {\n        key = decode ? decode(k) : key;\n      } catch (e) {}\n      if (!donotAllowRepeat && key in result) {\n        var curVal = result[key];\n        if (Array.isArray(curVal)) {\n          curVal.push(value);\n        } else {\n          result[key] = [curVal, value];\n        }\n      } else {\n        result[key] = value;\n      }\n      window.___hasFormData = true;\n    }\n  });\n  return result;\n}\n\nexports.parseQueryString = parseQueryString;\n\nfunction objectToString(obj, rawNames, noEncoding) {\n  if (!obj || typeof obj === 'string') {\n    return obj || '';\n  }\n  var keys = Object.keys(obj);\n  var index = noEncoding ? keys.indexOf('content-encoding') : -1;\n  index !== -1 && keys.splice(index, 1);\n  return keys\n    .map(function (key) {\n      var value = obj[key];\n      key = (rawNames && rawNames[key]) || key;\n      if (!Array.isArray(value)) {\n        return key + ': ' + value;\n      }\n      return value\n        .map(function (val) {\n          return key + ': ' + val;\n        })\n        .join('\\r\\n');\n    })\n    .join('\\r\\n');\n}\n\nexports.objectToString = objectToString;\n\nfunction getRealUrl(modal) {\n  var realUrl = modal.realUrl;\n  return /^(?:http|wss)s?:\\/\\//.test(realUrl) ? realUrl : modal.url;\n}\n\nexports.getRealUrl = getRealUrl;\n\nexports.getReqRawHeaders = function(modal) {\n  var req = modal.req;\n  var realUrl = getRealUrl(modal);\n  var headers = objectToString(req.headers, req.rawHeaderNames);\n  return [ req.method, req.method == 'CONNECT' ? headers.host : getPath(realUrl),\n    'HTTP/' + (req.httpVersion || '1.1') ].join(' ') + '\\r\\n' +  headers;\n};\n\nexports.getResRawHeaders = function(modal) {\n  var res = modal.res || '';\n  var headers = objectToString(res.headers, res.rawHeaderNames);\n  var status = res.statusCode;\n  var msg = status === 'captureError' ? '(Most likely caused by SSL pinning)' : getStatusMessage(res);\n  return ['HTTP/' + (modal.req.httpVersion || '1.1'), status, msg].join(' ') + '\\r\\n' + headers;\n};\n\nfunction toLowerCase(str) {\n  return typeof str == 'string' ? str.trim().toLowerCase() : str;\n}\n\nfunction getContentEncoding(headers) {\n  var encoding = toLowerCase(\n    (headers && headers['content-encoding']) || headers\n  );\n  return encoding === 'gzip' || encoding === 'br' || encoding === 'deflate' ? encoding : null;\n}\n\nexports.getOriginalReqHeaders = function (item, rulesHeaders) {\n  var req = item.req;\n  var headers = $.extend({}, req.headers, rulesHeaders || item.rulesHeaders, true);\n  if (item.clientId && !headers['x-whistle-client-id']) {\n    headers['x-whistle-client-id'] = item.clientId;\n  }\n  if (item.h2Id) {\n    headers['x-whistle-alpn-protocol'] = item.h2Id;\n  }\n  if (getContentEncoding(headers)) {\n    delete headers['content-encoding'];\n  }\n  return objectToString(headers, req.rawHeaderNames);\n};\n\nfunction removeProtocol(url) {\n  var index = url.indexOf('://');\n  return index == -1 ? url : url.substring(index + 3);\n}\n\nexports.removeProtocol = removeProtocol;\n\nfunction getPath(url) {\n  if (!notEStr(url)) {\n    return '';\n  }\n  url = removeProtocol(url);\n  var index = url.indexOf('/');\n  return index == -1 ? '/' : url.substring(index);\n}\n\nexports.getPath = getPath;\n\nvar parseJ = function (str, resolve) {\n  var result;\n  if (resolve && BIG_NUM_RE.test(str)) {\n    window._$hasBigNumberJson = true;\n    result = json2.parse(str);\n  } else {\n    result = JSON.parse(str);\n  }\n  return typeof result === 'object' ? result : null;\n};\n\nfunction parseJSON(str, resolve) {\n  isJSONText = false;\n  if (typeof str !== 'string' || !(str = str.trim())) {\n    return;\n  }\n  if (resolve) {\n    if (!/({[\\w\\W]*}|\\[[\\w\\W]*\\])/.test(str)) {\n      return;\n    }\n    if (str === RegExp.$1) {\n      isJSONText = true;\n    } else {\n      str = RegExp.$1;\n    }\n  }\n  try {\n    return parseJ(str, resolve);\n  } catch (e) {}\n}\n\nexports.parseJSON = parseJSON;\n\nfunction isNum(n) {\n  return typeof n === 'number';\n}\n\nfunction parseLine(line) {\n  var index = line.indexOf(': ');\n  if (index === -1) {\n    index = line.indexOf(':');\n    if (index === -1) {\n      index = line.indexOf('=');\n    }\n  }\n  var name, value;\n  if (index != -1) {\n    name = line.substring(0, index).trim();\n    value = line.substring(index + 1).trim();\n    if (value) {\n      var fv = value[0];\n      var lv = value[value.length - 1];\n      if (fv === lv) {\n        if (fv === '\"' || fv === '\\'' || fv === '`') {\n          value = value.slice(1, -1);\n          if (value && fv === '`') {\n            value = value.replace(RAW_CRLF_RE, replaceCrLf);\n          }\n        }\n      } else if (isSafeNumStr(value)) {\n        value = parseInt(value, 10);\n      }\n    }\n  } else {\n    name = line.trim();\n    value = '';\n  }\n  return {\n    name: name,\n    value: value\n  };\n}\n\nfunction parseLinesJSON(text) {\n  if (typeof text !== 'string' || !(text = text.trim())) {\n    return null;\n  }\n  var result;\n  text.split(/\\r\\n|\\n|\\r/).forEach(function(line) {\n    if (!(line = line.trim())) {\n      return;\n    }\n    var obj = parseLine(line);\n    var name = obj.name;\n    var value = obj.value;\n    var isKey = !Array.isArray(name);\n    if (isKey || name.length <= 1) {\n      if (isKey) {\n        result = result || {};\n      } else {\n        name = name[0];\n        result = result || [];\n      }\n      result[name] = value;\n      return;\n    }\n    result = result || (isNum(name[0]) ? [] : {});\n    obj = result;\n    var lastIndex = name.length - 1;\n    name.forEach(function(key, i) {\n      if (i === lastIndex) {\n        obj[key] = value;\n        return;\n      }\n      var next = obj[key];\n      if (!next || typeof next !== 'object') {\n        next = isNum(name[i + 1]) ? [] : {};\n      }\n      obj[key] = next;\n      obj = next;\n    });\n  });\n  return result || {};\n}\n\nexports.parseJSON2 = function (str) {\n  return parseJSON(str) || parseLinesJSON(str);\n};\n\nfunction resolveJSON(str, decode) {\n  window._$hasBigNumberJson = false;\n  var result = parseJSON(str, true);\n  if (result || !str || !decode) {\n    return result;\n  }\n  try {\n    return parseJSON(decode(str), true);\n  } catch (e) {}\n}\n\nexports.unique = function (arr, reverse) {\n  var result = [];\n  if (reverse) {\n    for (var i = arr.length - 1; i >= 0; i--) {\n      var item = arr[i];\n      if (result.indexOf(item) == -1) {\n        result.unshift(item);\n      }\n    }\n  } else {\n    arr.forEach(function (item) {\n      if (result.indexOf(item) == -1) {\n        result.push(item);\n      }\n    });\n  }\n\n  return result;\n};\n\nexports.getFilename = function (item, notEmpty) {\n  var url = item.url;\n  if (item.isHttps) {\n    return url;\n  }\n  if (notEmpty && item.filename) {\n    return item.filename;\n  }\n  if (item.simplePath) {\n    return item.simplePath;\n  }\n\n  url = removeProtocol(url.replace(/[?#].*/, ''));\n  var index = url.lastIndexOf('/');\n  var name = index != -1 && url.substring(index + 1);\n  if (!name) {\n    if (notEmpty) {\n      url = url.substring(0, index);\n      index = url.lastIndexOf('/');\n      if (index === -1) {\n        name = url;\n      } else {\n        name = url.substring(index + 1);\n      }\n    } else {\n      name = '/';\n    }\n  }\n  item[notEmpty ? 'filename' : 'simplePath'] = name;\n  return name;\n};\n\nvar STATUS_CODES = {\n  100: 'Continue',\n  101: 'Switching Protocols',\n  102: 'Processing', // RFC 2518, obsoleted by RFC 4918\n  200: 'OK',\n  201: 'Created',\n  202: 'Accepted',\n  203: 'Non-Authoritative Information',\n  204: 'No Content',\n  205: 'Reset Content',\n  206: 'Partial Content',\n  207: 'Multi-Status', // RFC 4918\n  208: 'Already Reported',\n  226: 'IM Used',\n  300: 'Multiple Choices',\n  301: 'Moved Permanently',\n  302: 'Moved Temporarily',\n  303: 'See Other',\n  304: 'Not Modified',\n  305: 'Use Proxy',\n  307: 'Temporary Redirect',\n  308: 'Permanent Redirect', // RFC 7238\n  400: 'Bad Request',\n  401: 'Unauthorized',\n  402: 'Payment Required',\n  403: 'Forbidden',\n  404: 'Not Found',\n  405: 'Method Not Allowed',\n  406: 'Not Acceptable',\n  407: 'Proxy Authentication Required',\n  408: 'Request Time-out',\n  409: 'Conflict',\n  410: 'Gone',\n  411: 'Length Required',\n  412: 'Precondition Failed',\n  413: 'Request Entity Too Large',\n  414: 'Request-URI Too Large',\n  415: 'Unsupported Media Type',\n  416: 'Requested Range Not Satisfiable',\n  417: 'Expectation Failed',\n  418: 'I\\'m a teapot', // RFC 2324\n  422: 'Unprocessable Entity', // RFC 4918\n  423: 'Locked', // RFC 4918\n  424: 'Failed Dependency', // RFC 4918\n  425: 'Unordered Collection', // RFC 4918\n  426: 'Upgrade Required', // RFC 2817\n  428: 'Precondition Required', // RFC 6585\n  429: 'Too Many Requests', // RFC 6585\n  431: 'Request Header Fields Too Large', // RFC 6585\n  500: 'Internal Server Error',\n  501: 'Not Implemented',\n  502: 'Bad Gateway',\n  503: 'Service Unavailable',\n  504: 'Gateway Time-out',\n  505: 'HTTP Version Not Supported',\n  506: 'Variant Also Negotiates', // RFC 2295\n  507: 'Insufficient Storage', // RFC 4918\n  509: 'Bandwidth Limit Exceeded',\n  510: 'Not Extended', // RFC 2774\n  511: 'Network Authentication Required' // RFC 6585\n};\n\nfunction getStatusMessage(res) {\n  if (!res.statusCode) {\n    return '';\n  }\n  if (typeof res.statusMessage == 'string') {\n    return res.statusMessage;\n  }\n  return STATUS_CODES[res.statusCode] || 'unknown';\n}\n\nexports.getStatusMessage = getStatusMessage;\n\nfunction isUrlEncoded(req) {\n  return (\n    /^post$/i.test(req.method) &&\n    /urlencoded/i.test(req.headers && req.headers['content-type'])\n  );\n}\n\nexports.isUrlEncoded = isUrlEncoded;\n\nfunction toString(value) {\n  return value === undefined ? '' : value + '';\n}\n\nexports.toString = toString;\n\nexports.getValue = function(item, key) {\n  key = key.split('.');\n  var len = key.length;\n  var value;\n  if (len > 1) {\n    value = item;\n    for (var i = 0; i < len; i++) {\n      var origVal = value;\n      value = origVal[key[i]];\n      if (value == null) {\n        value = origVal[key[i].toLowerCase()];\n        if (value == null) {\n          break;\n        }\n      }\n    }\n  } else {\n    value = item[key[0]];\n  }\n  if (value == null) {\n    return '';\n  }\n  value = String(value);\n  return value.length > 1690 ? value.substring(0, 1680) + '...' : value;\n};\n\nfunction openEditor(value) {\n  if (\n    useCustomEditor &&\n    typeof window.customWhistleEditor === 'function' &&\n    window.customWhistleEditor(value) !== false\n  ) {\n    return;\n  }\n  events.trigger('openEditor', value);\n}\n\nexports.openEditor = openEditor;\n\nfunction getTheme() {\n  return document.documentElement.getAttribute('data-theme') || 'light';\n}\n\nexports.openInNewWin = function(value) {\n  var win = window.open('editor.html');\n  win.getValue = function () {\n    return value;\n  };\n  win.getWhistleTheme = getTheme;\n  if (win.setValue) {\n    win.setValue(value);\n  }\n};\n\nfunction getMockValues(values) {\n  if (!values || (!isString(values.value) && !isString(values.base64)) ||\n    (!values.isFile && !notEStr(values.name))) {\n    return;\n  }\n  return values;\n}\n\nfunction getMockData(data) {\n  if (!Array.isArray(data) || data.length > 2 || !notEStr(data[0])) {\n    return;\n  }\n  return {\n    rules: data[0].substring(0, 16000),\n    values: getMockValues(data[1])\n  };\n}\n\nexports.pluginIsDisabled = function(props, name) {\n  var disabledPlugins = props.disabledPlugins || {};\n  return !props.ndp && (props.disabledAllPlugins || disabledPlugins[name]);\n};\n\nvar H2_RE = /http\\/2\\.0/i;\n\nfunction harToSession(entry) {\n  if (!entry) {\n    return;\n  }\n  var times = entry.whistleTimes || '';\n  var startTime = new Date(\n          times.startTime || entry.startedDateTime\n        ).getTime();\n  if (isNaN(startTime)) {\n    return;\n  }\n\n  var rawReq = entry.request || {};\n  var rawRes = entry.response || {};\n  var reqHeaders = parseHeadersFromHar(rawReq.headers);\n  var resHeaders = parseHeadersFromHar(rawRes.headers);\n  var clientIp = entry.clientIPAddress || '127.0.0.1';\n  var serverIp = entry.serverIPAddress || '';\n  var useH2 = H2_RE.test(rawReq.httpVersion || rawRes.httpVersion);\n  var version = useH2 ? '2.0' : '1.1';\n  var postData = rawReq.postData || '';\n  var req = {\n    method: rawReq.method,\n    ip: clientIp,\n    port: rawReq.port,\n    httpVersion: version,\n    unzipSize: postData.size,\n    size: rawReq.bodySize > 0 ? rawReq.bodySize : 0,\n    headers: reqHeaders.headers,\n    rawHeaderNames: reqHeaders.rawHeaderNames,\n    body: ''\n  };\n  var reqText = postData.base64 || postData.text;\n  if (reqText) {\n    if (postData.base64) {\n      req.base64 = reqText;\n    } else {\n      req.body = reqText;\n    }\n  }\n  var content = rawRes.content;\n  var res = {\n    httpVersion: version,\n    statusCode: rawRes.statusCode || rawRes.status,\n    statusMessage: rawRes.statusText,\n    unzipSize: content.size,\n    size: rawRes.bodySize > 0 ? rawRes.bodySize : 0,\n    headers: resHeaders.headers,\n    rawHeaderNames: resHeaders.rawHeaderNames,\n    ip: serverIp,\n    port: rawRes.port,\n    body: ''\n  };\n  var resCtn = rawRes.content;\n  var text = resCtn && resCtn.text;\n  if (text) {\n    if (resCtn.base64) {\n      res.base64 = resCtn.base64;\n    } else if (\n            getContentType(resCtn.mimeType) === 'IMG' ||\n            (text.length % 4 === 0 && /^[a-z\\d+/]+={0,2}$/i.test(text))\n          ) {\n      res.base64 = text;\n    } else {\n      res.body = text;\n    }\n  }\n  var session = {\n    useH2: useH2,\n    startTime: startTime,\n    ttfb: entry.ttfb,\n    frames: entry.frames,\n    url: rawReq.url,\n    realUrl: entry.whistleRealUrl,\n    req: req,\n    res: res,\n    customData: entry.whistleCustomData,\n    fwdHost: entry.whistleFwdHost,\n    sniPlugin: entry.whistleSniPlugin,\n    rules: entry.whistleRules || {},\n    captureError: entry.whistleCaptureError,\n    isHttps: entry.whistleIsHttps,\n    reqError: entry.whistleReqError,\n    resError: entry.whistleResError,\n    version: entry.whistleVersion,\n    nodeVersion: entry.whistleNodeVersion\n  };\n  if (times && times.startTime) {\n    session.dnsTime = times.dnsTime;\n    session.requestTime = times.requestTime;\n    session.responseTime = times.responseTime;\n    session.endTime = times.endTime;\n  } else {\n    var timings = entry.timings || {};\n    var endTime = Math.round(startTime + getTimeFromHar(entry.time));\n    startTime = Math.floor(startTime + getTimeFromHar(timings.dns));\n    session.dnsTime = startTime;\n    startTime = Math.floor(\n            startTime +\n              getTimeFromHar(timings.connect) +\n              getTimeFromHar(timings.ssl) +\n              getTimeFromHar(timings.send) +\n              getTimeFromHar(timings.blocked) +\n              getTimeFromHar(timings.wait)\n          );\n    session.requestTime = startTime;\n    startTime = Math.floor(\n            startTime + getTimeFromHar(timings.receive)\n          );\n    session.responseTime = startTime;\n    session.endTime = Math.max(startTime, endTime);\n  }\n  return session;\n}\n\nexports.harToSession = harToSession;\n\nexports.handleImportData = function(data, type) {\n  if (data) {\n    if (data.type === 'setNetworkSettings') {\n      events.trigger('setNetworkSettings', data);\n      return true;\n    }\n    if (data.type === 'setRulesSettings') {\n      events.trigger('setRulesSettings', data);\n      return true;\n    }\n    if (data.type === 'setValuesSettings') {\n      events.trigger('setValuesSettings', data);\n      return true;\n    }\n    if (data.type === 'setComposerData') {\n      events.trigger('setComposerData', data);\n      return true;\n    }\n  }\n  var mockData = getMockData(data);\n  if (mockData) {\n    events.trigger('showRulesDialog', mockData);\n  } else if (data && type === 'composerImportFile') {\n    if (Array.isArray(data)) {\n      events.trigger('composer', data[0]);\n    } else {\n      var entries = data.log && data.log.entries;\n      if (Array.isArray(entries) && entries.length) {\n        var entry = harToSession(entries[0]);\n        entry && events.trigger('composer', entry);\n      }\n    }\n  }\n  return mockData;\n};\n\nvar rentity = /['<> \"&]/g;\nvar entities = {\n  '\"': '&quot;',\n  '<': '&lt;',\n  '>': '&gt;',\n  '&': '&amp;',\n  ' ': '&nbsp;',\n  '\\'': '&#39;'\n};\nvar rlf = /\\r?\\n/g;\nvar rspace = /\\s/g;\n\nfunction escapeFn(matched) {\n  return entities[matched];\n}\n\nexports.escape = function (str) {\n  if (!str) {\n    return str;\n  }\n  str = (str + '').replace(rentity, escapeFn);\n  return str.replace(rlf, '<br />').replace(rspace, '&nbsp;');\n};\n\nfunction findArray(arr, cb) {\n  if (typeof arr.find === 'function') {\n    return arr.find(cb);\n  }\n  for (var i = 0, len = arr.length; i < len; i++) {\n    var val = arr[i];\n    if (cb(val, i, arr)) {\n      return val;\n    }\n  }\n}\nexports.findArray = findArray;\n\nexports.isFocusEditor = function () {\n  var activeElement = document.activeElement;\n  var nodeName = activeElement && activeElement.nodeName;\n  if (nodeName !== 'INPUT' && nodeName !== 'TEXTAREA') {\n    return false;\n  }\n  return !activeElement.readOnly && !activeElement.disabled;\n};\n\nfunction getMenuPosition(e, menuWidth, menuHeight) {\n  var left = e.pageX;\n  var top = e.pageY;\n  var docElem = document.documentElement;\n  var clientWidth = docElem.clientWidth;\n  if (left + menuWidth - window.scrollX >= clientWidth) {\n    left = Math.max(left - menuWidth, window.scrollX + 1);\n  }\n  if (top + menuHeight - window.scrollY >= docElem.clientHeight - 25) {\n    top = Math.max(top - menuHeight, window.scrollY + 1);\n  }\n  return { top: top, left: left, marginRight: clientWidth - left };\n}\n\nexports.getMenuPosition = getMenuPosition;\n\nfunction socketIsClosed(reqData) {\n  if (!reqData.closed && reqData.frames) {\n    var lastItem = reqData.frames[reqData.frames.length - 1];\n    if (lastItem && (lastItem.closed || lastItem.err)) {\n      reqData.closed = true;\n      reqData.lastErr = lastItem.err;\n    }\n  }\n  return reqData.closed;\n}\n\nexports.socketIsClosed = socketIsClosed;\n\nexports.canAbort = function (item) {\n  if (!item.lost && !item.endTime) {\n    return true;\n  }\n  if (item.reqError || item.resError) {\n    return false;\n  }\n  return !!item.frames && !socketIsClosed(item);\n};\n\nfunction formatBody(body) {\n  if (body.indexOf('\\'') === -1) {\n    return '\\'' + body + '\\'';\n  }\n  return '\"' + body.replace(/\"/g, '\\\\\"') + '\"';\n}\n\nexports.asCURL = function (item) {\n  if (!item) {\n    return item;\n  }\n  var req = item.req;\n  var url = item.url.replace(/^ws/, 'http');\n  var method = req.method;\n  var result = ['curl', '-X', method, JSON.stringify(url)];\n  var headers = req.headers;\n  var rawHeaderNames = req.rawHeaderNames || {};\n  Object.keys(headers).forEach(function (key) {\n    if (\n      key === 'content-length' ||\n      key === 'content-encoding' ||\n      key === 'accept-encoding'\n    ) {\n      return;\n    }\n    result.push(\n      '-H',\n      JSON.stringify((rawHeaderNames[key] || key) + ': ' + headers[key])\n    );\n  });\n  var body = getBody(req, true);\n  if (body && (body.length <= MAX_CURL_BODY || isText(req.headers) || isUrlEncoded(req))) {\n    result.push('--data-raw', formatBody(body));\n  }\n  return result.join(' ').replace(/!/g, '\\\\!');\n};\n\nfunction parseHeadersFromHar(list) {\n  var headers = {};\n  var rawHeaderNames = {};\n  if (Array.isArray(list)) {\n    list.forEach(function (header) {\n      var name = header.name;\n      var key = name.toLowerCase();\n      headers[key] = header.value;\n      rawHeaderNames[key] = name;\n    });\n  }\n  return {\n    headers: headers,\n    rawHeaderNames: rawHeaderNames\n  };\n}\n\nexports.parseHeadersFromHar = parseHeadersFromHar;\n\nfunction getTimeFromHar(time) {\n  return time > 0 ? time : 0;\n}\n\nexports.getTimeFromHar = getTimeFromHar;\n\nexports.parseKeyword = function (keyword) {\n  keyword = keyword.split(/\\s+/);\n  var result = '';\n  var index = 0;\n  for (var i = 0, len = keyword.length; i < len; i++) {\n    var key = keyword[i];\n    var not = key[0] === '!';\n    if (not) {\n      key = key.substring(1);\n    }\n    if (key) {\n      if (key.indexOf('level:') === 0) {\n        if (!result || !result.level) {\n          key = key.substring(6);\n          if (key[0] === '!') {\n            not = true;\n            key = key.substring(1);\n          }\n          if (key) {\n            result = result || {};\n            result.level = key.toLowerCase();\n            result.levelNot = not;\n          }\n        }\n      } else if (key && index < 3) {\n        ++index;\n        result = result || {};\n        result['key' + index] = toRegExp(key) || key.toLowerCase();\n        result['key' + index + 'Not'] = not;\n      }\n    }\n  }\n  return result;\n};\n\nfunction checkKey(raw, text, key) {\n  if (key.test) {\n    return !key.test(raw);\n  }\n  return text.toLowerCase().indexOf(key) === -1;\n}\n\nexports.checkKey = checkKey;\n\nfunction checkLogText(text, keyword) {\n  if (!keyword.key1) {\n    return '';\n  }\n  var raw = text;\n  if (setNot(checkKey(raw, text, keyword.key1), keyword.key1Not)) {\n    return ' hide';\n  }\n  if (keyword.key2 && setNot(checkKey(raw, text, keyword.key2), keyword.key2Not)) {\n    return ' hide';\n  }\n  if (keyword.key3 && setNot(checkKey(raw, text, keyword.key3), keyword.key3Not)) {\n    return ' hide';\n  }\n  return '';\n}\n\nexports.hasVisibleLog = function (list) {\n  var len = list.length;\n  if (!len) {\n    return false;\n  }\n  for (var i = 0; i < len; i++) {\n    if (!list[i].hide) {\n      return true;\n    }\n  }\n};\nexports.trimLogList = function (list, overflow, hasKeyword) {\n  var len = list.length;\n  if (hasKeyword) {\n    var i = 0;\n    while (overflow > 0 && i < len) {\n      if (list[i].hide) {\n        --len;\n        --overflow;\n        list.splice(i, 1);\n      } else {\n        ++i;\n      }\n    }\n  }\n  overflow > 0 && list.splice(0, overflow);\n  return list;\n};\n\nvar TIME_RE = /\\b\\d\\d?:\\d\\d?:\\d\\d?\\b/;\n\nfunction toLocaleString(date) {\n  var str = date.toLocaleString();\n  if (!TIME_RE.test(str)) {\n    return str;\n  }\n  var time = RegExp['$&'];\n  var ms = date.getTime() % 1000;\n  return str.replace(time, time + '.' + paddingMS(ms));\n}\n\nexports.toLocaleString = toLocaleString;\n\nfunction setNot(flag, not) {\n  return not ? !flag : flag;\n}\n\nexports.filterLogList = function (list, keyword, init) {\n  if (!list) {\n    return list;\n  }\n  var map;\n  var result;\n  var addLog;\n  if (init) {\n    map = {};\n    result = [];\n    addLog = function(log) {\n      if (!map[log.id]) {\n        result.push(log);\n        map[log.id] = 1;\n      }\n    };\n  }\n  if (!keyword) {\n    list.forEach(function(item) {\n      item.hide = false;\n      addLog && addLog(item);\n    });\n    return result || list;\n  }\n  list.forEach(function (log) {\n    var level = keyword.level;\n    if (level && setNot(log.level !== level, keyword.levelNot)) {\n      log.hide = true;\n    } else {\n      var text =\n        'Date: ' +\n        toLocaleString(new Date(log.date)) +\n        log.logId +\n        '\\r\\n' +\n        log.text;\n      log.hide = checkLogText(text, keyword);\n    }\n    addLog && addLog(log);\n  });\n  return result || list;\n};\n\nexports.scrollAtBottom = function (con, ctn) {\n  return con.scrollTop + con.offsetHeight + 5 > ctn.offsetHeight;\n};\n\nexports.triggerListChange = function (name, data) {\n  events.trigger(name + 'Change', data);\n  try {\n    var onChange =\n      window.parent[\n        name === 'rules' ? 'onWhistleRulesChange' : 'onWhistleValuesChange'\n      ];\n    if (typeof onChange === 'function') {\n      onChange(data);\n    }\n  } catch (e) {}\n};\n\nvar REG_EXP = /^\\/(.+)\\/([miu]{0,3})$/;\nfunction toRegExp(regExp) {\n  if (regExp && REG_EXP.test(regExp)) {\n    try {\n      return new RegExp(RegExp.$1, RegExp.$2);\n    } catch (e) {}\n  }\n}\nexports.toRegExp = toRegExp;\n\nfunction getPadding(len) {\n  return len > 0 ? new Array(len + 1).join('0') : '';\n}\n\nfunction padLeftZero(n, len) {\n  n = n.toString(16).toUpperCase();\n  return getPadding(len - n.length) + n;\n}\n\nfunction getHexString(arr) {\n  var len = arr.length;\n  var offsetLen = Math.max(6, len.toString(16).length);\n  var str, ch;\n  var result = [];\n  var rowsCount = Math.ceil(len / 16);\n  for (var i = 0; i < rowsCount; i++) {\n    var j = i * 16;\n    var rowLen = Math.min(16 + j, len);\n    str = padLeftZero(Math.max(rowLen - 16, 0), offsetLen) + '  ';\n    var char = '';\n    for (; j < rowLen; j++) {\n      ch = arr[j];\n      str += ' ' + padLeftZero(ch, 2);\n      char += (ch > 31 && ch < 127) || ch > 159 ? String.fromCharCode(ch) : '.';\n    }\n    result.push(str + new Array((17 - char.length) * 3).join(' ') + char);\n  }\n  return result.join('\\n');\n}\n\nvar COMP_RE = /%[a-f\\d]{2}|./gi;\nvar CHECK_COMP_RE = /%[a-f\\d]{2}/i;\nvar SPACE_RE = /\\+/g;\nvar gbkDecoder;\nif (window.TextDecoder) {\n  try {\n    gbkDecoder = new TextDecoder('GB18030');\n  } catch (e) {}\n}\n\nfunction decodeURIComponentSafe(str, isUtf8) {\n  if (!str || typeof str !== 'string') {\n    return '';\n  }\n  var result = str.replace(SPACE_RE, ' ');\n  try {\n    return decodeURIComponent(result);\n  } catch (e) {}\n  if (!isUtf8 && gbkDecoder && CHECK_COMP_RE.test(result)) {\n    try {\n      var arr = [];\n      result.replace(COMP_RE, function (code) {\n        if (code.length > 1) {\n          arr.push(parseInt(code.substring(1), 16));\n        } else {\n          arr.push(String.fromCharCode(code));\n        }\n      });\n      if (!isUtf8(arr)) {\n        return gbkDecoder.decode(new window.Uint8Array(arr));\n      }\n    } catch (e) {}\n  }\n  return str;\n}\n\nexports.decodeURIComponentSafe = decodeURIComponentSafe;\n\nfunction safeEncodeURIComponent(str) {\n  try {\n    return encodeURIComponent(str);\n  } catch (e) {}\n  return str;\n}\nexports.encodeURIComponent = safeEncodeURIComponent;\n\nfunction base64toBytes(base64) {\n  try {\n    return toByteArray(base64);\n  } catch (e) {}\n  return [];\n}\n\nexports.base64Decode = base64Decode;\n\nfunction decodeBase64(base64) {\n  var arr = base64toBytes(base64);\n  var result = {\n    hex: getHexString(arr)\n  };\n  if (!isUtf8(arr)) {\n    try {\n      result.text = gbkDecoder.decode(arr);\n    } catch (e) {}\n  }\n  if (!result.text) {\n    try {\n      result.text = base64Decode(base64);\n    } catch (e) {\n      result.text = base64;\n    }\n  }\n  return result;\n}\n\nexports.decodeBase64 = decodeBase64;\n\nexports.joinBase64 = function(b1, b2) {\n  if (!b1 || !b2) {\n    return b1 || b2;\n  }\n  b1 = toByteArray(b1);\n  b2 = toByteArray(b2);\n  b1 = concatByteArray(b1, b2);\n  return fromByteArray(b1);\n};\n\nfunction getMediaType(res) {\n  var type = getRawType(res.headers);\n  if (!type || getContentType(type) !== 'IMG') {\n    return '';\n  }\n  return type;\n}\n\nvar BODY_KEY = '$body';\nvar HEX_KEY = '$hex';\nvar JSON_KEY = '$json';\nif (window.Symbol) {\n  BODY_KEY = window.Symbol.for(BODY_KEY);\n  HEX_KEY = window.Symbol.for(HEX_KEY);\n  JSON_KEY = window.Symbol.for(JSON_KEY);\n}\n\nexports.BODY_KEY = BODY_KEY;\nexports.HEX_KEY = HEX_KEY;\nexports.JSON_KEY = JSON_KEY;\n\nfunction getHexFromBase64(base64) {\n  if (base64) {\n    try {\n      return getHexString(base64toBytes(base64));\n    } catch (e) {}\n  }\n  return base64;\n}\n\nexports.getHexFromBase64 = getHexFromBase64;\n\nfunction getClosedMsg(data) {\n  return 'Closed' + (data.code ? ' (' + data.code + ')' : '');\n}\n\nfunction initData(data, isReq) {\n  if (data[BODY_KEY] && data[HEX_KEY]) {\n    return;\n  }\n  if (!data.base64) {\n    var body = data.body || data.text;\n    if (data.closed || data.err) {\n      body = String(data.err || getClosedMsg(data));\n    }\n    if (body) {\n      try {\n        body = String(body);\n        data.base64 = base64Encode(body);\n        data[BODY_KEY] = body;\n        data[HEX_KEY] = getHexFromBase64(data.base64);\n      } catch (e) {\n      } finally {\n        delete data.body;\n        delete data.bin;\n        delete data.text;\n      }\n    }\n    if (!data.base64 || data._hasError) {\n      return;\n    }\n  }\n  var type = !isReq && getMediaType(data);\n  if (type) {\n    data[BODY_KEY] = 'data:' + type + ';base64,' + data.base64;\n    data[HEX_KEY] = getHexFromBase64(data.base64);\n  } else {\n    var result = decodeBase64(data.base64);\n    data[BODY_KEY] = result.text;\n    data[HEX_KEY] = result.hex;\n  }\n}\n\nfunction getJson(data, isReq, decode) {\n  if (data[JSON_KEY] == null) {\n    var body = getBody(data, isReq);\n    body = body && resolveJSON(body, decode);\n    data[JSON_KEY] = body\n      ? {\n        json: body,\n        isJSONText: isJSONText,\n        str: (window._$hasBigNumberJson ? json2 : JSON).stringify(\n            body,\n            null,\n            '    '\n          )\n      }\n      : '';\n  }\n  return data[JSON_KEY];\n}\n\nexports.getJson = getJson;\n\nexports.getJsonStr = function(data, isReq, decode) {\n  var json = getJson(data, isReq, decode);\n  return json && json.str;\n};\n\nfunction getBody(data, isReq) {\n  initData(data, isReq);\n  return data[BODY_KEY] || '';\n}\nexports.getBody = getBody;\n\nexports.getHex = function (data) {\n  initData(data);\n  return data[HEX_KEY] || '';\n};\n\nvar CHARSET_RE = /charset=([\\w-]+)/i;\nvar META_CHARSET_RE = /<meta\\s[^>]*\\bcharset=(?:'|\")?([\\w-]+)[^>]*>/i;\n\nfunction getCharset(res) {\n  var type = res.headers && res.headers['content-type'];\n  if (CHARSET_RE.test(type) || META_CHARSET_RE.test(getBody(res))) {\n    return RegExp.$1.toUpperCase();\n  }\n  return 'UTF8';\n}\n\nfunction getPreviewUrl(data) {\n  if (!data) {\n    return;\n  }\n  var res = data.res;\n  var type = getContentType(res.headers);\n  var isImg = type === 'IMG';\n  if (isImg || type === 'HTML') {\n    var url = data.url;\n    if (/^((?:http|ws)s?:)?\\/\\//i.test(url)) {\n      if (RegExp.$1) {\n        url = url.replace(/^ws/, 'http');\n      } else {\n        url = 'http:' + url;\n      }\n    } else {\n      url = 'http://' + url;\n    }\n    var charset = isImg ? 'UTF8' : getCharset(res);\n    url +=\n      (url.indexOf('?') === -1 ? '' : '&') +\n      '???WHISTLE_PREVIEW_CHARSET=' +\n      charset;\n    return url + '???#' + (isImg ? getBody(res) : res.base64);\n  }\n}\n\nexports.getPreviewUrl = getPreviewUrl;\n\nexports.openPreview = function (data) {\n  var url = getPreviewUrl(data);\n  url && window.open(url);\n};\n\nfunction parseRawJson(str, quite) {\n  try {\n    var json = JSON.parse(str);\n    if (json && typeof json === 'object') {\n      return json;\n    }\n    !quite && message.error('Error: invalid JSON format');\n  } catch (e) {\n    !quite && message.error('Error: ' + e.message);\n  }\n}\n\nexports.parseRawJson = parseRawJson;\n\nfunction parseHeaders(str) {\n  var headers = {};\n  str = str.split(CRLF_RE);\n  str.forEach(function (line) {\n    var index = line.indexOf(':');\n    var value = '';\n    if (index != -1) {\n      value = line.substring(index + 1).trim();\n      var name = line.substring(0, index).trim();\n      var list = headers[name];\n      if (list) {\n        if (!Array.isArray(list)) {\n          headers[name] = list = [list];\n        }\n        list.push(value);\n      } else {\n        headers[name] = value;\n      }\n    }\n  });\n  return headers;\n}\n\nexports.parseHeaders = function (str) {\n  str = typeof str === 'string' ? str.trim() : null;\n  if (!str) {\n    return {};\n  }\n  return parseRawJson(str, true) || parseHeaders(str);\n};\n\nfunction hasRequestBody(method) {\n  if (typeof method != 'string') {\n    return false;\n  }\n  method = method.toUpperCase();\n  return !(\n    method === 'GET' ||\n    method === 'HEAD' ||\n    method === 'OPTIONS' ||\n    method === 'CONNECT'\n  );\n}\n\nexports.hasRequestBody = hasRequestBody;\n\nvar NON_LATIN1_RE = /([^\\x00-\\xFF]|[\\r\\n%])/g;\nexports.encodeNonLatin1Char = function (str) {\n  /*eslint no-control-regex: \"off\"*/\n  return str && typeof str === 'string'\n    ? str.replace(NON_LATIN1_RE, safeEncodeURIComponent)\n    : '';\n};\n\nvar VER_LEN = 3;\n\nfunction compareVer(n1, n2, index) {\n  n1 = parseInt(n1, 10) || 0;\n  n2 = parseInt(n2, 10) || 0;\n  if (n1 === n2) {\n    return 0;\n  }\n  return n1 > n2 ? VER_LEN - index : index - VER_LEN;\n}\n\nexports.compareVersion = function(v1, v2) {\n  if (v1 === v2 || !v1 || typeof v1 !== 'string') {\n    return 0;\n  }\n  if (!v2 || typeof v2 !== 'string') {\n    return 3;\n  }\n  v1 = v1.split('.');\n  v2 = v2.split('.');\n\n  for (var i = 0; i < 3; i++) {\n    var flag = compareVer(v1[i], v2[i], i);\n    if (flag) {\n      return Math.max(flag, 0);\n    }\n  }\n  v1 = v1[2];\n  v2 = v2[2];\n  if (!v1 || !v2) {\n    return 0;\n  }\n  var i1 = v1.indexOf('-');\n  var i2 = v2.indexOf('-');\n  var test1 = i1 === -1 ? '' : v1.substring(i1);\n  var test2 = i2 === -1 ? '' : v2.substring(i2);\n  if (test1 === test2) {\n    return 0;\n  }\n  if (!test1 || !test2) {\n    return test1 ? 0 : 1;\n  }\n  return test1 > test2 ? 1 : 0;\n};\n\nfunction getHexLine(line) {\n  var index = line.indexOf('  ') + 2;\n  return line.substring(index, line.indexOf('  ', index)).trim();\n}\n\nexports.getHexText = function (text) {\n  if (!text) {\n    return '';\n  }\n  return text.split('\\n').map(getHexLine).join('\\n');\n};\n\nvar curPageName;\nfunction triggerPageChange(name) {\n  try {\n    var onPageChange = window.parent.onWhistlePageChange;\n    if (typeof onPageChange === 'function' && curPageName !== name) {\n      curPageName = name;\n      onPageChange(name, location.href);\n    }\n  } catch (e) {}\n}\n\nexports.triggerPageChange = triggerPageChange;\n\nvar curActiveRules;\nexports.triggerRulesActiveChange = function (name) {\n  if (curActiveRules === name) {\n    return;\n  }\n  curActiveRules = name;\n  try {\n    var onChange = window.parent.onWhistleRulesActiveChange;\n    if (typeof onChange === 'function') {\n      onChange(name, location.href);\n    }\n  } catch (e) {}\n};\nvar curActiveValues;\nexports.triggerValuesActiveChange = function (name) {\n  if (curActiveValues === name) {\n    return;\n  }\n  curActiveValues = name;\n  try {\n    var onChange = window.parent.onWhistleValuesActiveChange;\n    if (typeof onChange === 'function') {\n      onChange(name, location.href);\n    }\n  } catch (e) {}\n};\n\nfunction changePageName(name) {\n  var hash = location.hash.substring(1);\n  var index = hash.indexOf('?');\n  hash = index === -1 ? '' : hash.substring(index);\n  location.hash = name + hash;\n  triggerPageChange(name);\n}\n\nexports.changePageName = changePageName;\n\nexports.getTempName = function () {\n  return Date.now() + '' + Math.floor(Math.random() * 10000);\n};\n\nfunction readFile(file, callback, type) {\n  var reader = new FileReader();\n  var done;\n  var execCallback = function (err, result) {\n    if (done) {\n      return;\n    }\n    done = true;\n    if (err) {\n      reader.abort();\n      return win.alert(err.message);\n    }\n    callback(result);\n  };\n  var isText = type === 'text';\n  reader[isText ? 'readAsText' : 'readAsArrayBuffer'](file);\n  reader.onerror = execCallback;\n  reader.onabort = function () {\n    execCallback(new Error('Aborted'));\n  };\n  reader.onload = function () {\n    var result = reader.result;\n    try {\n      if (!isText) {\n        result = new window.Uint8Array(result);\n        result = type === 'base64' ? fromByteArray(result) : result;\n      }\n      execCallback(null, result);\n    } catch (e) {\n      execCallback(e);\n    }\n  };\n  return reader;\n}\n\nexports.readFile = readFile;\n\nexports.readFileAsBase64 = function (file, callback) {\n  return readFile(file, callback, 'base64');\n};\n\nexports.readFileAsText = function (file, callback) {\n  return readFile(file, callback, 'text');\n};\n\nexports.addPluginMenus = function (item, list, maxTop, disabled, treeId, url) {\n  var pluginsList = (item.list = list);\n  var count = pluginsList.length;\n  if (count) {\n    item.hide = false;\n    var disabledOthers = disabled;\n    var curUrl = treeId || url;\n    for (var j = 0; j < count; j++) {\n      var plugin = pluginsList[j];\n      var pattern = plugin._urlPattern;\n      if (plugin.required || plugin.requiredTreeNode) {\n        var disd = disabled && (!plugin.requiredTreeNode || !treeId);\n        if (!disd && (pattern && (!curUrl || !pattern.test(curUrl)))) {\n          disd = true;\n        }\n        plugin.disabled = disd;\n        if (!disd) {\n          disabledOthers = false;\n        }\n      } else if (pattern && (!curUrl || !pattern.test(curUrl))) {\n        plugin.disabled = true;\n      } else {\n        disabledOthers = plugin.disabled = false;\n      }\n    }\n    var top = count - 2;\n    item.top = top > 0 ? Math.min(maxTop, top) : undefined;\n    item.disabled = disabledOthers;\n  } else {\n    item.hide = true;\n  }\n};\n\nexports.getText = function(text) {\n  if (text && typeof text === 'object') {\n    try {\n      return JSON.stringify(text, null, '  ');\n    } catch (e) {}\n  }\n  return text == null ? '' : String(text);\n};\n\nfunction getKeys(obj) {\n  var list = obj[''];\n  var keys = Object.keys(obj);\n  list = list && (Array.isArray(list) ? list : Array.isArray(list.list) ? list.list : null);\n  if (!list) {\n    return keys;\n  }\n  delete obj[''];\n  var result = [];\n  list = list.concat(keys);\n  for (var i = 0, len = list.length; i < len; i++) {\n    var name = list[i];\n    if (notEStr(name) && (result.indexOf(name) === -1)) {\n      result.push(name);\n    }\n  }\n  return result;\n}\n\nexports.parseImportData = function (data, modal, isValues) {\n  var list = [];\n  var hasConflict;\n  var handleItem = function(name, value) {\n    if (value == null) {\n      return;\n    }\n    if (isValues) {\n      if (typeof value === 'object') {\n        try {\n          value = JSON.stringify(value, null, '  ');\n        } catch (e) {\n          return;\n        }\n      } else {\n        value = value + '';\n      }\n    }\n    if (typeof value !== 'string') {\n      return;\n    }\n    var isConflict;\n    var item = modal && modal.get(name);\n    if (item) {\n      isConflict = item.value && item.value != value;\n      hasConflict = hasConflict || isConflict;\n    }\n    list.push({\n      name: name,\n      value: value,\n      isConflict: isConflict\n    });\n  };\n  if (Array.isArray(data)) {\n    var map = {};\n    data.forEach(function (item) {\n      var name = item && item.name;\n      if (name && typeof name === 'string' && !map[name]) {\n        var value = isGroup(name) ? '' : (item.value == null ? item.content : item.value);\n        map[name] = 1;\n        handleItem(name, value);\n      }\n    });\n  } else {\n\n    getKeys(data).forEach(function (name) {\n      name && handleItem(name, data[name]);\n    });\n  }\n  list.hasConflict = hasConflict;\n  return list;\n};\n\nfunction getSize(size) {\n  if (!(size >= 1024)) {\n    return size;\n  }\n  size = (size / 1024).toFixed(2);\n  if (size < 1024) {\n    return size + 'k';\n  }\n  size = (size / 1024).toFixed(2);\n  if (size < 1024) {\n    return size + 'm';\n  }\n  return (size / 1024).toFixed(2) + 'G';\n}\n\nexports.getSize = getSize;\n\nfunction getQps(num) {\n  if (!num) {\n    return '0';\n  }\n  return (num / 100).toFixed(2);\n}\n\nexports.getQps = getQps;\n\nfunction indexOfList(list, subList, start) {\n  var len = list.length;\n  var subLen = subList && subList.length;\n  if (!len || !subLen) {\n    return -1;\n  }\n  var first = subList[0];\n  var index = list.indexOf(first, start || 0);\n  if (subLen === 1) {\n    return index;\n  }\n  var result = -1;\n  while (index !== -1) {\n    result = index;\n    for (var i = 0; i < subLen; i++) {\n      if (subList[i] !== list[i + index]) {\n        result = -1;\n        index = list.indexOf(first, index + 1);\n        break;\n      }\n    }\n    if (result !== -1) {\n      return result;\n    }\n  }\n  return result;\n}\n\nfunction concatByteArray(list1, list2, list3) {\n  var len = list1.length;\n  var len2 = list2.length;\n  var result = new window.Uint8Array(len + len2 + (list3 ? list3.length : 0));\n  result.set(list1);\n  result.set(list2, len);\n  list3 && result.set(list3, len + len2);\n  return result;\n}\n\nexports.toBase64 = toBase64;\n\nfunction strToByteArray(str) {\n  try {\n    str = toBase64(str);\n    return toByteArray(str);\n  } catch (e) {}\n  return null;\n}\n\nfunction base64ToByteArray(str) {\n  try {\n    return toByteArray(str);\n  } catch (e) {}\n  return null;\n}\n\nexports.base64ToByteArray = base64ToByteArray;\n\nvar UPLOAD_TYPE_RE = /^\\s*multipart\\//i;\nvar BOUNDARY_RE = /boundary=(?:\"([^\"]+)\"|([^;]+))/i;\nvar BODY_SEP = strToByteArray('\\r\\n\\r\\n');\nvar NAME_RE = /name=(?:\"([^\"]+)\"|([^;]+))/i;\nvar FILENAME_RE = /filename=(?:\"([^\"]+)\"|([^;]+))/i;\nvar TYPE_RE = /^\\s*content-type:\\s*([^\\s]+)/i;\nvar CRLF_BUF = strToByteArray('\\r\\n');\nvar EMPTY_BUF = strToByteArray('');\n\nexports.EMPTY_BUF = EMPTY_BUF;\n\nfunction parseMultiHeader(header) {\n  try {\n    header = base64Decode(fromByteArray(header)).split('\\r\\n');\n  } catch (e) {\n    return;\n  }\n  if (!NAME_RE.test(header[0])) {\n    return;\n  }\n  var result = {\n    name: RegExp.$1 || RegExp.$2,\n    value: ''\n  };\n  if (FILENAME_RE.test(header[0].replace(RegExp['$&'], '\\n'))) {\n    result.value = RegExp.$1 || RegExp.$2;\n  }\n  if (TYPE_RE.test(header[1])) {\n    result.type = RegExp.$1;\n  }\n  return result;\n}\n\nexports.isUploadForm = function (req) {\n  var type = req.headers && req.headers['content-type'];\n  return UPLOAD_TYPE_RE.test(type);\n};\n\nfunction getUploadName(header) {\n  var result = [];\n  if (header.value) {\n    result.push(header.value);\n  }\n  if (header.type) {\n    result.push(header.type);\n  }\n  result = result.join('; ');\n  return header.name + (result ? '\\r\\u0000(' + result + ')' : '');\n}\n\nfunction parseUploadBody(body, boundary, needObj) {\n  var sep = '--' + boundary;\n  var start = strToByteArray(sep + '\\r\\n');\n  var end = strToByteArray('\\r\\n' + sep);\n  var len = start.length;\n  var index = indexOfList(body, start);\n  var result = needObj ? {} : [];\n  while (index >= 0) {\n    index += len;\n    var hIndex = indexOfList(body, BODY_SEP, index - 2);\n    if (hIndex === -1) {\n      return result;\n    }\n    var endIndex = indexOfList(body, end, hIndex + 2);\n    if (endIndex === -1) {\n      return result;\n    }\n    var header = body.slice(index, hIndex);\n    hIndex += 4;\n    var data = hIndex >= endIndex ? '' : body.slice(hIndex, endIndex);\n    header = header && parseMultiHeader(header);\n    if (header) {\n      if (needObj) {\n        var name = getUploadName(header);\n        var curVal = header.value;\n        if (data) {\n          try {\n            curVal = base64Decode(fromByteArray(data)) || '';\n          } catch (e) {\n            curVal = '[Binary data]';\n          }\n        }\n        var value = result[name];\n        if (value != null) {\n          if (!Array.isArray(value)) {\n            value = result[name] = [ value ];\n          }\n          value.push(curVal);\n        } else {\n          result[name] = curVal;\n        }\n      } else {\n        if (header.type) {\n          header.data = data || EMPTY_BUF;\n        } else {\n          try {\n            header.value = data && base64Decode(fromByteArray(data));\n          } catch (e) {}\n        }\n        result.push(header);\n      }\n    }\n    index = indexOfList(body, start, endIndex + 2);\n  }\n\n  return result;\n}\n\nexports.parseUploadBody = function (req, needObj) {\n  if (!req.base64) {\n    return;\n  }\n  var type = req.headers && req.headers['content-type'];\n  if (!BOUNDARY_RE.test(type)) {\n    return;\n  }\n  var boundary = RegExp.$1 || RegExp.$2;\n  var body = base64ToByteArray(req.base64);\n  return body && parseUploadBody(body, boundary, needObj);\n};\n\nfunction getMultiPart(part) {\n  var header = 'Content-Disposition: form-data; name=\"' + part.name + '\"';\n  var data = part.data;\n  if (data) {\n    header += '; filename=\"' + part.value + '\"';\n    if (part.type) {\n      header += '\\r\\nContent-Type: ' + part.type;\n    }\n  } else {\n    data = part.value && strToByteArray(part.value);\n  }\n  if (!header) {\n    return;\n  }\n  header = strToByteArray(header + '\\r\\n\\r\\n');\n  return data ? concatByteArray(header, data, CRLF_BUF) : header;\n}\n\nfunction getBoundary() {\n  return (\n    '----WhistleUploadForm' +\n    Date.now().toString(16) +\n    Math.floor(Math.random() * 100000000000).toString(16)\n  );\n}\n\nvar base64abc = [\n  'A',\n  'B',\n  'C',\n  'D',\n  'E',\n  'F',\n  'G',\n  'H',\n  'I',\n  'J',\n  'K',\n  'L',\n  'M',\n  'N',\n  'O',\n  'P',\n  'Q',\n  'R',\n  'S',\n  'T',\n  'U',\n  'V',\n  'W',\n  'X',\n  'Y',\n  'Z',\n  'a',\n  'b',\n  'c',\n  'd',\n  'e',\n  'f',\n  'g',\n  'h',\n  'i',\n  'j',\n  'k',\n  'l',\n  'm',\n  'n',\n  'o',\n  'p',\n  'q',\n  'r',\n  's',\n  't',\n  'u',\n  'v',\n  'w',\n  'x',\n  'y',\n  'z',\n  '0',\n  '1',\n  '2',\n  '3',\n  '4',\n  '5',\n  '6',\n  '7',\n  '8',\n  '9',\n  '+',\n  '/'\n];\n\nfunction bytesToBase64(bytes) {\n  var result = '';\n  var i;\n  var l = bytes.length;\n  for (i = 2; i < l; i += 3) {\n    result += base64abc[bytes[i - 2] >> 2];\n    result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];\n    result += base64abc[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)];\n    result += base64abc[bytes[i] & 0x3f];\n  }\n  if (i === l + 1) {\n    // 1 octet yet to write\n    result += base64abc[bytes[i - 2] >> 2];\n    result += base64abc[(bytes[i - 2] & 0x03) << 4];\n    result += '==';\n  }\n  if (i === l) {\n    // 2 octets yet to write\n    result += base64abc[bytes[i - 2] >> 2];\n    result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];\n    result += base64abc[(bytes[i - 1] & 0x0f) << 2];\n    result += '=';\n  }\n  return result;\n}\n\nexports.bytesToBase64 = bytesToBase64;\n\nexports.getMultiBody = function (fields) {\n  var result;\n  var boundary = getBoundary();\n  var boundBuf = strToByteArray('--' + boundary + '\\r\\n');\n  fields.forEach(function (field) {\n    field = getMultiPart(field);\n    if (field) {\n      field = concatByteArray(boundBuf, field);\n      result = result ? concatByteArray(result, field) : field;\n    }\n  });\n  result =\n    result && concatByteArray(result, strToByteArray('--' + boundary + '--'));\n  return {\n    boundary: boundary,\n    length: result ? result.length : 0,\n    base64: result && bytesToBase64(result)\n  };\n};\n\nfunction padding(num) {\n  return num < 10 ? '0' + num : num;\n}\n\nfunction paddingMS(ms) {\n  if (ms > 99) {\n    return ms;\n  }\n  if (ms > 9) {\n    return '0' + ms;\n  }\n  return '00' + ms;\n}\n\nfunction formatDate(date) {\n  date = date || new Date();\n  var result = [];\n  result.push(date.getFullYear());\n  result.push(padding(date.getMonth() + 1));\n  result.push(padding(date.getDate()));\n  result.push(padding(date.getHours()));\n  result.push(padding(date.getMinutes()));\n  result.push(padding(date.getSeconds()));\n  result.push(paddingMS(date.getMilliseconds()));\n  return result.join('');\n}\n\nexports.formatDate = formatDate;\n\nexports.formatTime = function (time) {\n  time = Math.floor(time / 1000);\n  var sec = padding(time % 60);\n  time = Math.floor(time / 60);\n  var min = padding(time % 60);\n  time = Math.floor(time / 60);\n  var hour = padding(time % 24);\n  var day = Math.floor(time / 24);\n  return (day ? padding(day) + ' ' : '') + hour + ':' + min + ':' + sec;\n};\n\nvar EMPTY_OBJ = {};\n\nfunction parseResCookie(cookie) {\n  cookie = parseQueryString(cookie, /;\\s*/, null, null, true);\n  var result = {\n    httpOnly: false,\n    secure: false\n  };\n  for (var i in cookie) {\n    switch (i.toLowerCase()) {\n    case 'domain':\n      result.domain = cookie[i];\n      break;\n    case 'path':\n      result.path = cookie[i];\n      break;\n    case 'expires':\n      result.expires = cookie[i];\n      break;\n    case 'max-age':\n      result['max-age'] = cookie[i];\n      result.maxAge = cookie[i];\n      result.maxage = cookie[i];\n      break;\n    case 'httponly':\n      result.httpOnly = true;\n      result.httponly = true;\n      break;\n    case 'secure':\n      result.secure = true;\n      break;\n    case 'partitioned':\n      result.partitioned = true;\n      break;\n    case 'samesite':\n      result.sameSite = cookie[i];\n      result.samesite = cookie[i];\n      break;\n    default:\n      if (!result[0]) {\n        result.name = i;\n        result.value = cookie[i];\n      }\n    }\n  }\n\n  return result;\n}\n\nfunction objectToArray(obj, rawNames) {\n  var result = [];\n  if (obj) {\n    rawNames = rawNames || EMPTY_OBJ;\n    Object.keys(obj).forEach(function (name) {\n      var value = obj[name];\n      name = rawNames[name] || name;\n      if (Array.isArray(value)) {\n        value.forEach(function (val) {\n          result.push({\n            name: name,\n            value: val + ''\n          });\n        });\n      } else {\n        result.push({\n          name: name,\n          value: value + ''\n        });\n      }\n    });\n  }\n  return result;\n}\n\nfunction stringToArray(str, delimiter) {\n  str = parseQueryString(str, delimiter);\n  return objectToArray(str);\n}\n\nfunction getPostData(req) {\n  var headers = req.headers || '';\n  return {\n    size: req.unzipSize || req.size || -1,\n    mimeType: headers['content-type'] || 'none',\n    params: [],\n    text: ''\n  };\n}\n\nfunction toHarReq(item) {\n  var req = item.req;\n  var url = item.url;\n  var headers = req.headers || '';\n  var cookies = stringToArray(headers.cookie, /;\\s*/);\n  var index = url.indexOf('?');\n  var queryString = index === -1 ? [] : stringToArray(url.substring(index + 1));\n  var isForm = isUrlEncoded(req);\n  var postData;\n  if (isForm) {\n    var body = getBody(req, true);\n    postData = postData || getPostData(req);\n    postData.text = body;\n    postData.base64 = req.base64;\n    postData.params = stringToArray(body);\n  } else if (req.base64) {\n    postData = postData || getPostData(req);\n    postData.base64 = req.base64;\n    postData.text = getBody(req, true);\n  } else if (req.body) {\n    postData = postData || getPostData(req);\n    postData.text = req.body;\n  }\n  return {\n    method: item.method,\n    url: url,\n    ip: req.ip,\n    port: req.port,\n    httpVersion: item.useH2 ? 'HTTP/2.0' : 'HTTP/1.1',\n    cookies: cookies,\n    headers: objectToArray(headers, req.rawHeaderNames),\n    queryString: queryString,\n    postData: postData,\n    headersSize: -1,\n    bodySize: req.size || -1,\n    comment: ''\n  };\n}\n\nfunction toHarRes(item) {\n  var res = item.res;\n  var headers = res.headers || '';\n  var cookies = headers['set-cookie'];\n  if (cookies) {\n    if (Array.isArray(cookies)) {\n      cookies = cookies.map(parseResCookie);\n    } else {\n      cookies = [parseResCookie(cookies)];\n    }\n  } else {\n    cookies = [];\n  }\n  return {\n    statusCode: res.statusCode,\n    status: parseInt(res.statusCode, 10) || 0,\n    ip: res.ip,\n    port: res.port,\n    statusText: getStatusMessage(res),\n    httpVersion: item.useH2 ? 'HTTP/2.0' : 'HTTP/1.1',\n    cookies: cookies,\n    headers: objectToArray(headers, res.rawHeaderNames),\n    content: {\n      size: res.unzipSize || res.size || -1,\n      mimeType: headers['content-type'] || '',\n      base64: res.base64,\n      text: getBody(res)\n    },\n    redirectURL: headers.location || '',\n    headersSize: -1,\n    bodySize: res.size || -1,\n    comment: ''\n  };\n}\n\nexports.toHar = function (item) {\n  var time = -1;\n  var dns = -1;\n  var send = -1;\n  var receive = -1;\n  if (item.dnsTime >= item.startTime) {\n    dns = item.dnsTime - item.startTime;\n    time = dns;\n    if (item.requestTime >= item.dnsTime) {\n      send = item.requestTime - item.dnsTime;\n      if (item.responseTime >= item.requestTime) {\n        receive = item.responseTime - item.requestTime;\n        if (item.endTime >= item.responseTime) {\n          time = item.endTime - item.startTime;\n        } else {\n          time = item.responseTime - item.startTime;\n        }\n      } else {\n        time = item.requestTime - item.startTime;\n      }\n    }\n  }\n  return {\n    startedDateTime: new Date(item.startTime).toISOString(),\n    ttfb: item.ttfb,\n    time: time,\n    whistleCustomData: item.customData,\n    whistleRules: item.rules,\n    whistleFwdHost: item.fwdHost,\n    whistleSniPlugin: item.sniPlugin,\n    whistleVersion: item.version,\n    whistleNodeVersion: item.nodeVersion,\n    whistleRealUrl: item.realUrl,\n    whistleCaptureError: item.captureError,\n    whistleReqError: item.reqError,\n    whistleIsHttps: item.isHttps,\n    whistleResError: item.resError,\n    whistleTimes: {\n      startTime: item.startTime,\n      dnsTime: item.dnsTime,\n      requestTime: item.requestTime,\n      responseTime: item.responseTime,\n      endTime: item.endTime\n    },\n    request: toHarReq(item),\n    response: toHarRes(item),\n    frames: item.frames,\n    cache: {},\n    timings: {\n      blocked: 0,\n      dns: dns,\n      connect: -1,\n      send: send,\n      wait: -1,\n      receive: receive,\n      ssl: -1,\n      comment: ''\n    },\n    clientIPAddress: item.clientIp,\n    serverIPAddress: item.hostIp\n  };\n};\n\nexports.getUrl = function (url) {\n  return url && url.indexOf('/') === -1 ? 'tunnel://' + url : url;\n};\n\nfunction expandAll(node) {\n  if (node.children) {\n    node.expand = true;\n    node.pExpand = true;\n    node.children.forEach(expandAll);\n  }\n}\n\nexports.expandAll = expandAll;\n\nfunction collapseAll(node) {\n  if (node.children) {\n    node.expand = false;\n    node.pExpand = false;\n    node.children.forEach(collapseAll);\n  }\n}\n\nexports.collapseAll = collapseAll;\n\nfunction setPExpand(node, pExpand) {\n  if (node.children) {\n    node.pExpand = pExpand;\n    pExpand = node.expand && pExpand;\n    node.children.forEach(function (child) {\n      setPExpand(child, pExpand);\n    });\n  }\n}\n\nfunction expand(node) {\n  node.expand = true;\n  setPExpand(node, true);\n}\n\nfunction collapse(node) {\n  node.expand = false;\n  setPExpand(node, false);\n}\n\nexports.expand = expand;\nexports.collapse = collapse;\n\nvar PROTO_RE = /^((?:http|ws)s?:\\/\\/)[^/?]*/;\nexports.getRawUrl = function (item) {\n  return item.fwdHost && item.url.replace(PROTO_RE, '$1' + item.fwdHost);\n};\n\nfunction isGroup(name) {\n  return name && name[0] === '\\r';\n}\n\nexports.isGroup = isGroup;\n\nfunction checkKeyword(obj, str) {\n  if (obj.regexp) {\n    return setNot(obj.regexp.test(str), obj.not);\n  }\n  return setNot(str.toLowerCase().indexOf(obj.keyword) !== -1, obj.not);\n}\n\nfunction filterJson(obj, keyword, filterType) {\n  if (obj == null) {\n    return false;\n  }\n  var type = typeof obj;\n  var isKey = filterType === 1;\n  var isVal = filterType > 1;\n  if (type === 'string' || type === 'number' || type === 'boolean') {\n    return !isKey && checkKeyword(keyword, String(obj));\n  }\n  if (type !== 'object') {\n    return false;\n  }\n  if (Array.isArray(obj)) {\n    var idx = [];\n    for (var i = obj.length - 1; i >=0; i--) {\n      if ((isVal || !checkKeyword(keyword, i + '')) && !filterJson(obj[i], keyword, filterType)) {\n        obj.splice(i, 1);\n      } else {\n        idx.push(i);\n      }\n    }\n    obj._idx = idx.reverse();\n    return obj.length;\n  }\n  Object.keys(obj).forEach(function(key) {\n    var hasKey = !isVal && checkKeyword(keyword, key);\n    if (isKey && hasKey) {\n      return true;\n    }\n    if (!filterJson(obj[key], keyword, filterType) && !hasKey) {\n      delete obj[key];\n    }\n  });\n  return Object.keys(obj).length;\n}\n\nexports.filterJsonText = function(str, keyword, filterType) {\n  var obj;\n  if (keyword) {\n    if (!keyword.not && !checkKeyword(keyword, str)) {\n      return {};\n    }\n    obj = JSON.parse(str);\n    filterJson(obj, keyword, filterType);\n  }\n  return obj;\n};\n\n\nvar URL_RE = /^(?:([a-z0-9.+-]+:)?\\/\\/)?([^/?#]+)(\\/[^?#]*)?(\\?[^#]*)?(#.*)?$/i;\nvar HOST_RE = /^(.+)(?::(\\d*))$/;\nvar BRACKET_RE = /^\\[|\\]$/g;\n\nexports.parseUrl = function (url) {\n  if (!URL_RE.test(url)) {\n    return;\n  }\n  var protocol = RegExp.$1 || 'http:';\n  var host = RegExp.$2;\n  var pathname = RegExp.$3 || '/';\n  var search = RegExp.$4;\n  var hash = RegExp.$5 || null;\n  var port = null;\n  var hostname = host;\n  if (HOST_RE.test(host)) {\n    hostname = RegExp.$1;\n    port = RegExp.$2;\n  }\n\n  return {\n    protocol: protocol,\n    slashes: true,\n    auth: null,\n    host: host,\n    port: port,\n    hostname: hostname.replace(BRACKET_RE, ''),\n    hash: hash,\n    search: search || null,\n    query: search ? search.substring(1) : null,\n    pathname: pathname,\n    path: pathname + search,\n    href: url\n  };\n};\n\nexports.replacQuery = function(url, query) {\n  var index = url.indexOf('#');\n  var hash = '';\n  if (index !== -1) {\n    hash = url.substring(index);\n    url = url.substring(0, index);\n  }\n  if (query) {\n    query = '?' + query;\n  }\n  index = url.indexOf('?');\n  if (index !== -1) {\n    url = url.substring(0, index);\n  }\n  return url + query + hash;\n};\n\nexports.getDisplaySize = function(size, unzipSize) {\n  unzipSize = size == unzipSize ? '' : getSize(unzipSize);\n  size = getSize(size);\n  return unzipSize ? size + ' / ' + unzipSize : size;\n};\n\nfunction formatSize(value) {\n  return value >= 1024 ? value + ' (' + getSize(value) + ')' : value;\n}\n\nexports.formatSize = function(size, unzipSize) {\n  var value = formatSize(size);\n  if (size && unzipSize >= 0 && unzipSize != size) {\n    value += ' / ' + formatSize(unzipSize);\n    value += (unzipSize ? ' = ' + Number((size * 100) / unzipSize).toFixed(2) + '%' : '');\n  }\n  return value;\n};\n\n\nexports.getTabIcon = function (tab) {\n  return tab.icon && getPluginCgiUrl(tab.plugin, tab.icon);\n};\n\nexports.getPluginIcon = function (plugin, name) {\n  var icon = plugin && plugin[name || 'favicon'];\n  return icon && getPluginCgiUrl(plugin.moduleName, icon);\n};\n\nvar IMPORT_URL_RE = /[?&#]data(?:_url|Url)=([^&#]+)(?:&|#|$)/;\nexports.getDataUrl = function() {\n  var result = IMPORT_URL_RE.exec(location.href);\n  return result && decodeURIComponentSafe(result[1]).trim();\n};\n\nfunction getSimplePluginName(plugin) {\n  var name = typeof plugin === 'string' ? plugin : plugin.moduleName;\n  return name.substring(name.lastIndexOf('.') + 1);\n}\n\nexports.getSimplePluginName = getSimplePluginName;\n\nexports.showJSONDialog = function(data, keyPath) {\n  var str = data && JSON.stringify(data);\n  if (str) {\n    events.trigger('showJsonViewDialog', [str, Array.isArray(keyPath) ? keyPath : null]);\n  }\n};\n\nexports.handleFormat = function(e, onFormat) {\n  if (e.shiftKey && e.keyCode === 70  && (e.metaKey || e.ctrlKey)) {\n    onFormat(e);\n    e.preventDefault();\n  }\n};\n\nexports.handleTab = function(e) {\n  var target = e.target;\n  if (e.keyCode !== 9 || e.altKey || target.readOnly || target.disabled) {\n    return;\n  }\n  e.preventDefault();\n  var start = target.selectionStart;\n  var end = target.selectionEnd;\n  var value = target.value;\n  target.value = value.substring(0, start) + '  ' + value.substring(end);\n  target.selectionStart = target.selectionEnd = start + 2;\n};\n\nfunction getPluginCgiUrl(moduleName, url) {\n  if (/^(?:https?:\\/\\/|data:image\\/)/.test(url)) {\n    return url;\n  }\n  moduleName = getSimplePluginName(moduleName);\n  var pluginName = 'plugin.' + moduleName;\n  if (url.indexOf('whistle.' + moduleName) === 0 || url.indexOf(pluginName) === 0) {\n    return url;\n  }\n  return pluginName + '/' + url;\n}\n\nexports.getPluginCgiUrl = getPluginCgiUrl;\n\nexports.showHandlePluginInfo = function(data, xhr) {\n  if (!data) {\n    showSystemError(xhr);\n    return false;\n  }\n  if (data.ec) {\n    return message.error(data.em || 'Request error, please try again!');\n  }\n  message.success('Request successful - plugin list updating...');\n  return true;\n};\n\nexports.getDialogTitle = function(name, action) {\n  action = action || 'Import';\n  switch (name) {\n  case 'network':\n    return action + ' Network Sessions';\n  case 'networkSettings':\n    return action + ' Network Settings';\n  case 'composer':\n    return action + ' Composer Data';\n  case 'console':\n    return action + ' Console Logs';\n  case 'server':\n    return action + ' Server Logs';\n  case 'rules':\n    return action + ' Rules';\n  case 'rulesSettings':\n    return action + ' Rules Settings';\n  case 'values':\n    return action + ' Values';\n  case 'valuesSettings':\n    return action + ' Values Settings';\n  case 'mock':\n    return action + ' Mock Data';\n  default:\n    return '';\n  }\n};\n\nvar CONTROL_RE =\n  /[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c]+/g;\nvar MULTI_LINE_VALUE_RE =\n  /^[^\\n\\r\\S]*(```+)[^\\n\\r\\S]*(\\S+)[^\\n\\r\\S]*[\\r\\n](?:([\\s\\S]*?)[\\r\\n])??[^\\n\\r\\S]*\\1\\s*$/gm;\n\nfunction resolveInlineValues(str, values, rawValues) {\n  str = str && str.replace(CONTROL_RE, '').trim();\n  if (!str || str.indexOf('```') === -1) {\n    return str;\n  }\n  return str.replace(MULTI_LINE_VALUE_RE, function (all, _, key, value) {\n    if (values && values[key] == null) {\n      values[key] = value || '';\n      if (rawValues) {\n        rawValues[key] = all.trim();\n      }\n    }\n    return '';\n  });\n}\n\nexports.resolveInlineValues = resolveInlineValues;\n\nvar MULTI_TO_ONE_RE = /^\\s*line`\\s*[\\r\\n]([\\s\\S]*?)[\\r\\n]\\s*`\\s*?$/gm;\nvar LINE_END_RE = /\\s*[\\r\\n]+\\s*/;\n\nfunction removeRulesComments(str) {\n  return !str || str.indexOf('#') === -1 ? str : str.replace(COMMENT_RE, '').trim();\n}\n\nexports.removeRulesComments = removeRulesComments;\n\nfunction mergeLines(str) {\n  return str.replace(MULTI_TO_ONE_RE, function(_, line) {\n    return line.replace(SPACE_RE, ' ');\n  });\n}\n\nexports.formatRules = function (str, values, rawValues) {\n  str = resolveInlineValues(str, values, rawValues);\n  str = removeRulesComments(str);\n  str = mergeLines(str);\n  return str.trim().split(LINE_END_RE);\n};\n\nexports.handleClickLocate = function(text) {\n  var index = text && text.indexOf(SOURCE_SEP);\n  if (index > 0) {\n    text = text.substring(index + SOURCE_SEP_LEN, text.length - 1);\n    index = text.indexOf(':');\n    var type = text.substring(0, index);\n    var name = text.substring(index + 1).trim();\n    if (!type || !name) {\n      if (text === 'Service Rules') {\n        showService('serviceRules', true);\n      } else if (text === 'Mock Rules') {\n        showService('mockRules', true);\n      }\n      return;\n    }\n    if (type === 'File') {\n      events.trigger('showRules', name);\n    } else if (type === 'Plugin') {\n      events.trigger('showPlugins', name);\n    }\n  }\n};\n\nfunction getDocUrl(url) {\n  var path = /^zh-/i.test(navigator.language || navigator.userLanguage) ? '/' : '/en/';\n  return 'https://wproxy.org' + path + (url ? 'docs/' : '') + (url || '');\n}\n\nexports.getDocUrl = getDocUrl;\n\nexports.UPDATE_URL = getDocUrl('faq.html#update');\n\nexports.shakeElem = function (elem) {\n  if (elem.hasClass('w-shake-horizontal')) {\n    return;\n  }\n  elem.addClass('w-shake-horizontal');\n  setTimeout(function() {\n    elem.removeClass('w-shake-horizontal');\n  }, 500);\n};\n\nvar INVALID_NAME_RE = /[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c'<>:\"\\\\/|?*]+/g;\nvar BLANK_RE = /\\s+/g;\nvar START_SPACE_RE = /^\\s+/;\n\nexports.formatFilename = function(name) {\n  return name.replace(INVALID_NAME_RE, '').replace(START_SPACE_RE, '').replace(BLANK_RE, ' ');\n};\n\nvar shortcutsSettings;\ntry {\n  shortcutsSettings = $.extend({}, JSON.parse(storage.get('shortcutsSettings')) || {});\n} catch (e) {}\n\nshortcutsSettings = shortcutsSettings || {};\nexports.shortcutsSettings = shortcutsSettings;\n\nexports.saveShortcutsSettings = function() {\n  storage.set('shortcutsSettings', JSON.stringify(shortcutsSettings));\n};\n\nvar EDITOR_SHORTCUTS = ['switchTabReverse', 'switchTab'];\n\nexports.hasShortcut = function(name) {\n  if (EDITOR_SHORTCUTS.indexOf(name) !== -1) {\n    var active = document.activeElement;\n    var nodeName = active && active.nodeName;\n    if ((nodeName === 'INPUT' || nodeName === 'TEXTAREA' || active && active.isContentEditable)) {\n      return false;\n    }\n  }\n  return shortcutsSettings[name] !== false;\n};\n\nexports.noModal = function() {\n  return !$('.modal.in').length;\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/view-inspector.js",
    "content": "var React = require('react');\nvar events = require('./events');\n\nvar ViewInspector = React.createClass({\n  getInitialState: function () {\n    return { visible: false };\n  },\n  showInspectors: function () {\n    events.trigger('setActiveSession', this.props.reqId);\n    events.trigger('showInspectors');\n  },\n  shouldComponentUpdate: function (nextProps, nextState) {\n    var reqId = nextProps.reqId;\n    var isChanged = this.props.reqId !== reqId;\n    if (isChanged) {\n      events.trigger('checkViewInspectors', reqId);\n    }\n    return isChanged || this.state.visible !== nextState.visible;\n  },\n  componentDidMount: function() {\n    events.on('showViewInspectorsBtn', (_, visible) => {\n      this.setState({ visible: visible });\n    });\n    events.trigger('checkViewInspectors', this.props.reqId);\n  },\n  render: function() {\n    if (!this.state.visible || !this.props.reqId) {\n      return null;\n    }\n    return <a className=\"w-view-inspectors-btn\" onClick={this.showInspectors}>View server-received request data</a>;\n  }\n});\n\nmodule.exports = ViewInspector;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/win.js",
    "content": "var $ = require('jquery');\n\nvar alertDialog;\nvar confirmDialog;\nvar handleConfirm;\n\nfunction createAlert() {\n  if (!alertDialog) {\n    alertDialog = $(\n      '<div class=\"modal fade w-win-dialog\" tabindex=\"-1\" role=\"dialog\">' +\n        '<div class=\"modal-dialog\" role=\"document\">' +\n        '<div class=\"modal-content\">' +\n        '<div class=\"modal-body\">' +\n        '<pre class=\"alert alert-danger\"></pre>' +\n        '</div>' +\n        '<div class=\"modal-footer\">' +\n        '<button type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>' +\n        '<button type=\"button\" class=\"btn btn-primary w-copy-text-with-tips\" data-dismiss=\"modal\"></button>' +\n        '</div>' +\n        '</div>' +\n        '</div>' +\n        '</div>'\n    );\n  }\n  return alertDialog;\n}\n\nfunction createConfirm() {\n  if (!confirmDialog) {\n    confirmDialog = $(\n      '<div class=\"modal fade w-win-dialog\" tabindex=\"-1\" role=\"dialog\">' +\n        '<div class=\"modal-dialog\" role=\"document\">' +\n        '<div class=\"modal-content\">' +\n        '<div class=\"modal-body\">' +\n        '<pre></pre>' +\n        '</div>' +\n        '<div class=\"modal-footer\">' +\n        '<button type=\"button\" class=\"btn btn-default w-win-cancel\" data-dismiss=\"modal\">Cancel</button>' +\n        '<button type=\"button\" class=\"btn btn-danger w-win-delete-all\" data-dismiss=\"modal\">Delete All</button>' +\n        '<button type=\"button\" class=\"btn btn-primary w-win-confirm\" data-dismiss=\"modal\">Confirm</button>' +\n        '</div>' +\n        '</div>' +\n        '</div>' +\n        '</div>'\n    );\n    confirmDialog.on('click', '.w-win-cancel', function () {\n      if (typeof handleConfirm === 'function') {\n        handleConfirm(false);\n      }\n      handleConfirm = null;\n    });\n    confirmDialog.on('click', '.w-win-delete-all', function () {\n      if (typeof handleConfirm === 'function') {\n        handleConfirm(2);\n      }\n      handleConfirm = null;\n    });\n    confirmDialog.on('click', '.w-win-confirm', function () {\n      if (typeof handleConfirm === 'function') {\n        handleConfirm(1);\n      }\n      handleConfirm = null;\n    });\n  }\n  return confirmDialog;\n}\n\nfunction mockAlert(msg, copyText, btnText) {\n  createAlert();\n  alertDialog.find('pre').text(msg);\n  alertDialog.modal('show');\n  var btn = alertDialog.find('.w-copy-text-with-tips');\n  if (copyText && typeof copyText === 'string') {\n    btn.text(btnText || 'Copy');\n    btn.show().attr('data-clipboard-text', copyText);\n  } else {\n    btn.hide();\n  }\n}\n\nfunction mockConfirm(msg, cb, removeAllBtn, flag) {\n  createConfirm();\n  if (confirmDialog.is(':visible')) {\n    return;\n  }\n  confirmDialog.find('.w-win-delete-all')[removeAllBtn ? 'show' : 'hide']();\n  handleConfirm = cb;\n  confirmDialog.find('pre').text(msg);\n  confirmDialog.modal('show');\n  if (flag) {\n    confirmDialog.attr('data-confirm-flag', flag);\n  } else {\n    confirmDialog.removeAttr('data-confirm-flag');\n  }\n}\n\nexports.alert = mockAlert;\nexports.confirm = mockConfirm;\n"
  },
  {
    "path": "biz/webui/htdocs/src/js/workers.js",
    "content": "var events = require('./events');\nvar util = require('./util');\n\nvar workers = {};\nvar modal;\n\nfunction isEnd(item) {\n  return item.endTime || item.lost;\n}\n\nfunction assign(o1, o2) {\n  if (typeof Object.assign === 'function') {\n    Object.assign(o1, o2);\n  } else {\n    Object.keys(o2).forEach(function(key) {\n      o1[key] = o2[key];\n    });\n  }\n}\n\nfunction getString(str) {\n  if (!str || typeof str !== 'string') {\n    return;\n  }\n  return str;\n}\n\nfunction setCustomData(item, newData, name) {\n  var data = item.customData || {};\n  item.customData = data;\n  assign(data, newData);\n  var style = data.style;\n  if (style) {\n    var color = getString(style.color);\n    var fontStyle = getString(style.fontStyle);\n    var backgroundColor = getString(style.backgroundColor || style.bgColor);\n    if (color || fontStyle || backgroundColor) {\n      style = item.style || {};\n      item.style = style;\n      style.color = style.color || color;\n      style.fontStyle = style.fontStyle || fontStyle;\n      style.backgroundColor = style.backgroundColor || backgroundColor;\n    }\n  }\n  if (util.notEStr(data.appName)) {\n    item.appName = util.getPluginCgiUrl(name, data.appName);\n  }\n}\n\nfunction setWorker(id) {\n  if (workers[id] || !window.Worker) {\n    return;\n  }\n  var worker = new Worker('web-worker.js?id=' + id);\n  var _destroy = function() {\n    if (timer) {\n      clearTimeout(timer);\n      timer = null;\n      destroy(worker);\n    }\n  };\n  var timer = setTimeout(_destroy, 16000);\n  worker.onerror = _destroy;\n  worker.onmessage = function(e) {\n    var data = e.data;\n    if (data === true) {\n      if (timer) {\n        clearTimeout(timer);\n        timer = null;\n        modal.list.forEach(function(item) {\n          if (isEnd(item)) {\n            worker.postMessage(item);\n          }\n        });\n        workers[id] = worker;\n      }\n      return;\n    }\n    if (data && data.id && data.data) {\n      var item = modal.getItem(data.id);\n      if (item) {\n        setCustomData(item, data.data, data.plugin);\n        events.trigger('updateUIThrottle');\n      }\n    }\n  };\n}\n\nfunction destroy(worker) {\n  worker.terminate();\n  worker.onmessage = null;\n  worker.onerror = null;\n  worker.onmessageerror = null;\n}\n\nfunction removeWorker(id) {\n  var worker = workers[id];\n  if (worker) {\n    delete workers[id];\n    destroy(worker);\n  }\n}\n\nexports.updateWorkers = function(list) {\n  Object.keys(workers).forEach(function(id) {\n    if (list.indexOf(id) === -1) {\n      removeWorker(id);\n    }\n  });\n  list.forEach(setWorker);\n};\n\nexports.postMessage = function(item) {\n  if (isEnd(item)) {\n    Object.keys(workers).forEach(function(key) {\n      workers[key].postMessage(item);\n    });\n  }\n};\n\nexports.setup = function(m) {\n  modal = m;\n};\n"
  },
  {
    "path": "biz/webui/htdocs/src/webpack.config.js",
    "content": "var webpack = require('webpack');\nvar path = require('path');\n\nmodule.exports = {\n  entry: {\n    index: path.join(__dirname, './js/index'),\n    decode: path.join(__dirname, './js/decode')\n  },\n  output: {\n    path: path.join(__dirname, '../js'),\n    filename: '[name].js'\n  },\n  module: {\n    loaders: [\n      {\n        test: /\\.js$/,\n        exclude: /node_modules/,\n        loader: 'babel-loader'\n      },\n      {\n        test: /\\.css$/,\n        loader: 'style-loader!css-loader'\n      },\n      {\n        test: /\\.(png|woff|woff2|eot|ttf|svg)$/,\n        loader: 'url-loader?limit=1000000'\n      }\n    ]\n  },\n  plugins: [\n    new webpack.DefinePlugin({\n      'process.env': {\n        NODE_ENV: '\"production\"'\n      }\n    }),\n    new webpack.optimize.UglifyJsPlugin({\n      compress: {\n        warnings: false\n      }\n    })\n  ]\n};\n"
  },
  {
    "path": "biz/webui/htdocs.js",
    "content": "var path = require('path');\nvar ROOT = path.join(__dirname, 'htdocs');\n\nfunction getHtmlFile(file) {\n  return path.join(ROOT, file || '');\n}\n\nexports.getHtmlFile = getHtmlFile;\n\nfunction getImgFile(file) {\n  return path.join(ROOT, 'img', file || '');\n}\n\nexports.getImgFile = getImgFile;\n\nfunction getJsFile(file) {\n  return path.join(ROOT, 'js', file || '');\n}\n\nexports.getJsFile = getJsFile;\n"
  },
  {
    "path": "biz/webui/lib/index.js",
    "content": "var express = require('express');\nvar app = express();\nvar path = require('path');\nvar http = require('http');\nvar https = require('https');\nvar parseReqUrl = require('parseurl');\nvar bodyParser = require('body-parser');\nvar fs = require('fs');\nvar zlib = require('zlib');\nvar extend = require('extend');\nvar LRU = require('lru-cache');\nvar htdocs = require('../htdocs');\nvar handleWeinreReq = require('../../weinre');\nvar setProxy = require('./proxy');\nvar rulesUtil = require('../../../lib/rules/util');\nvar getRootCAFile = require('../../../lib/https/ca').getRootCAFile;\nvar config = require('../../../lib/config');\nvar cgiUtil = require('../cgi-bin/util');\nvar common = require('../../../lib/util/common');\nvar getWorker = require('../../../lib/plugins/util').getWorker;\nvar loadAuthPlugins = require('../../../lib/plugins').loadAuthPlugins;\nvar parseUrl = require('../../../lib/util/parse-url-safe');\n\nvar sendError = cgiUtil.sendError;\nvar sendGzipText = cgiUtil.sendGzipText;\nvar parseAuth = common.parseAuth;\nvar createHash = common.createHash;\nvar PARSE_CONF = { extended: true, limit: '3mb'};\nvar UPLOAD_PARSE_CONF = { extended: true, limit: '30mb'};\nvar PLUGIN_NAMES = new LRU({ max: 360 });\nvar urlencodedParser = bodyParser.urlencoded(PARSE_CONF);\nvar jsonParser = bodyParser.json(PARSE_CONF);\nvar uploadUrlencodedParser = bodyParser.urlencoded(UPLOAD_PARSE_CONF);\nvar uploadJsonParser = bodyParser.json(UPLOAD_PARSE_CONF);\nvar GET_METHOD_RE = /^get$/i;\nvar WEINRE_RE = /^\\/weinre\\/.*/;\nvar ALLOW_PLUGIN_PATHS = ['/cgi-bin/rules/list2', '/cgi-bin/values/list2', '/cgi-bin/get-custom-certs-info'];\nvar SPECIAL_PATHS = ['/cgi-bin/rules/project'];\nvar DONT_CHECK_PATHS = ['/cgi-bin/server-info', '/cgi-bin/plugins/is-enable', '/cgi-bin/plugins/get-plugins',\n  '/preview.html', '/cgi-bin/rootca', '/cgi-bin/check-update', '/cgi-bin/log/set', '/cgi-bin/status'];\nvar GUEST_PATHS = ['/cgi-bin/composer', '/cgi-bin/socket/data', '/cgi-bin/abort', '/cgi-bin/socket/abort',\n  '/cgi-bin/socket/change-status', '/cgi-bin/sessions/export'];\nvar CORS_PATHS = ['/cgi-bin/status',  '/cgi-bin/rootca'];\nvar PLUGIN_PATH_RE = /^\\/(whistle|plugin)\\.([^/?#]+)(\\/)?/;\nvar STATIC_SRC_RE = /\\.(?:ico|js|css|png)$/i;\nvar UPLOAD_URLS = ['/cgi-bin/values/upload', '/cgi-bin/composer', '/cgi-bin/download'];\nvar ALLOW_CROSS_URLS = ['/cgi-bin/top', '/cgi-bin/status', '/cgi-bin/server-info',\n    '/cgi-bin/rootca', '/cgi-bin/check-update', '/cgi-bin/log/set'];\nvar proxyEvent, util, pluginMgr;\nvar MAX_AGE = 60 * 60 * 24 * 3;\nvar MENU_HTML = fs.readFileSync(path.join(__dirname, '../../../assets/menu.html'));\nvar INSPECTOR_HTML = fs.readFileSync(path.join(__dirname, '../../../assets/tab.html'));\nvar MODAL_HTML = fs.readFileSync(path.join(__dirname, '../../../assets/modal.html'));\nvar MENU_URL = '???_WHISTLE_PLUGIN_EXT_CONTEXT_MENU_' + config.port + '???';\nvar INSPECTOR_URL = '???_WHISTLE_PLUGIN_INSPECTOR_TAB_' + config.port + '???';\nvar KEY_RE_G = /\\${[^{}\\s]+}|{\\S+}/g;\nvar COMMENT_RE = /#[^\\r\\n]*$/mg;\n\nfunction sendToService(req, res) {\n  proxyEvent.loadService(function(err, options) {\n    if (err) {\n      common.sendRes(res, 500, err.stack || err);\n    } else {\n      util.transformReq(req, res, options.port);\n    }\n  });\n}\n\nfunction doNotCheckLogin(req) {\n  var path = req.path;\n  return STATIC_SRC_RE.test(path) || DONT_CHECK_PATHS.indexOf(path) !== -1;\n}\n\nfunction getUsername() {\n  return config.username || '';\n}\n\nfunction getPassword() {\n  return config.password || '';\n}\n\nfunction parseCookie(str) {\n  var result = {};\n  str && str.split(';').forEach(function(pair) {\n    var index = pair.indexOf('=');\n    if (index === -1) {\n      return;\n    }\n    var key = pair.substring(0, index).trim();\n    var val = pair.substring(index + 1).trim();\n    if (result[key] == null) {\n      try {\n        result[key] = decodeURIComponent(val);\n      } catch (e) {\n        result[key] = val;\n      }\n    }\n  });\n  return result;\n}\n\nfunction getLoginKey (req, res, auth) {\n  var ip = util.getClientIp(req);\n  var password = auth.password;\n  if (config.encrypted) {\n    password = createHash(password);\n  }\n  return createHash([auth.username, password, ip].join('\\n'));\n}\n\nfunction requireLogin(req, res, msg) {\n  if (config.client) {\n    if (config.handleWebReq) {\n      return config.handleWebReq(req, res);\n    }\n    return res.status(404).end();\n  }\n  res.setHeader('WWW-Authenticate', ' Basic realm=User Login');\n  res.setHeader('Content-Type', 'text/html; charset=utf8');\n  res.status(401).end(msg || 'Access denied, please <a href=\"javascript:;\" onclick=\"location.reload()\">try again</a>');\n}\n\nfunction equalAuth(auth, src) {\n  return auth.name === src.username && auth.pass === src.password;\n}\n\nfunction verifyLogin(req, res, auth) {\n  var isGuest = !auth;\n  if (isGuest) {\n    auth = config.guest;\n    if (!auth) {\n      return;\n    }\n    if (!auth.authKey) {\n      auth.authKey = 'whistle_v_lk_' + encodeURIComponent(auth.username);\n    }\n  }\n  var username = auth.username;\n  var password = auth.password;\n\n  if (!username && !password) {\n    return true;\n  }\n  var authKey = auth.authKey;\n  var cookies = parseCookie(req.headers.cookie);\n  var lkey = cookies[authKey];\n  var correctKey = getLoginKey(req, res, auth);\n  if (correctKey === lkey) {\n    return true;\n  }\n  var headerAuth = parseAuth(req.headers.authorization || req.headers['proxy-authorization']);\n  var queryAuth = parseAuth(req.query.authorization);\n  if (!isGuest && config.encrypted) {\n    headerAuth.pass = headerAuth.pass && createHash(headerAuth.pass);\n    queryAuth.pass = queryAuth.pass && createHash(queryAuth.pass);\n  }\n  if (equalAuth(headerAuth, auth) || equalAuth(queryAuth, auth)) {\n    res.setHeader('Set-Cookie', [\n      authKey + '=' + util.encodeURIComponent(correctKey),\n      'Max-Age=' + MAX_AGE,\n      'Path=/',\n      'Expires=' + new Date(Date.now() + (MAX_AGE * 1000)).toUTCString()\n    ].join('; '));\n    return true;\n  }\n}\n\nfunction checkAuth(req, res) {\n  var username = getUsername();\n  var auth = {\n    authKey: 'whistle_lk_' + encodeURIComponent(username),\n    username: username,\n    password: getPassword()\n  };\n  if (verifyLogin(req, res, auth)) {\n    return true;\n  }\n  if (config.specialAuth && SPECIAL_PATHS.indexOf(req.path) !== -1 &&\n    config.specialAuth === req.headers['x-whistle-special-auth']) {\n    return true;\n  }\n  requireLogin(req, res);\n  return false;\n}\n\napp.disable('x-powered-by');\n\nfunction readRemoteStream(req, res, authUrl) {\n  var client;\n  var handleError = function(err) {\n    res.emit('error', err);\n    client && client.destroy();\n  };\n  if (authUrl[0] === 'f') {\n    var stream = fs.createReadStream(authUrl.substring(7));\n    stream.on('error', handleError);\n    return stream.pipe(res);\n  }\n  var options = parseUrl(authUrl);\n  options.rejectUnauthorized = false;\n  var httpModule = options.protocol === 'https:' ? https : http;\n  var headers = extend({}, req.headers);\n  delete headers.host;\n  options.headers = headers;\n  client = httpModule.request(options, function(svrRes) {\n    svrRes.on('error', handleError);\n    res.writeHead(svrRes.statusCode, svrRes.headers);\n    svrRes.pipe(res);\n  });\n  client.on('error', handleError);\n  client.end();\n}\n\nfunction injectFile(req, res, filepath, prepend, append, isJs) {\n  fs.readFile(filepath, function(err, ctn) {\n    if (err) {\n      return sendError(res, err);\n    }\n    sendGzipText(req, res, {\n      'Content-Type': (isJs ? 'application/javascript' : 'text/html') + '; charset=utf-8'\n    }, concat(prepend, ctn, append));\n  });\n}\n\napp.use(function(req, res, next) {\n  proxyEvent.emit('_request', req.url);\n  var aborted;\n  var abort = function() {\n    if (!aborted) {\n      aborted = true;\n      res.destroy();\n    }\n  };\n  req.on('error', abort);\n  res.on('error', abort).on('close', abort);\n  loadAuthPlugins(req, function(status, msg, authUrl) {\n    if (!status && !authUrl) {\n      return next();\n    }\n    res.set('x-server', 'whistle');\n    res.set('x-module', 'webui');\n    if (!msg && !authUrl) {\n      return res.redirect(status);\n    }\n    if (status === 401) {\n      return requireLogin(req, res, msg);\n    }\n    res.set('Content-Type', 'text/html; charset=utf8');\n    if (authUrl) {\n      return readRemoteStream(req, res, authUrl);\n    }\n    if (status === 502) {\n      return res.status(502).end(msg || 'Error');\n    }\n    res.status(403).end(msg || 'Forbidden');\n  });\n});\n\nif (typeof config.uiMiddleware === 'function') {\n  app.use(config.uiMiddleware);\n}\n\napp.use(function(req, res, next) {\n  var referer = req.headers.referer;\n  var options = parseReqUrl(req);\n  var path = options.path;\n  var index = path.indexOf('/whistle/');\n  req.url = path;\n  if (index === 0) {\n    delete req.headers.referer;\n    req.url = path.substring(8);\n  } else if (PLUGIN_PATH_RE.test(options.pathname)) {\n    var pluginName = RegExp['$&'];\n    var len = pluginName.length - 1;\n    if (len === index) {\n      delete req.headers.referer;\n      req.url = path.substring(len + 8);\n    }\n  } else {\n    pluginName = config.getPluginNameByHost(req.headers.host);\n    if (!pluginName) {\n      if (referer) {\n        var refOpts = parseUrl(referer);\n        var pathname = refOpts.pathname;\n        if (PLUGIN_PATH_RE.test(pathname) && RegExp.$3) {\n          var name = RegExp.$2;\n          req.url = '/' + RegExp.$1 + '.' + name + path;\n          PLUGIN_NAMES.set(path, name);\n        } else {\n          pluginName = config.getPluginNameByHost(refOpts.hostname);\n        }\n      } else {\n        pluginName = PLUGIN_NAMES.get(path);\n      }\n    }\n    if (pluginName) {\n      req.url = '/plugin.' + pluginName + path;\n    }\n  }\n\n  next();\n});\n\napp.use(function(req, res, next) {\n  if (req.headers.host !== 'rootca.pro') {\n    return next();\n  }\n  var type = 'cer';\n  if (!req.path.indexOf('/crt')) {\n    type = 'crt';\n  } else if (!req.path.indexOf('/pem')) {\n    type = 'pem';\n  }\n  res.download(getRootCAFile(), 'rootCA.' + type);\n});\n\nfunction isAllowHost(host) {\n  var list = config.allowOrigin;\n  if (list) {\n    for (var i = 0, len = list.length; i < len; i++) {\n      var h = list[i];\n      if (typeof h === 'string' ? host === h : h.test(host)) {\n        return true;\n      }\n    }\n  }\n}\n\nfunction checkInternalPath(req) {\n  if (config.allowAllOrigin || ALLOW_CROSS_URLS.indexOf(req.path) !== -1) {\n    return true;\n  }\n  var host = req.headers[common.ORIGIN_HOST_HEADER];\n  return !host || isAllowHost(host);\n}\n\nfunction checkAllowOrigin(req) {\n  var host = req.headers.origin;\n  if (!host || typeof host !== 'string' ||\n    req.headers['sec-fetch-site'] === 'same-origin') {\n    return false;\n  }\n  if (config.allowAllOrigin) {\n    return true;\n  }\n  if (CORS_PATHS.indexOf(req.path) !== -1) {\n    return true;\n  }\n  if (!config.allowOrigin) {\n    return false;\n  }\n  var index = host.indexOf('://');\n  if (index !== -1) {\n    host = host.substring(index + 3);\n  }\n  host = util.parseHost(host)[0];\n  return host && isAllowHost(host);\n}\n\nfunction cgiHandler(req, res) {\n  if (common.existsUpPath(req.path) || !checkInternalPath(req)) {\n    return res.status(403).end('Forbidden');\n  }\n  if (checkAllowOrigin(req)) {\n    res.setHeader('access-control-allow-origin', req.headers.origin);\n    res.setHeader('access-control-allow-credentials', true);\n  }\n  var filepath = path.join(__dirname, '..' + req.path) + '.js';\n  var handleResponse = function() {\n    try {\n      require(filepath)(req, res);\n    } catch(err) {\n      sendError(res, err);\n    }\n  };\n  if (require.cache[filepath]) {\n    return handleResponse();\n  }\n  common.getStat(filepath, function(err, stat) {\n    if (err || !stat.isFile()) {\n      var notFound = err ? err.code === 'ENOENT' : !stat.isFile();\n      var msg;\n      if (config.debugMode) {\n        msg = util.THEME_STYLE + '<pre>' + (err ? util.encodeHtml(util.getErrorStack(err)) : 'No such File') + '</pre>';\n      } else {\n        msg = notFound ? 'Not Found' : 'Internal Server Error';\n      }\n      return common.sendRes(res, notFound ? 404 : 500, msg);\n    }\n    handleResponse();\n  });\n}\napp.all('/service/*', sendToService);\napp.all('/cgi-bin/service/*', sendToService);\napp.all('/cgi-bin/sessions/*', sendToService);\napp.post('/cgi-bin/plugins/install', sendToService);\napp.all('/favicon.ico', function(req, res) {\n  res.sendFile(htdocs.getImgFile('favicon.ico'));\n});\n\nfunction readPluginPage(req, res, plugin, html, config) {\n  res.type('html');\n  res.write('<!DOCTYPE html>\\n');\n  res.write(config);\n  res.write(html);\n  var index = req.path.indexOf('/', 1);\n  if (index === -1) {\n    res.end();\n  } else {\n    var filepath = req.path.substring(index + 1);\n    var reader = fs.createReadStream(path.join(plugin.path, filepath));\n    reader.on('error', function() {\n      if (reader) {\n        reader = null;\n        res.end();\n      }\n    });\n    reader.pipe(res);\n  }\n}\n\napp.all(PLUGIN_PATH_RE, function(req, res) {\n  var result = PLUGIN_PATH_RE.exec(req.url);\n  var type = result[1];\n  var name = result[2];\n  var slash = result[3];\n  var plugin = type === 'whistle' ? pluginMgr.getPlugin(name + ':')\n    : pluginMgr.getPluginByName(name);\n  if (!plugin) {\n    return res.status(404).send('Not Found');\n  }\n  if (req.url.indexOf(MENU_URL) !== -1) {\n    return readPluginPage(req, res, plugin, MENU_HTML, plugin[util.PLUGIN_MENU_CONFIG]);\n  }\n  if (req.url.indexOf(INSPECTOR_URL) !== -1) {\n    return readPluginPage(req, res, plugin, INSPECTOR_HTML, plugin[util.PLUGIN_INSPECTOR_CONFIG]);\n  }\n  if (req.headers[util.INTERNAL_ID_HEADER] === util.INTERNAL_ID) {\n    delete req.headers[util.INTERNAL_ID_HEADER];\n  } else if (plugin.inheritAuth && !checkAuth(req, res)) {\n    return;\n  }\n  if (!slash) {\n    return res.redirect(type + '.' + name + '/');\n  }\n  pluginMgr.loadPlugin(plugin, function(err, ports) {\n    if (err || !ports.uiPort) {\n      return common.sendRes(res, err ? 500 : 404, err ? '<pre>' + util.encodeHtml(err) + '</pre>' : 'Not Found');\n    }\n    var options = parseReqUrl(req);\n    var headers = req.headers;\n    headers[config.PLUGIN_HOOK_NAME_HEADER] = config.PLUGIN_HOOKS.UI;\n    req.url = options.path.replace(result[0].slice(0, -1), '');\n    var openInModal = req.url.indexOf('?openInModal=5b6af7b9884e1165') !== -1;\n    util.transformReq(req, res, ports.uiPort, null, openInModal ? MODAL_HTML : null);\n  });\n});\n\napp.use(function(req, res, next) {\n  var pathname = req.path;\n  if (ALLOW_PLUGIN_PATHS.indexOf(pathname) !== -1) {\n    var name = req.headers[config.PROXY_ID_HEADER];\n    if (name) {\n      return pluginMgr.getPlugin(name + ':') ? next() : res.sendStatus(403);\n    }\n  }\n  if (doNotCheckLogin(req)) {\n    return next();\n  }\n  if (config.disableWebUI && !config.debugMode && (!config.captureData || pathname !== '/cgi-bin/get-data')) {\n    return res.status(404).end('Not Found');\n  }\n  if (config.authKey && config.authKey === req.headers['x-whistle-auth-key']) {\n    return next();\n  }\n  var guestAuthKey = config.guestAuthKey;\n  if (((guestAuthKey && guestAuthKey === req.headers['x-whistle-guest-auth-key'])\n    || verifyLogin(req, res)) && (!req.method || GET_METHOD_RE.test(req.method)\n    || WEINRE_RE.test(pathname) || GUEST_PATHS.indexOf(pathname) !== -1)) {\n    return next();\n  }\n  if (checkAuth(req, res)) {\n    next();\n  }\n});\n\napp.post('/cgi-bin/composer', function(req, res) {\n  req.headers[config.CLIENT_IP_HEADER] = util.getClientIp(req);\n  req.headers[config.CLIENT_PORT_HEADER] = util.getClientPort(req);\n  sendToService(req, res);\n});\napp.get('/cgi-bin/compose-data', sendToService);\napp.all('/cgi-bin/saved/*', sendToService);\napp.all('/cgi-bin/temp/*', sendToService);\napp.get('/cgi-bin/history', sendToService);\nfunction sendText(req, res, text) {\n  sendGzipText(req, res, {\n    'Content-Type': 'text/plain; charset=utf-8'\n  }, typeof text === 'string' ? text : '');\n}\n\nfunction parseKey(key) {\n  return key[0] === '$' ? key.slice(2, -1) : key.slice(1, -1);\n}\n\nif (config.handleUpdate) {\n  app.post('/cgi-bin/update', config.handleUpdate);\n}\n\napp.get('/rules', function(req, res) {\n  var query = req.query;\n  var name = query.name || query.key;\n  if (name === 'Default') {\n    name = rulesUtil.rules.getDefault();\n  } else if (name) {\n    name = rulesUtil.rules.get(name);\n  } else {\n    name = rulesUtil.rules.getRawRulesText();\n  }\n  if (name && query.values !== 'false' && !(query.values <= 0)) {\n    var keys = name.replace(COMMENT_RE, '').match(KEY_RE_G);\n    if (keys) {\n      keys = keys.map(parseKey).map(function (key) {\n        return util.wrapRuleValue(key, rulesUtil.values.get(key), query.values, query.policy);\n      }).join('');\n      if (keys) {\n        name += '\\n' + keys;\n      }\n    }\n  }\n  sendText(req, res, name);\n});\n\napp.get('/values', function(req, res) {\n  var name = req.query.name || req.query.key;\n  sendText(req, res, rulesUtil.values.get(name));\n});\n\napp.get('/web-worker.js', urlencodedParser, function(req, res) {\n  var body = getWorker(req.query.id);\n  if (!body) {\n    return res.status(404).end('Not Found');\n  }\n  sendGzipText(req, res, {\n    'Content-Type': 'application/javascript; charset=utf-8'\n  }, body);\n});\n\napp.all('/cgi-bin/*', function(req, res, next) {\n  req.isUploadReq = UPLOAD_URLS.indexOf(req.path) !== -1;\n  return req.isUploadReq ? uploadUrlencodedParser(req, res, next) : urlencodedParser(req, res, next);\n}, function(req, res, next) {\n  return req.isUploadReq ? uploadJsonParser(req, res, next) : jsonParser(req, res, next);\n}, cgiHandler);\n\napp.use('/preview.html', function(req, res, next) {\n  if (req.headers[util.INTERNAL_ID_HEADER] !== util.INTERNAL_ID) {\n    return res.status(404).end('Not Found');\n  }\n  next();\n  var index = req.path.indexOf('=') + 1;\n  if (index) {\n    var charset = req.path.substring(index);\n    res.set('content-type', 'text/html;charset=' + charset);\n  }\n});\n\nvar uiExt = config.uiExt || {};\nvar htmlPrepend = uiExt.htmlPrepend;\nvar htmlAppend = uiExt.htmlAppend;\nvar jsPrepend = uiExt.jsPrepend;\nvar jsAppend = uiExt.jsAppend;\nvar htmlFile = htdocs.getHtmlFile('index.html');\nvar jsFile = htdocs.getJsFile('index.js');\n\nfunction concat(a, b, c) {\n  if (!a && !c) {\n    return b;\n  }\n  var list = [];\n  a && list.push(a);\n  list.push(b);\n  c && list.push(c);\n  return Buffer.concat(list);\n}\n\nif (!config.debugMode) {\n  var indexHtml = concat(htmlPrepend, fs.readFileSync(htmlFile), htmlAppend);\n  var indexJs = concat(jsPrepend, fs.readFileSync(jsFile), jsAppend);\n  var jsETag = createHash(indexJs);\n  var gzipIndexHtml = zlib.gzipSync(indexHtml);\n  var gzipIndexJs = zlib.gzipSync(indexJs);\n  app.use('/js/index.js', function(req, res) {\n    if (req.headers['if-none-match'] === jsETag) {\n      return res.sendStatus(304);\n    }\n    var headers = {\n      'Content-Type': 'application/javascript; charset=utf-8',\n      'Cache-Control': 'public, max-age=300',\n      ETag: jsETag\n    };\n    sendGzipText(req, res, headers, indexJs, gzipIndexJs);\n  });\n  var sendIndex = function(req, res) {\n    sendGzipText(req, res, {\n      'Content-Type': 'text/html; charset=utf-8'\n    }, indexHtml, gzipIndexHtml);\n  };\n  app.get('/', sendIndex);\n  app.get('/index.html', sendIndex);\n} else {\n  if (htmlPrepend || htmlAppend) {\n    var injectHtml = function(req, res) {\n      injectFile(req, res, htmlFile, htmlPrepend, htmlAppend);\n    };\n    app.get('/', injectHtml);\n    app.get('/index.html', injectHtml);\n  }\n  if (jsPrepend || jsAppend) {\n    app.get('/js/index.js', function(req, res) {\n      injectFile(req, res, jsFile, jsPrepend, jsAppend, true);\n    });\n  }\n}\n\n\napp.all(WEINRE_RE, function(req, res) {\n  var options = parseReqUrl(req);\n  if (options.pathname === '/weinre/client') {\n    return res.redirect('client/' + (options.search || ''));\n  }\n  req.url = options.path.replace('/weinre', '');\n  handleWeinreReq(req, res);\n});\n\nfunction init(proxy) {\n  if (proxyEvent) {\n    return;\n  }\n  proxyEvent = proxy;\n  pluginMgr = proxy.pluginMgr;\n  util = proxy.util;\n  setProxy(proxy);\n}\n\napp.use(express.static(path.join(__dirname, '../htdocs'), {maxAge: 600000}));\n\nexports.init = init;\nexports.setupServer = function(server) {\n  server.on('request', app);\n};\nmodule.exports.handleRequest = app;\n"
  },
  {
    "path": "biz/webui/lib/proxy.js",
    "content": "module.exports = function(proxy) {\n  module.exports = proxy;\n};\n"
  },
  {
    "path": "biz/weinre/index.js",
    "content": "var fork = require('pfork').fork;\nvar path = require('path');\nvar util = require('../../lib/util');\nvar config = require('../../lib/config');\n\nvar options = {\n  script: path.join(__dirname, 'server.js'),\n  debugMode: config.debugMode\n};\n\nmodule.exports = function(req, res) {\n  fork(options,\n    function(err, options) {\n      if (err) {\n        util.sendRes(res, 500, err.stack || err);\n      } else {\n        util.transformReq(req, res, options.port);\n      }\n    }\n  );\n};\n"
  },
  {
    "path": "biz/weinre/server.js",
    "content": "var getServer = require('hagent').getServer;\nvar startWeinre = require('weinre2').run;\n\nmodule.exports = function (_, callback) {\n  getServer(function (server, port) {\n    startWeinre({\n      server: server,\n      verbose: false,\n      debug: false,\n      readTimeout: 5,\n      deathTimeout: 15\n    });\n    callback(null, { port: port });\n  });\n};\n\n\n"
  },
  {
    "path": "docs/.vitepress/config.mts",
    "content": "import { defineConfig } from 'vitepress';\n\n// https://vitepress.dev/reference/site-config\nexport default defineConfig({\n  title: \"Whistle\",\n  description: \"HTTP, HTTP2, HTTPS, Websocket debugging proxy\",\n  head: [\n    ['link', { rel: 'icon', href: '/img/favicon.ico' }],\n    ['style', {}, `\n      .VPFooter {\n        padding-bottom: 20px;\n        text-align: center;\n      }\n      .VPFooter a {\n        color: var(--vp-c-text-2);\n        font-size: 12px;\n      }\n    `]],\n  locales: {\n    root: {\n      label: '简体中文',\n      lang: 'zh'\n    },\n    en: {\n      label: 'English',\n      lang: 'en',\n      link: '/en/',\n      themeConfig: {\n        search: {\n          provider: 'local',\n        },\n        nav: [\n          { text: 'Home', link: '/en/' },\n          { text: 'Documentation', link: '/en/docs/' },\n        ],\n        sidebar: [\n          { text: 'Installation', link: '/en/docs/' },\n          { text: 'Getting Started', link: '/en/docs/getting-started' },\n          { text: 'Mobile Settings', link: '/en/docs/mobile' },\n          {\n            text: 'UI Features',\n            collapsed: false,\n            items: [\n              { text: 'Network', link: '/en/docs/gui/network' },\n              { text: 'HTTPS', link: '/en/docs/gui/https' },\n              { text: 'Weinre', link: '/en/docs/gui/weinre' },\n              { text: 'Console/Log', link: '/en/docs/gui/console' },\n              { text: 'Online', link: '/en/docs/gui/online' },\n              { text: 'Composer', link: '/en/docs/gui/composer' },\n              { text: 'Rules', link: '/en/docs/gui/rules' },\n              { text: 'Values', link: '/en/docs/gui/values' },\n              { text: 'Plugins', link: '/en/docs/gui/plugins' },\n              { text: 'Shortcut', link: '/en/docs/gui/shortcut' },\n            ]\n          },\n          {\n            text: 'Rule Configuration',\n            collapsed: false,\n            items: [\n              { text: 'Rule Syntax', link: '/en/docs/rules/rule' },\n              { text: 'Pattern', link: '/en/docs/rules/pattern' },\n              { text: 'Operation', link: '/en/docs/rules/operation' },\n              { text: 'Filters', link: '/en/docs/rules/filters' },\n              { text: 'Protocols', link: '/en/docs/rules/protocols' },\n              { text: '@', link: '/en/docs/rules/@' },\n              { text: '%', link: '/en/docs/rules/plugin-vars' },\n              {\n                text: 'Map Local',\n                collapsed: false,\n                items: [\n                  { text: 'file', link: '/en/docs/rules/file' },\n                  { text: 'xfile', link: '/en/docs/rules/xfile' },\n                  { text: 'tpl', link: '/en/docs/rules/tpl' },\n                  { text: 'xtpl', link: '/en/docs/rules/xtpl' },\n                  { text: 'rawfile', link: '/en/docs/rules/rawfile' },\n                  { text: 'xrawfile', link: '/en/docs/rules/xrawfile' },\n                ]\n              },\n              {\n                text: 'Map Remote',\n                collapsed: false,\n                items: [\n                  { text: 'https', link: '/en/docs/rules/https' },\n                  { text: 'http', link: '/en/docs/rules/http' },\n                  { text: 'wss', link: '/en/docs/rules/wss' },\n                  { text: 'ws', link: '/en/docs/rules/ws' },\n                  { text: 'tunnel', link: '/en/docs/rules/tunnel' },\n                  { text: 'Protocol inheritance', link: '/en/docs/rules/inherit' },\n                ]\n              },\n              {\n                text: 'DNS Spoofing',\n                collapsed: false,\n                items: [\n                  { text: 'host', link: '/en/docs/rules/host' },\n                  { text: 'xhost', link: '/en/docs/rules/xhost' },\n                  { text: 'proxy (http-proxy)', link: '/en/docs/rules/proxy' },\n                  { text: 'xproxy (xhttp-proxy)', link: '/en/docs/rules/xproxy' },\n                  { text: 'https-proxy', link: '/en/docs/rules/https-proxy' },\n                  { text: 'xhttps-proxy', link: '/en/docs/rules/xhttps-proxy' },\n                  { text: 'socks', link: '/en/docs/rules/socks' },\n                  { text: 'xsocks', link: '/en/docs/rules/xsocks' },\n                  { text: 'pac', link: '/en/docs/rules/pac' },\n                ]\n              },\n              {\n                text: 'Rewrite Request',\n                collapsed: false,\n                items: [\n                  { text: 'urlParams', link: '/en/docs/rules/urlParams' },\n                  { text: 'pathReplace', link: '/en/docs/rules/pathReplace' },\n                  { text: 'sniCallback', link: '/en/docs/rules/sniCallback' },\n                  { text: 'method', link: '/en/docs/rules/method' },\n                  { text: 'tlsOptions', link: '/en/docs/rules/cipher' },\n                  { text: 'reqHeaders', link: '/en/docs/rules/reqHeaders' },\n                  { text: 'forwardedFor', link: '/en/docs/rules/forwardedFor' },\n                  { text: 'ua', link: '/en/docs/rules/ua' },\n                  { text: 'auth', link: '/en/docs/rules/auth' },\n                  { text: 'cache', link: '/en/docs/rules/cache' },\n                  { text: 'referer', link: '/en/docs/rules/referer' },\n                  { text: 'reqType', link: '/en/docs/rules/reqType' },\n                  { text: 'reqCharset', link: '/en/docs/rules/reqCharset' },\n                  { text: 'reqCookies', link: '/en/docs/rules/reqCookies' },\n                  { text: 'reqCors', link: '/en/docs/rules/reqCors' },\n                  { text: 'reqBody', link: '/en/docs/rules/reqBody' },\n                  { text: 'reqMerge', link: '/en/docs/rules/reqMerge' },\n                  { text: 'reqPrepend', link: '/en/docs/rules/reqPrepend' },\n                  { text: 'reqAppend', link: '/en/docs/rules/reqAppend' },\n                  { text: 'reqReplace', link: '/en/docs/rules/reqReplace' },\n                  { text: 'reqWrite', link: '/en/docs/rules/reqWrite' },\n                  { text: 'reqWriteRaw', link: '/en/docs/rules/reqWriteRaw' },\n                  { text: 'reqRules', link: '/en/docs/rules/reqRules' },\n                  { text: 'reqScript', link: '/en/docs/rules/reqScript' },\n                ]\n              },\n              {\n                text: 'Rewrite Reponse',\n                collapsed: false,\n                items: [\n                  { text: 'statusCode', link: '/en/docs/rules/statusCode' },\n                  { text: 'replaceStatus', link: '/en/docs/rules/replaceStatus' },\n                  { text: 'redirect', link: '/en/docs/rules/redirect' },\n                  { text: 'locationHref', link: '/en/docs/rules/locationHref' },\n                  { text: 'resHeaders', link: '/en/docs/rules/resHeaders' },\n                  { text: 'responseFor', link: '/en/docs/rules/responseFor' },\n                  { text: 'resType', link: '/en/docs/rules/resType' },\n                  { text: 'resCharset', link: '/en/docs/rules/resCharset' },\n                  { text: 'resCookies', link: '/en/docs/rules/resCookies' },\n                  { text: 'resCors', link: '/en/docs/rules/resCors' },\n                  { text: 'attachment', link: '/en/docs/rules/attachment' },\n                  { text: 'resBody', link: '/en/docs/rules/resBody' },\n                  { text: 'resMerge', link: '/en/docs/rules/resMerge' },\n                  { text: 'resPrepend', link: '/en/docs/rules/resPrepend' },\n                  { text: 'resAppend', link: '/en/docs/rules/resAppend' },\n                  { text: 'resReplace', link: '/en/docs/rules/resReplace' },\n                  { text: 'htmlPrepend', link: '/en/docs/rules/htmlPrepend' },\n                  { text: 'htmlBody', link: '/en/docs/rules/htmlBody' },\n                  { text: 'htmlAppend', link: '/en/docs/rules/htmlAppend' },\n                  { text: 'cssPrepend', link: '/en/docs/rules/cssPrepend' },\n                  { text: 'cssBody', link: '/en/docs/rules/cssBody' },\n                  { text: 'cssAppend', link: '/en/docs/rules/cssAppend' },\n                  { text: 'jsPrepend', link: '/en/docs/rules/jsPrepend' },\n                  { text: 'jsBody', link: '/en/docs/rules/jsBody' },\n                  { text: 'jsAppend', link: '/en/docs/rules/jsAppend' },\n                  { text: 'trailers', link: '/en/docs/rules/trailers' },\n                  { text: 'resWrite', link: '/en/docs/rules/resWrite' },\n                  { text: 'resWriteRaw', link: '/en/docs/rules/resWriteRaw' },\n                  { text: 'resRules', link: '/en/docs/rules/resRules' },\n                  { text: 'resScript', link: '/en/docs/rules/resScript' },\n                  { text: 'frameScript', link: '/en/docs/rules/frameScript' },\n                ]\n              },\n              {\n                text: 'General',\n                collapsed: false,\n                items: [\n                  { text: 'pipe', link: '/en/docs/rules/pipe' },\n                  { text: 'delete', link: '/en/docs/rules/delete' },\n                  { text: 'headerReplace', link: '/en/docs/rules/headerReplace' },\n                ]\n              },\n              {\n                text: 'Throttle',\n                collapsed: false,\n                items: [\n                  { text: 'reqDelay', link: '/en/docs/rules/reqDelay' },\n                  { text: 'resDelay', link: '/en/docs/rules/resDelay' },\n                  { text: 'reqSpeed', link: '/en/docs/rules/reqSpeed' },\n                  { text: 'resSpeed', link: '/en/docs/rules/resSpeed' },\n                ]\n              },\n              {\n                text: 'Tools',\n                collapsed: false,\n                items: [\n                  { text: 'weinre', link: '/en/docs/rules/weinre' },\n                  { text: 'log', link: '/en/docs/rules/log' },\n                ]\n              },\n              {\n                text: 'Settings',\n                collapsed: false,\n                items: [\n                  { text: 'style', link: '/en/docs/rules/style' },\n                  { text: 'enable', link: '/en/docs/rules/enable' },\n                  { text: 'disable', link: '/en/docs/rules/disable' },\n                  { text: 'lineProps', link: '/en/docs/rules/lineProps' },\n                ]\n              },\n              {\n                text: 'Filters',\n                collapsed: false,\n                items: [\n                  { text: 'excludeFilter', link: '/en/docs/rules/excludeFilter' },\n                  { text: 'includeFilter', link: '/en/docs/rules/includeFilter' },\n                  { text: 'ignore', link: '/en/docs/rules/ignore' },\n                  { text: 'skip', link: '/en/docs/rules/skip' },\n                ]\n              },\n            ],\n          },\n          {\n            text: 'Extensions',\n            collapsed: false,\n            items: [\n              { text: 'Plugin Usage', link: '/en/docs/extensions/usage' },\n              { text: 'Plugin Development', link: '/en/docs/extensions/dev' },\n              { text: 'NPM Modules', link: '/en/docs/extensions/npm' }\n            ]\n          },\n          { text: 'CLI', link: '/en/docs/cli' },\n          { text: 'FAQ', link: '/en/docs/faq' },\n        ],\n      }\n    }\n  },\n  themeConfig: {\n    // https://vitepress.dev/reference/default-theme-config\n    search: {\n      provider: 'local',\n    },\n    nav: [\n      { text: '首页', link: '/' },\n      { text: '帮助文档', link: '/docs/' },\n    ],\n\n    sidebar: [\n      { text: '安装', link: '/docs/' },\n      { text: '快速上手', link: '/docs/getting-started' },\n      { text: '移动端抓包', link: '/docs/mobile' },\n      {\n        text: '界面功能',\n        collapsed: false,\n        items: [\n          { text: 'Network', link: '/docs/gui/network' },\n          { text: 'HTTPS', link: '/docs/gui/https' },\n          { text: 'Weinre', link: '/docs/gui/weinre' },\n          { text: 'Console', link: '/docs/gui/console' },\n          { text: 'Online', link: '/docs/gui/online' },\n          { text: 'Composer', link: '/docs/gui/composer' },\n          { text: 'Rules', link: '/docs/gui/rules' },\n          { text: 'Values', link: '/docs/gui/values' },\n          { text: 'Plugins', link: '/docs/gui/plugins' },\n          { text: '快捷键', link: '/docs/gui/shortcut' },\n        ]\n      },\n      {\n        text: '规则配置',\n        collapsed: false,\n        items: [\n          { text: '规则语法', link: '/docs/rules/rule' },\n          { text: '匹配模式（pattern）', link: '/docs/rules/pattern' },\n          { text: '操作指令（operation）', link: '/docs/rules/operation' },\n          { text: '过滤器（filters）', link: '/docs/rules/filters' },\n          { text: '协议列表', link: '/docs/rules/protocols' },\n          { text: '@', link: '/docs/rules/@' },\n          { text: '%', link: '/docs/rules/plugin-vars' },\n          {\n            text: 'Map Local',\n            collapsed: false,\n            items: [\n              { text: 'file', link: '/docs/rules/file' },\n              { text: 'xfile', link: '/docs/rules/xfile' },\n              { text: 'tpl', link: '/docs/rules/tpl' },\n              { text: 'xtpl', link: '/docs/rules/xtpl' },\n              { text: 'rawfile', link: '/docs/rules/rawfile' },\n              { text: 'xrawfile', link: '/docs/rules/xrawfile' },\n            ]\n          },\n          {\n            text: 'Map Remote',\n            collapsed: false,\n            items: [\n              { text: 'https', link: '/docs/rules/https' },\n              { text: 'http', link: '/docs/rules/http' },\n              { text: 'wss', link: '/docs/rules/wss' },\n              { text: 'ws', link: '/docs/rules/ws' },\n              { text: 'tunnel', link: '/docs/rules/tunnel' },\n               { text: '协议继承', link: '/docs/rules/inherit' },\n            ]\n          },\n          {\n            text: 'DNS Spoofing',\n            collapsed: false,\n            items: [\n              { text: 'host', link: '/docs/rules/host' },\n              { text: 'xhost', link: '/docs/rules/xhost' },\n              { text: 'proxy (http-proxy)', link: '/docs/rules/proxy' },\n              { text: 'xproxy (xhttp-proxy)', link: '/docs/rules/xproxy' },\n              { text: 'https-proxy', link: '/docs/rules/https-proxy' },\n              { text: 'xhttps-proxy', link: '/docs/rules/xhttps-proxy' },\n              { text: 'socks', link: '/docs/rules/socks' },\n              { text: 'xsocks', link: '/docs/rules/xsocks' },\n              { text: 'pac', link: '/docs/rules/pac' },\n            ]\n          },\n          {\n            text: 'Rewrite Request',\n            collapsed: false,\n            items: [\n              { text: 'urlParams', link: '/docs/rules/urlParams' },\n              { text: 'pathReplace', link: '/docs/rules/pathReplace' },\n              { text: 'sniCallback', link: '/docs/rules/sniCallback' },\n              { text: 'method', link: '/docs/rules/method' },\n              { text: 'tlsOptions', link: '/docs/rules/cipher' },\n              { text: 'reqHeaders', link: '/docs/rules/reqHeaders' },\n              { text: 'forwardedFor', link: '/docs/rules/forwardedFor' },\n              { text: 'ua', link: '/docs/rules/ua' },\n              { text: 'auth', link: '/docs/rules/auth' },\n              { text: 'cache', link: '/docs/rules/cache' },\n              { text: 'referer', link: '/docs/rules/referer' },\n              { text: 'reqType', link: '/docs/rules/reqType' },\n              { text: 'reqCharset', link: '/docs/rules/reqCharset' },\n              { text: 'reqCookies', link: '/docs/rules/reqCookies' },\n              { text: 'reqCors', link: '/docs/rules/reqCors' },\n              { text: 'reqBody', link: '/docs/rules/reqBody' },\n              { text: 'reqMerge', link: '/docs/rules/reqMerge' },\n              { text: 'reqPrepend', link: '/docs/rules/reqPrepend' },\n              { text: 'reqAppend', link: '/docs/rules/reqAppend' },\n              { text: 'reqReplace', link: '/docs/rules/reqReplace' },\n              { text: 'reqWrite', link: '/docs/rules/reqWrite' },\n              { text: 'reqWriteRaw', link: '/docs/rules/reqWriteRaw' },\n              { text: 'reqRules', link: '/docs/rules/reqRules' },\n              { text: 'reqScript', link: '/docs/rules/reqScript' },\n            ]\n          },\n          {\n            text: 'Rewrite Reponse',\n            collapsed: false,\n            items: [\n              { text: 'statusCode', link: '/docs/rules/statusCode' },\n              { text: 'replaceStatus', link: '/docs/rules/replaceStatus' },\n              { text: 'redirect', link: '/docs/rules/redirect' },\n              { text: 'locationHref', link: '/docs/rules/locationHref' },\n              { text: 'resHeaders', link: '/docs/rules/resHeaders' },\n              { text: 'responseFor', link: '/docs/rules/responseFor' },\n              { text: 'resType', link: '/docs/rules/resType' },\n              { text: 'resCharset', link: '/docs/rules/resCharset' },\n              { text: 'resCookies', link: '/docs/rules/resCookies' },\n              { text: 'attachment', link: '/docs/rules/attachment' },\n              { text: 'resCors', link: '/docs/rules/resCors' },\n              { text: 'resBody', link: '/docs/rules/resBody' },\n              { text: 'resMerge', link: '/docs/rules/resMerge' },\n              { text: 'resPrepend', link: '/docs/rules/resPrepend' },\n              { text: 'resAppend', link: '/docs/rules/resAppend' },\n              { text: 'resReplace', link: '/docs/rules/resReplace' },\n              { text: 'htmlPrepend', link: '/docs/rules/htmlPrepend' },\n              { text: 'htmlBody', link: '/docs/rules/htmlBody' },\n              { text: 'htmlAppend', link: '/docs/rules/htmlAppend' },\n              { text: 'cssPrepend', link: '/docs/rules/cssPrepend' },\n              { text: 'cssBody', link: '/docs/rules/cssBody' },\n              { text: 'cssAppend', link: '/docs/rules/cssAppend' },\n              { text: 'jsPrepend', link: '/docs/rules/jsPrepend' },\n              { text: 'jsBody', link: '/docs/rules/jsBody' },\n              { text: 'jsAppend', link: '/docs/rules/jsAppend' },\n              { text: 'trailers', link: '/docs/rules/trailers' },\n              { text: 'resWrite', link: '/docs/rules/resWrite' },\n              { text: 'resWriteRaw', link: '/docs/rules/resWriteRaw' },\n              { text: 'resRules', link: '/docs/rules/resRules' },\n              { text: 'resScript', link: '/docs/rules/resScript' },\n              { text: 'frameScript', link: '/docs/rules/frameScript' },\n            ]\n          },\n          {\n            text: 'General',\n            collapsed: false,\n            items: [\n              { text: 'pipe', link: '/docs/rules/pipe' },\n              { text: 'delete', link: '/docs/rules/delete' },\n              { text: 'headerReplace', link: '/docs/rules/headerReplace' },\n            ]\n          },\n          {\n            text: 'Throttle',\n            collapsed: false,\n            items: [\n              { text: 'reqDelay', link: '/docs/rules/reqDelay' },\n              { text: 'resDelay', link: '/docs/rules/resDelay' },\n              { text: 'reqSpeed', link: '/docs/rules/reqSpeed' },\n              { text: 'resSpeed', link: '/docs/rules/resSpeed' },\n            ]\n          },\n          {\n            text: 'Tools',\n            collapsed: false,\n            items: [\n              { text: 'weinre', link: '/docs/rules/weinre' },\n              { text: 'log', link: '/docs/rules/log' },\n            ]\n          },\n          {\n            text: 'Settings',\n            collapsed: false,\n            items: [\n              { text: 'style', link: '/docs/rules/style' },\n              { text: 'enable', link: '/docs/rules/enable' },\n              { text: 'disable', link: '/docs/rules/disable' },\n              { text: 'lineProps', link: '/docs/rules/lineProps' },\n            ]\n          },\n          {\n            text: 'Filters',\n            collapsed: false,\n            items: [\n              { text: 'excludeFilter', link: '/docs/rules/excludeFilter' },\n              { text: 'includeFilter', link: '/docs/rules/includeFilter' },\n              { text: 'ignore', link: '/docs/rules/ignore' },\n              { text: 'skip', link: '/docs/rules/skip' },\n            ]\n          },\n        ]\n      },\n      {\n        text: '功能扩展',\n        collapsed: false,\n        items: [\n          { text: '插件使用', link: '/docs/extensions/usage' },\n          { text: '插件开发', link: '/docs/extensions/dev' },\n          { text: 'NPM 模块', link: '/docs/extensions/npm' }\n        ]\n      },\n      { text: '命令行操作', link: '/docs/cli' },\n      { text: '常见问题', link: '/docs/faq' },\n    ],\n\n    socialLinks: [\n      { icon: 'github', link: 'https://github.com/avwo/whistle' },\n      { icon: {\n        svg: '<svg class=\"icon\" style=\"width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"5530\"><path d=\"M821.333333 1024H201.955556A202.296889 202.296889 0 0 1 0 821.902222V202.097778A202.296889 202.296889 0 0 1 201.955556 0h309.674666a40.391111 40.391111 0 0 1 0 80.839111H201.955556a121.486222 121.486222 0 0 0-121.173334 121.258667v619.804444a121.486222 121.486222 0 0 0 121.173334 121.258667h619.377777a121.486222 121.486222 0 0 0 121.173334-121.258667V512a40.391111 40.391111 0 1 1 80.782222 0v309.902222a202.296889 202.296889 0 0 1-201.955556 202.097778z m33.905778-932.124444l37.973333 38.286222 38.257778 38.001778-185.827555 185.912888-222.151111 222.065778-83.740445 7.793778 7.793778-83.797333 221.895111-222.321778 185.799111-185.912889z m0-91.875556c-14.336 0-28.103111 5.688889-38.229333 15.900444l-204.657778 204.8-235.349333 237.397334a26.965333 26.965333 0 0 0-7.822223 16.440889l-15.075555 167.879111a26.965333 26.965333 0 0 0 26.936889 29.383111h2.417778l167.765333-15.075556a26.908444 26.908444 0 0 0 16.440889-7.822222l235.889778-236.088889 204.657777-204.8a53.902222 53.902222 0 0 0 0-76.231111l-57.912889-58.766222L893.212444 15.928889A53.845333 53.845333 0 0 0 855.239111 0z\" fill=\"#000000\" p-id=\"5531\"></path></svg>',\n      }, link: 'https://github.com/avwo/whistle/tree/master/docs' },\n    ],\n    footer: {\n      message: '<a href=\"https://beian.miit.gov.cn/\" target=\"_blank\">浙ICP备15019507号-2</a>',\n      copyright: 'Copyright © 2015-present Aven Wu'\n    }\n  }\n});\n"
  },
  {
    "path": "docs/docs/cli.md",
    "content": "# 命令行操作\nWhistle 命令行版本支持以下命令行操作，查看完整命令行功能执行命令：`w2 -h`：\n\n``` sh\nUsage: w2 <command> [options]\n\n\nCommands:\n\n  status      Display running status\n  add         Add rules from local JS file (.whistle.js by default)\n  proxy       Configure system proxy settings\n  ca          Manage Root CA certificates\n  install     Install Whistle plugin\n  uninstall   Uninstall Whistle plugin\n  exec        Execute plugin command\n  run         Start a front service\n  start       Start a background service\n  stop        Stop current background service\n  restart     Restart current background service\n  help        Display help information\n\nOptions:\n\n  -h, --help                                      output usage information\n  -D, --baseDir [baseDir]                         set storage root path\n  -z, --certDir [directory]                       set custom certificate directory\n  -l, --localUIHost [hostname]                    set web UI domain (local.whistlejs.com by default)\n  -L, --pluginHost [hostname]                     set plugin UI domains  (as: \"script=a.b.com&vase=x.y.com\")\n  -n, --username [username]                       set web UI username\n  -w, --password [password]                       set web UI password\n  -N, --guestName [username]                      set web UI guest username (read-only)\n  -W, --guestPassword [password]                  set web UI guest password (read-only)\n  -s, --sockets [number]                          set max cached connections per domain (256 by default)\n  -S, --storage [newStorageDir]                   set configuration storage directory\n  -C, --copy [storageDir]                         copy configuration from specified directory\n  -c, --dnsCache [time]                           set DNS cache time (default: 60000ms)\n  -H, --host [boundHost]                          set bound host (default: INADDR_ANY)\n  -p, --port [proxyPort]                          set proxy port (default: 8899 by default)\n  -P, --uiport [uiport]                           set web UI port\n  -m, --middlewares [script path or module name]  set startup middlewares (format: xx,yy/zz.js)\n  -M, --mode [mode]                               set startup mode (options: pureProxy|debug|multiEnv|capture|disableH2|network|rules|plugins|prod)\n  -t, --timeout [ms]                              set request timeout (default: 360000\n  -e, --extra [extraData]                         set plugin extra parameters\n  -f, --secureFilter [secureFilter]               set secure filter path\n  -r, --shadowRules [shadowRules]                 set default shadow rules\n  -R, --reqCacheSize [reqCacheSize]               set request data cache size (default: 600)\n  -F, --frameCacheSize [frameCacheSize]           set WebSocket frame cache size (default: 512)\n  -A, --addon [pluginPaths]                       add custom plugin paths\n  --init [bypass]                                 auto configure proxy and install Root CA\n  --cluster [workers]                             start cluster with worker count (default: CPU cores)\n  --config [config]                               load startup config from file\n  --rcPath [rcPath]                               load configuration from file (default: ～/.whistlerc)\n  --dnsServer [dnsServer]                         set custom DNS servers\n  --socksPort [socksPort]                         set SOCKSv5 server port\n  --httpPort [httpPort]                           set HTTP server port\n  --httpsPort [httpsPort]                         set HTTPS server port\n  --allowOrigin [originList]                      set allowed CORS origins (format: a.b.c,x.y.z or *)\n  --no-global-plugins                             disable global plugins\n  --no-prev-options                               ignore previous options on restart\n  --inspect [[host:]port]                         enable inspector (default: 127.0.0.1:9229)\n  --inspectBrk [[host:]port]                      enable inspector with breakpoint (default: 127.0.0.1:9229)\n  -V, --version                                   output the version number\n```\n\n## w2 start\n1. `w2 start`：启动 Whistle，并使用默认存储目录\n2. `w2 start -p 8100`：启动指定端口的 Whistle（默认为 `8899`）\n3. `w2 start --httpsPort 8001`：启动 Whistle，并开启 HTTPS 代理端口\n4. `w2 start --socksPort 1080`：启动 Whistle，并开启 SOCKSv5 代理端口\n5. `w2 start -S storageName`：启动指定存储目录的 Whistle（大写 `S`）\n> `storageName` 应为纯目录名（非完整路径），多实例运行时需满足：\n> - 每个实例使用独立目录\n> - 配置不同监听端口\n> ``` sh\n> w2 start\n> w2 start -p 8100 -S storageName2\n> ```\n\n## w2 restart\n1. `w2 restart`：重启 Whistle\n   - 如果对应的端口和存储目录未有运行的 Whistle 实例，则会启动该 Whistle\n   - 如果对应的端口和存储目录有运行的 Whistle 实例，会先关闭该实例再启动\n2. `w2 restart --no-prev-options`：等价于 `w2 stop && w2 start`\n3. `w2 restart -p 8100`：重启并修改端口\n4. `w2 starrestartt --httpsPort 8001`：重启并开启 HTTPS 代理端口\n4. `w2 restart --socksPort 1080`：重启并开启 SOCKSv5 代理端口\n5. `w2 restart -S storageName`：重启指定存储目录的 Whistle 实例\n\n## w2 stop\n1. `w2 stop`：停止默认存储目录下的 Whistle\n2. `w2 stop -S storageName`：停止指定存储目录的 Whistle\n\n> 可通过下面 `w2 status --all` 查看当前命令行后台运行的 Whistle 实例\n\n##  w2 status\n\n1. 输出所有当前命令行后台运行的 Whistle 实例：`w2 status --all`\n    ``` sh\n    [i] All running Whistle instances:\n      1. PID: 51512, Port: 8899\n      2. PID: 53951, Port: 8080, Storage: 8080\n    ```\n2. 输出命令行后台运行的默认实例：`w2 status`\n    ``` sh\n    [!] whistle@version is running\n    [i] 1. use your device to visit the following URL list, gets the IP of the URL you can access:\n          http://127.0.0.1:8899/\n          http://192.168.10.153:8899/\n          http://10.211.55.2:8899/\n          http://10.37.129.2:8899/\n          Note: If all URLs are inaccessible, check firewall settings\n                For help see https://github.com/avwo/whistle\n    [i] 2. set the HTTP proxy on your device with the above IP & PORT(8899)\n    [i] 3. use Chrome to visit http://local.whistlejs.com/ to get started\n    ```\n\n## w2 add\n1. `w2 add`：执行当前目录下的 `.whistle.js`（或 `.whistle.mjs`）并将 export 出来的 `name`、`rules`、或 `groupName`（可选） 设置到界面的 Rules 里面\n2. `w2 add filepath`：自定义执行的文件\n\n`.whistle.js` 文件内容：\n``` js\nconst pkg = require('./package.json');\n\nexports.groupName = '项目开发环境'; // 可选，设置分组， 要求 Whistle 版本 >= v2.9.21\nexports.name = `[${pkg.name}]本地环境配置`;\nexports.rules = `\ntest.example.com http://127.0.0.1:5566\n# cgi走现网\ntest.example.com/cgi-bin ignore://http\n```\n\n或异步获取规则：\n``` js\nconst assert = require('assert);\nconst path = require('path');\nconst pkg = require('./package.json');\n\nmodule.exports = (cb, util) => {\n  // 如果依赖插件，可以检查插件\n  assert(util.existsPlugin('@scope/whistle.combo')\n    || util.existsPlugin('whistle.combo'), '请先安装插件: w2 i whisltle.combo');\n  // 也可以远程获取规则\n  // do sth\n  cb({\n    name: `[${pkg.name}]本地环境配置`,\n    rules:  `\n      test.example.com/combo whisle.combo://${path.join(__dirname, 'dev')}\n      test.example.com http://127.0.0.1:5566\n      # 接口走现网\n      test.example.com/cgi-bin ignore://http\n      `\n  });\n};\n```\n\nWhistle 会检查是否存在同名规则：\n- 规则不存在或为空：自动创建新规则并启用\n- 规则已存在且非空：提示用户确认，避免意外覆盖\n- 若需强制覆盖已有规则，请显式添加 `--force` 参数：\n  ``` sh\n  w2 add --force\n  ```\n\n## w2 proxy\n1. `w2 proxy`：设置系统代理\n   - IP：`127.0.0.1`\n   - 端口：Whistle 运行的端口，如果没有运行的 Whistle，用默认端口 `8899`\n2. `w2 proxy 0`：关闭系统代理\n3. `w2 proxy 8100`：设置系统代理\n   - IP：`127.0.0.1`\n   - 端口：`8100`\n4. `w2 proxy www.test.com:8100`：设置系统代理\n   - IP 或域名：`www.test.com`\n   - 端口：`8100`\n\n## w2 ca\n1. `w2 ca`：安装本地 Whistle 的根证书（安装本地 Whistle 根证书一般用这个命令即可）\n2. `w2 ca 8080`：从指定端口（IP：`127.0.0.1`）下载 Whistle 根证书并安装\n4. `w2 ca www.test.com:8080`：安装指定端口及 IP（或域名）的 Whistle 证书（可用于安装远程的 Whistle 根证书）\n5. `w2 ca certUrl`：下载指定 URL 的证书并安装\n6. `w2 ca localCertPath`：安装本地指定路径的证书\n\n## w2 install\n1. `w2 install whistle.script`：安装插件\n2. `w2 install whistle.script --registry=https://npm-registry`：安装插件，并指定 npm registry\n\n推荐使用界面安装：[使用插件](./extensions/usage)\n\n## w2 uninstall\n`w2 uninstall whistle.script`：卸载指定插件\n\n推荐使用界面卸载：[使用插件](./extensions/usage)\n## w2 exec\n`w2 exec xxx`：执行插件在 package.json 中配置的 bin 命令（即插件提供的可执行脚本）\n> 适用于插件开发者或需要调用插件 CLI 功能的场景\n\n## w2 run\n`w2 run`：以开发调试模式启动 Whistle，实时输出插件和系统的日志信息到控制台，修改界面代码刷新自动生效等，并支持所有 `w2 start` 的参数配置\n\n以下是优化后的帮助文档：\n\n## 启动多个实例\n\n当需要同时运行多个实例时，必须为每个实例配置**不同的端口**和**独立的存储目录**，避免资源冲突。\n\n### 配置示例\n\n```sh\n# 实例1 - 端口8010，存储目录8010\nw2 start --port 8010 --storage 8010\n\n# 实例2 - 端口8011，存储目录8011  \nw2 start --port 8011 --storage 8011\n\n# 实例3 - 端口8012，存储目录8012\nw2 start --port 8012 --storage 8012\n```\n\n### 配置说明\n\n| 参数 | 作用 | 要求 |\n|------|------|------|\n| `--port` | 服务监听端口 | 必须唯一，不能重复 |\n| `--storage` | 数据存储目录 | 必须唯一，避免数据混淆 |\n\n### 配置建议\n\n**推荐配置**：\n- 端口与存储目录使用相同编号（便于管理）\n- 端口号范围：8000-9000（避免系统端口冲突）\n\n**配置示例**：\n```sh\n# ✅ 推荐：端口与存储目录一致\nw2 start -p 8010 -S 8010\nw2 start -p 8011 -S 8011\n\n# ✅ 允许：端口与存储目录不同（需确保唯一性）\nw2 start -p 8020 -S instance_1\nw2 start -p 8021 -S instance_2\n\n# ❌ 错误：端口或存储目录重复\nw2 start -p 8010 -S 8010\nw2 start -p 8010 -S 8011  # 端口冲突！\nw2 start -p 8011 -S 8010  # 存储目录冲突！\n```\n\n### 注意事项\n\n1. **端口冲突**：相同端口会导致实例启动失败\n2. **数据安全**：共享存储目录会造成数据覆盖或损坏\n3. **资源隔离**：每个实例应有独立的运行环境\n\n通过为每个实例分配唯一的端口和存储目录，可以确保多个实例稳定并行运行。\n\n## 启动配置文件 {#whistlerc}\nWhistle 启动时会自动加载 ~/.whistlerc 配置文件。您可以通过以下方式管理配置加载：\n- **默认路径:** `~/.whistlerc`\n- **自定义路径:** 使用 `--rcPath [rcPath]` 参数指定其他配置文件\n- **忽略配置:** 使用 `-rcPath none` 参数完全忽略配置文件\n\n`.whistlerc` 文件采用键值对格式，支持多种配置作用域：\n``` txt\n# 全局默认配置（没有指定 storage）\nusername=test\npassword=123\n\n# 特定存储配置（通过命令行指定 storage）\nxxx.username=test-storage\nxxx.password=123-storage\n\n# 通配符配置（对所有实例生效，优先级最低）\n*.username=test-default\n*.password=123-default\n```\n1. `xxx` 为启动参数 `--storage` 对应的值（没有 storage 则使用缺省的配置）\n2. `*` 表示对所实例生效（优先级最低）\n3. 其它配置参数参考本文档\n\n## 其它参数\n1. `-D, --baseDir [baseDir]`：自定义 Whistle 存储根目录（默认为 `$WWHISTLE_PATH/.whistle`）\n   > 示例：`w2 start -D ~/my_whistle_data`\n2. `-n, --username [username]`：设置管理界面登录用户名\n3. `-w, --password [password]`：设置管理界面登录密码\n   > 示例：`w2 start -n abc -w 123`\n4. `-N, --guestName [username]`：设置只读权限的游客账号（游客仅可查看配置和抓包，不可修改）\n5. `-W, --guestPassword [password]`：设置游客账号密码（游客仅可查看配置和抓包，不可修改）\n   > 示例：`w2 start -N test -W 123`\n6. `-P, --uiport [uiport]`：单独设置管理界面端口（默认与代理端口相同）\n   > 示例：`w2 start -P 8889`\n7. `-e, --extra [extraData]`：启动时向指定插件传递数据（`如：{inspect: data}`）\n   > 示例：`w2 start -e '{\"debug\":true}'`\n8. `--allowOrigin [originList]`：允许跨域请求管理界面接口的域名\n   > 示例：`w2 start --allowOrigin *`\n9.  `--no-global-plugins`：启动时不加载 `npm i -g whistle.xxx` 安装的插件\n    > 示例：`w2 start --no-global-plugins`\n10. `--inspect [[host:]port]`：启用Node.js调试（默认9229端口），配合Chrome DevTools使用\n    > 示例：`w2 start --inspect`\n11. `--inspectBrk [[host:]port]`：启用调试并在首行断点\n    > 示例：`w2 start --inspectBrk`\n12. `--config [config]`：通过配置文件加载参数\n    > 示例：`w2 start --config /data/xxx.json`\n\n给请求设置用户密码需要用到插件（或自己开发插件）：[whistle.proxyauth](https://github.com/whistle-plugins/whistle.proxyauth)\n    "
  },
  {
    "path": "docs/docs/extensions/dev.md",
    "content": "# 插件开发\n您已了解插件的各项功能特性（参考[使用文档](./usage)），现在我们将通过模块化方式演示具体实现。\n> 每个核心功能作为独立插件实现，保持单一职责原则，实际开发中可自由拼装这些功能模块。\n\n## 准备工作\n1. 创建一个空目录，如\n   ``` sh\n    mkdir examples && cd examples\n   ```\n2. 安装脚手架 [lack](https://github.com/avwo/lack)\n   ``` txt\n    npm i -g lack\n   ```\n   > lack 必须用最新版本（`>= 1.4.0`）\n3. 更新 [Whistle](https://github.com/avwo/whistle)\n   > Whistle 必须用最新版本（`>= 2.9.100`，[客户端](https://github.com/avwo/whistle-client) `>= 1.3.8`）\n\n## rules.txt\n- 插件安装后会自动加载生效\n- 功能跟界面 Rules 配置的规则一样，优先级低于界面 Rules 规则\n- 插件被禁用立即失效\n\n**示例**\n1. 创建包含 `rules.txt` 规则文件的插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-rules && cd whistle.test-rules\n\n    # 初始化包含 rules.txt 的插件\n    lack init rules\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-rules` 插件\n    lack watch\n    ```\n2. 编辑生成的 `rules.txt` 文件：\n    ``` txt\n    * reqHeaders://x-client=whistle.test-rules\n    ```\n3. 所有经过 Whistle 的请求都会自动添加请求头 `x-client: whistle.test-rules`\n   \n   <img src=\"/img/test-rules.png\" width=\"600\" />\n4. 如果规则修改后未生效？请确认：\n    - 是否已执行 `lack watch`\n    - 是否跟界面配置冲突规则\n    - 插件是否被禁用\n\n## _rules.txt\n- 仅对匹配插件协议的请求生效\n- 支持长协议(whistle.myplugin://)和短协议(myplugin://)\n- 作用于请求处理阶段\n``` txt\npattern whistle.myplugin://value\npattern myplugin://value\n```\n\n**示例**\n1. 创建包含 `_rules.txt` 规则文件的插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-req-rules && cd whistle.test-req-rules\n\n    # 初始化包含 _rules.txt 的插件\n    lack init _rules\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-req-rules` 插件\n    lack watch\n    ```\n2. 编辑生成的 `_rules.txt` 文件：\n    ``` txt\n    * file://(hello)\n    ```\n    > 所有请求返回响应内容 `hello`\n4. 直接访问 `https://www.example.com/test` 返回正常页面\n5. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    https://www.example.com/test whistle.test-req-rules://\n    ```\n    后再访问 `https://www.example.com/test` 返回 `hello`\n   \n## resRules.txt\n- 仅对匹配插件协议的请求生效\n- 作用于响应处理阶段\n- 可修改响应状态码和内容\n``` txt\npattern whistle.myplugin://value\npattern myplugin://value\n```\n\n**示例**\n\n1. 创建包含 `resRules.txt` 规则文件的插件：\n   ```sh\n    # 创建插件目录\n    mkdir whistle.test-res-rules && cd whistle.test-res-rules\n\n    # 初始化包含 resRules.txt 的插件\n    lack init resRules\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-res-rules` 插件\n    lack watch\n    ```\n2. 编辑生成的 `resRules.txt` 文件：\n    ``` txt\n    * resBody://`(whistle_error_${statusCode})` includeFilter://s:500 includeFilter://s:404\n    ```\n    > 把响应状态码为 `404` 和 `500` 的请求的响应内容改为 `whistle_error_404` 和  `whistle_error_500`\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    https://www.example.com/500 whistle.test-req-rules:// statusCode://500\n    https://www.example.com/404 whistle.test-req-rules:// statusCode://404\n    ```\n4. 效果：\n      - 访问 `https://www.example.com/500` 响应内容被改成 `whistle_error_500`\n      - 访问 `https://www.example.com/404` 响应内容被改成 `whistle_error_404`\n      - 访问 `https://www.example.com/test` 则返回原始内容\n\n## auth\n对经过 Whistle 的请求进行鉴权。\n\n**示例**\n\n1. 创建请求鉴权插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-auth && cd whistle.test-auth\n\n    # 初始化包含 auth 的插件\n    lack init auth\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-auth` 插件\n    lack watch\n    ```\n2. 编辑 `src/auth.ts` 文件：\n    ``` ts\n    export default async (req: Whistle.PluginAuthRequest, options: Whistle.PluginOptions) => {\n      const { fullUrl } = req;\n      // URL 里面包含 `/test/forbidden` 的响应状态码为 403\n      if (fullUrl.includes('/test/forbidden')) {\n        return false;\n      }\n      // URL 里面包含 `/test/message/forbidden` 的响应状态码为 403，且自定义响应内容\n      if (fullUrl.includes('/test/message/forbidden')) {\n        req.setHtml('<strong>Access Denied</strong>');\n        return false;\n      }\n\n      // URL 里面包含 `/test/login` 要求用户输入用户名和密码\n      if (fullUrl.includes('/test/login')) {\n        const auth = req.headers.authorization || req.headers['proxy-authorization'];\n        if (auth) {\n          // TODO: 校验用户名和密码，如果正确返回 true，否则返回 false\n          return true;\n        }\n        req.setLogin(true);\n        return false;\n      }\n\n      // URL 里面包含 `/test/redirect` 的响应状态码为 302，且重定向到 `https://www.example.com/test`\n      if (fullUrl.includes('/test/redirect')) {\n        req.setRedirect('https://www.example.com/test');\n        return false;\n      }\n      // 其它请求直接放过\n      // 如果需要添加自定义请求头，可以使用 `req.setHeader` 方法\n      // 支持添加 key 前缀为 `x-whistle-` 的请求头\n      // 例如：req.setHeader('x-whistle-xxx', 'value');\n      req.setHeader('x-whistle-custom-header', 'lack');\n      return true;\n    };\n    ```\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    www.example.com whistle.auth://\n    ```\n4. 效果：\n      - 访问 `https://www.example.com/test/forbidden` 响应状态码 `403`，响应内容 `Forbidden`\n      - 访问 `https://www.example.com/test/message/forbidden` 响应状态码 `403`，响应内容 `<strong>Access Denied</strong>`\n      - 访问 `https://www.example.com/test/login` 弹出浏览器的登录框\n      - 访问 `https://www.example.com/test/redirect` 重定向到 `https://www.example.com/test`\n      - 其它 `www.example.com` 的请求正常，且被添加请求头 `x-whistle-custom-header: lack`\n\n## sniCallback\n通过插件动态下发请求证书。\n\n**示例**\n\n1. 创建自定义证书插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-sni && cd whistle.test-sni\n\n    # 初始化包含 sni 的插件\n    lack init sni\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-sni` 插件\n    lack watch\n    ```\n2. 编辑 `src/sniCallback.ts` 文件：\n    ``` ts\n    const key = `...`;\n    const cert = `...`;\n\n    // sniCallback 插件处理函数 - 根据请求 URL 动态决定 TLS 隧道处理方式\n    export default async (req: Whistle.PluginSNIRequest, options: Whistle.PluginOptions) => {\n      const { fullUrl } = req;\n      // 特殊域名返回 false，保持 TUNNEL 状态（不解除 TLS 加密）\n      if (fullUrl === 'https://tunnel.example.com') {\n        return false;\n      }\n\n      // 对特定域名使用自定义证书解密，返回一个包含 key 和 cert 的对象\n      // 支持 .crt、.pem、.cer 等格式的证书\n      if (fullUrl === 'https://custom.example.com') {\n        return { key, cert };\n      }\n      // 默认使用 Whistle 内置证书解密 TLS 流量\n      return true;\n    };\n    ```\n5. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    www.example.com sniCallback://test-sni\n    ```\n6.  效果：\n   - 访问 `https://tunnel.example.com/path`，请求保持 TUNNEL 状态（不解除 TLS 加密）\n   - 访问 `https://custom.example.com/test/` 报证书出错\n   - 其它请求正常\n\n## rulesServer\n通过 JavaScript 编程实时生成规则，规则功能跟上面的 _rules.txt 静态规则一样，但优先级高于 _rules.txt。\n\n**示例**\n\n1. 创建包含 `rulesServer` 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-sni && cd whistle.test-rules-server\n\n    # 初始化包含 rulesServer 的插件\n    lack init rulesServer\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-rules-server` 插件\n    lack watch\n    ```\n2. 编辑 `src/rulesServer.ts` 文件：\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest, res: Whistle.PluginResponse) => {\n        res.end(JSON.stringify({\n          values: {\n            'whistle.test-rules-server/a.html': 'test normal values',\n          },\n          rules: `\n            \\`\\`\\` whistle.test-rules-server/b.html\n            test inject values\n            \\`\\`\\`\n\n            */a file://{whistle.test-rules-server/a.html}\n            */b file://{whistle.test-rules-server/b.html}\n          `,\n        }));\n      });\n    };\n    ```\n    > 支持响应纯 rules 文本  `res.end(rules)`，或包含 rules & values 的序列化对象 `res.end(JSON.stringify({rules, values}))`\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    test.example.com whistle.test-rules-server://\n    ```\n4.  效果：\n   - 访问 `https://test.example.com/a` 返回 `test inject values`\n   - 访问 `https://test.example.com/b` 返回 `test normal values`\n\n## tunnelRulesServer\ntunnelRulesServer 是专门用于处理 TUNNEL 请求的动态规则生成机制，其核心特性与常规 [rulesServer](#rulesserver) 相同，但应用场景有专门区分：\n1. tunnelRulesServer 仅对 `tunnel://domain:port` 隧道请求生效\n2. rulesServer 对 `HTTP[S]/WebSocket` 请求生效\n\n## resRulesServer\n请求的响应阶段通过 JavaScript 编程实时生成规则（可作用于所有类型的请求），规则功能跟上面的 resRules.txt 静态规则一样，但优先级高于 resRules.txt。\n\n**示例**\n\n1. 创建包含 `resRulesServer` 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-res-rules-server && cd whistle.test-res-rules-server\n\n    # 初始化包含 resRulesServer 的插件\n    lack init resRulesServer\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-res-rules-server` 插件\n    lack watch\n    ```\n2. 编辑 `src/resRulesServer.ts` 文件：\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest, res: Whistle.PluginResponse) => {\n        res.end('* resBody://(test-res-rules-server)');\n      });\n    };\n    ```\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    test.example.com/res whistle.test-res-rules-server://\n    ```\n4.  效果：\n   - 访问 `https://test.example.com/res/path` 返回 `test-res-rules-server`\n\n\n## statsServer\n如果仅想获取请求的 URL、请求方法、请求头、请求内容等信息，而不对请求做任何操作，可以用 statsServer。\n\n**示例**\n\n1. 创建包含 `statsServer` 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-stats-server && cd whistle.test-stats-server\n\n    # 初始化包含 statsServer 的插件\n    lack init statsServer\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-stats-server` 插件\n    lack watch\n    ```\n2. 编辑 `src/statsServer.ts` 文件：\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest) => {\n        const { originalReq } = req;\n        console.log('Value:', originalReq.ruleValue);\n        console.log('URL:', originalReq.fullUrl);\n        console.log('Method:', originalReq.method);\n        console.log('Request Headers:', originalReq.headers);\n        // 获取请求的 body\n        req.getReqSession((reqSession) => {\n          if (reqSession) {\n            console.log('Request Body:', reqSession.req.body);\n          }\n        });\n      });\n    };\n    ```\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    www.example.com/stats whistle.test-stats-server://test\n    ```\n4.  效果：\n   - 访问 `https://www.example.com/stats` 控制台输出：\n      ``` sh\n      Value: test\n      URL: https://www.example.com/stats\n      Method: GET\n      Request Headers: {\n        host: 'www.example.com',\n        'cache-control': 'max-age=0',\n        'sec-ch-ua': '\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"',\n        'sec-ch-ua-mobile': '?0',\n        'sec-ch-ua-platform': '\"macOS\"',\n        'upgrade-insecure-requests': '1',\n        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',\n        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n        'sec-fetch-site': 'cross-site',\n        'sec-fetch-mode': 'navigate',\n        'sec-fetch-user': '?1',\n        'sec-fetch-dest': 'document',\n        'accept-encoding': 'gzip, br',\n        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',\n        priority: 'u=0, i',\n        'x-forwarded-for': '127.0.0.1',\n        connection: 'close'\n      }\n      Request Body: \n      ```\n\n## resStatsServer\n如果获取请求的响应状态码、响应头、响应内容等信息，而不对请求做任何操作，可以用 resStatsServer。\n\n**示例**\n\n1. 创建包含 `resStatsServer` 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-res-stats-server && cd whistle.test-res-stats-server\n\n    # 初始化包含 resStatsServer 的插件\n    lack init resStatsServer\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-res-stats-server` 插件\n    lack watch\n    ```\n2. 安装依赖\n    ``` sh\n    npm i\n    ```\n3. 编辑 `src/resStatsServer.ts` 文件：\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest) => {\n        const { originalReq, originalRes } = req;\n        console.log('Value:', originalReq.ruleValue);\n        console.log('URL:', originalReq.fullUrl);\n        console.log('Method:', originalReq.method);\n        console.log('Server IP', originalRes.serverIp);\n        console.log('Status Code:', originalRes.statusCode);\n        console.log('Response Headers:', originalReq.headers);\n        // 获取请求的完整抓包数据\n        req.getSession((reqSession) => {\n          if (reqSession) {\n            console.log('Response Body:', reqSession.res.body);\n          }\n        });\n      });\n    };\n    ```\n4. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    www.example.com/res/stats whistle.test-res-stats-server://testResStats\n    ```\n5.  效果：\n   - 访问 `https://www.example.com/res/stats` 控制台输出：\n      ``` txt\n      Value: testResStats\n      URL: https://www.example.com/res/stats\n      Method: GET\n      Server IP 127.0.0.1\n      Status Code: 200\n      Response Headers: {\n        'accept-ranges': 'bytes',\n        'content-type': 'text/html',\n        etag: '\"84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134\"',\n        'last-modified': 'Mon, 13 Jan 2025 20:11:20 GMT',\n        server: 'AkamaiNetStorage',\n        expires: 'Thu, 24 Jul 2025 02:36:23 GMT',\n        'cache-control': 'max-age=0, no-cache, no-store',\n        pragma: 'no-cache',\n        date: 'Thu, 24 Jul 2025 02:36:23 GMT',\n        'alt-svc': 'h3=\":443\"; ma=93600,h3-29=\":443\"; ma=93600',\n        'x-forwarded-for': '127.0.0.1',\n        host: 'www.example.com',\n        connection: 'close'\n      }\n      Response Body: <!doctype html>\n      <html>\n      <head>\n          <title>Example Domain</title>\n\n      ......\n      ```\n\n## server\n可以将插件作为 **server** 使用，由它接收并转发请求。插件可以直接响应请求，或在处理后继续转发至目标服务器。目标服务器返回响应后，插件可以进一步处理，最终将结果返回给浏览器或客户端。\n\n**示例**\n\n1. 创建包含 `server` 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-server && cd whistle.test-server\n\n    # 初始化包含 server 的插件\n    lack init server\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-server` 插件\n    lack watch\n    ```\n2. 编辑 `src/server.ts` 文件：https://github.com/whistle-plugins/examples/tree/master/whistle.test-server\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    https://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json test-server://setCookieFromBody\n    ```\n4.  访问 `https://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json`\n    - 浏览器 Cookie 新增 `test-name=whistle`\n    - 响应内容新增 `\"pluginName\":\"whistle\"`\n\n\n## pipe\n如果请求/响应内容被加密，或者需要转成特定格式显示在抓包界面，可以用 pipe 将请求/响应内容交给插件处理。\n> 在抓包界面中：\n> \n> 请求数据中无法看到 `reqWrite` 修改后的内容\n> \n> 响应数据中无法看到 `resWrite` 修改后的内容（这是预期行为）\n> \n> 所有 `xxxRexWrite` 操作的实际修改内容都不会显示在抓包界面\n> \n\n#### HTTP/HTTPS 协议实现\n1. 创建 pipe http 插件:\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-pipe-http && cd whistle.test-pipe-http\n\n    # 初始化包含 pipe http 的插件\n    lack init pipeHttp,server\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-pipe-http` 插件\n    lack watch\n    ```\n4. 编辑代码：https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-http\n5. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    www.example.com/test-pipe-http pipe://test-pipe-http test-pipe-http://mirror\n    ```\n    > `test-pipe-http://mirror` 的作用是客户端请求什么内容，服务端就响应什么内容\n6. Whistle / Network / Composer 输入 `www.example.com/test-pipe-http`，选择 `POST` 方法，Body 输入 `test-pipe-http`\n    <img src=\"/img/pipe-http.png\" width=\"400\" />\n7.  点击发送按钮，查看返回的 Body 如下\n    <img src=\"/img/pipe-http-result.png\" width=\"400\" />\n   \n\n#### WebSocket 协议实现\n1. 创建 pipe websocket 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-pipe-ws && cd whistle.test-pipe-ws\n\n    # 初始化包含 pipe websocket 的插件\n    lack init pipeWs\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-pipe-ws` 插件\n    lack watch\n    ```\n2. 编辑代码：https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-ws\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    wss://echo.websocket.org/ pipe://test-pipe-ws\n    ```\n4.  打开页面 `https://echo.websocket.org/.ws` 可以看到如下效果：\n   <img src=\"/img/pipe-ws.png\" width=\"520\" />\n   \n\n\n#### TCP 隧道实现\n1. 创建 pipe tunnel 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-pipe-tunnel && cd whistle.test-pipe-tunnel\n\n    # 初始化包含 pipe tunnel 的插件\n    lack init pipeTunnel.server\n\n    # 安装依赖\n    npm i --save-dev lack-proxy && npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-pipe-tunnel` 插件\n    lack watch\n    ```\n2. 编辑代码：https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-tunnel\n3. 在界面 Rules （或插件的 `rules.txt` 文件）配置以下规则：\n    ``` txt\n    test-pipe-tunnel.example.com pipe://test-pipe-tunnel test-pipe-tunnel://mirror\n    ```\n4. 执行 `whistle.test-pipe-tunnel` 根目录的 `test.js` 文件：\n    ``` sh\n    node test.js\n    ```\n5. 控制台输出的内容\n    <img src=\"/img/pipe-tunnel.png\" width=\"560\" />\n\n## 插件操作界面\n在 Whistle 插件列表中，点击 `Option` 或插件名称可快速打开插件操作界面，支持以下三种模式：\n1. **Tab 模式**：在 Whistle 界面内以标签页形式打开（默认方式）\n2. **对话框模式**：在 Whistle 界面内以弹窗形式打开\n3. **新标签模式**：在浏览器新标签页或客户端新窗口中打开\n\n---\n\n### 1. Tab 模式（默认）\n**特性**  \n- 插件管理界面默认地址为：`http[s]://domain[:port]/plugin.xxx/`  \n- 通过插件列表的 `Option` 或插件名称直接打开  \n\n**禁用 Tab 模式**  \n若需隐藏此打开方式，可在 `whistleConfig` 中配置：  \n```js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"noOption\": true // 禁用默认入口\n    ...\n  }\n  ...\n}\n```\n\n---\n\n### 2. 对话框模式\n**特性**  \n- 支持通过弹窗形式打开管理界面  \n- 弹窗页面可调用的 API：https://github.com/avwo/whistle/blob/master/assets/modal.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n**配置示例**  \n```js\n{\n  \"whistleConfig\": {\n    ...\n    \"openInModal\": {\n      \"width\": 360,  // 弹窗宽度（px）\n      \"height\": 566  // 弹窗高度（px）\n    }\n    ...\n  }\n}\n```\n\n---\n\n### 3. 新标签模式\n**场景**  \n需使用外部页面作为插件管理界面时使用。  \n\n**基础配置**  \n```js\n{\n   \"pluginHomepage\": \"https://your-external-page.com\" // 外部页面地址\n}\n```\n\n**高级选项**  \n若需将外部链接强制在 Whistle 内打开（Tab 或对话框模式）：  \n\n**方式一：对话框模式**  \n```js\n{\n  ...\n  \"pluginHomepage\": \"https://your-external-page.com\",\n  \"whistleConfig\": {\n    ...\n    \"openInModal\": {  // 强制以弹窗打开\n      \"width\": 360,\n      \"height\": 566\n    }\n    ...\n  }\n  ...\n}\n```\n\n**方式二：Tab 模式**  \n```js\n{\n  ...\n  \"pluginHomepage\": \"https://your-external-page.com\",\n  \"whistleConfig\": {\n    ...\n    \"openInPlugins\": true  // 强制在插件Tab页打开\n    ...\n  }\n  ...\n}\n```\n \n## 完整 Hooks API\n参考：https://github.com/avwo/lack/blob/master/assets/ts/src/types/global.d.ts\n\n## 插件变量配置{#pluginvars}\n支持通过 `%` 符号在规则里面配置，详见：[`%` 符号用途](../rules/plugin-vars)\n\n## 自定义规则补全功能{#rules-hint}\n综上所述，每个插件可以扩展以下规则：\n``` txt\n%myplugin=xxx\n%myplugin.key=xxx\nwhistle.myplugin://xxx\nmyplugin://xxx\nsinCallback://myplugin(sinValue)\npipe://myplugin(pipeValue)\n```\n\nWhistle 还支持对以下规则自定义补全功能：\n``` txt\n%myplugin=xxx\n%myplugin.key=xxx\nwhistle.myplugin://xxx\nmyplugin://xxx\n```\n\n**示例**\n1. 创建 rules hints 插件：\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-rules-hint && cd whistle.test-rules-hint\n\n    # 初始化包含 uiServer 的插件\n    lack init uiServer\n\n    # 安装依赖\n    npm i\n\n    # 编译代码\n    npm run dev\n\n    # 开发模式运行：将插件挂载到 Whistle，挂载后可以在插件列表中看到 `test-rules-hint` 插件\n    lack watch\n    ```\n5. 在 `package.json` 配置 `whistleConfig`，新增 `pluginVars` 和 `hintUrl`：\n   ``` js\n    {\n      ...\n      \"whistleConfig\": {\n        \"pluginVars\": {\n          ...\n          \"hintUrl\": '/cgi-bin/plugin-vars'\n          ...\n        },\n        \"hintUrl\": '/cgi-bin/get-hints'\n      },\n      ...\n    }\n   ```\n6. 编辑 `src/uiServer/router.ts` 文件：\n    ``` ts\n    export default (router: Router) => {\n      // 针对 `%test-rules-hint[.key]=xxx`\n      router.get('/cgi-bin/plugin-vars', (ctx) => {\n        const { sep, value } = ctx.query;\n        const isKey = sep === '.';\n        let key = '';\n        let keyword = '';\n        if (value && typeof value === 'string') {\n          if (isKey) {\n            const index = value.indexOf('=');\n            // %test-plugin-vars.xxx=yyy\n            if (index !== -1) {\n              key = value.substring(0, index);\n              keyword = value.substring(index + 1).toLowerCase();\n            } else {\n              // %test-plugin-vars.xxx or %test-plugin-vars.xxx=\n              key = value;\n            }\n          } else {\n            // %test-plugin-vars=yyy\n            keyword = value.toLowerCase();\n          }\n        }\n        const result: (string | {\n          isKey: true,\n          value: string,\n        })[] = [];\n        VARS_OPTIONS.forEach((option) => {\n          if (keyword && !option.toLowerCase().includes(keyword)) {\n            return;\n          }\n          if (isKey) {\n            result.push({\n              value: `${key}=${option}`,\n              isKey: true,\n            });\n          } else {\n            result.push(option);\n          }\n        });\n        ctx.body = result;\n      });\n\n      // 针对 `test-rules-hint://xxx` 和 `whistle.test-rules-hint://xxx`\n      // 如果没有配置 `pluginVars.hintUrl`，则对 `%test-rules-hint[.key]=xxx` 也生效\n      router.get('/cgi-bin/get-hints', (ctx) => {\n        const { protocol, value } = ctx.query;\n        if (!protocol || typeof protocol !== 'string' || typeof value !== 'string') {\n          return;\n        }\n        const isVar = protocol.startsWith('%');\n        // 事实上不会有这种情况，除非删除了 `pluginVars.hintUrl` 配置\n        if (isVar) {\n          return;\n        }\n        const isLong = protocol.startsWith('whistle.');\n        const prefix = isLong ? 'long-' : 'short-';\n        const keyword = value.toLowerCase();\n        const result: string[] = [];\n        HINTS_OPTIONS.forEach((option) => {\n          if (`${prefix}${option.toLowerCase()}`.includes(keyword)) {\n            result.push(`${prefix}${option}`);\n          }\n        });\n        ctx.body = result;\n      });\n    };\n\n    ```\n9. 效果：\n   \n   <img src=\"/img/rules-hint1.png\" width=\"300\" />\n\n   <img src=\"/img/rules-hint2.png\" width=\"300\" />\n\n   <img src=\"/img/plugin-vars-hint-url1.png\" width=\"300\" />\n\n## 界面扩展\nWhistle 插件系统支持扩展以下界面功能模块：\n- **Network 模块**\n  - 数据表格列扩展\n  - 上下文菜单扩展\n  - 详情面板 Tab 扩展\n- **Rules 模块**上下文菜单\n- **Values 模块**上下文菜单\n- **Plugins 模块**上下文菜单\n\n1. 创建示例插件\n    ```sh\n    # 创建插件目录\n    mkdir whistle.test-ui-ext && cd whistle.test-ui-ext\n\n    # 初始化空白插件\n    lack init blank\n\n    # 开发模式运行\n    lack watch\n    ```\n2. 编辑 `whistleConfig`：https://github.com/whistle-plugins/examples/tree/master/whistle.test-ui-ext/package.json\n3. 执行后可在 Whistle 插件列表看到 `test-ui-ext` 插件\n    ![界面扩展效果图](/img/ui-ext.png)\n\n#### 1. Network 表格列扩展\n**配置示例**：\n```js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"networkColumn\": {\n      \"name\": \"Referer\",          // 列显示名称（必填）\n      \"key\": \"req.headers.referer\", // 数据字段路径（必填）\n      \"iconKey\": \"\",              // 图标字段（可选）\n      \"showTitle\": true,          // 是否显示悬浮提示（可选）\n      \"width\": 120                // 列宽（可选，默认120）\n    }\n    ...\n  }\n  ...\n}\n```\n\n**高级数据处理**：\n1. 创建 `/public/webWorker.js`：\n    ```js\n    module.exports = function(session, next) {\n      const isGithub = /^https?:\\/\\/github\\.com\\//.test(session.url);\n      next({\n        testWebWorker: 'custom_value',\n        style: isGithub ? { \n          color: '#fff',\n          fontStyle: 'italic',\n          bgColor: 'red' \n        } : null\n      });\n    };\n    ```\n2. 配置 webWorker：\n    ```js\n    {\n      \"whistleConfig\": {\n        \"networkColumn\": {\n          \"name\": \"Test\",\n          \"key\": \"customData.testWebWorker\"  // 访问 webWorker 返回数据\n          ...\n        },\n        \"webWorker\": \"/public/webWorker.js\"\n      }\n    }\n    ```\n\n#### 2. 详情面板 Tab 扩展\n```js\n{\n  \"whistleConfig\": {\n    \"inspectorsTab\": {\n      \"name\": \"Custom Tab\",       // Tab 显示名称\n      \"action\": \"/public/tab.html\", // 功能页面\n      \"icon\": \"data:image/png;base64,...\", // 图标（可选）\n      \"req\": {                    // Request 子 Tab\n        \"name\": \"Req SubTab\",\n        \"action\": \"/public/req-tab.html\"\n      },\n      \"res\": {                    // Response 子 Tab\n        \"name\": \"Res SubTab\",\n        \"action\": \"/public/res-tab.html\"\n      }\n    },\n    \"composerTab\": { /* 同 inspectorsTab */ },\n    \"toolsTab\": { /* 同 inspectorsTab */ }\n  }\n}\n```\n\n- Tab 页面可调用的 API：https://github.com/avwo/whistle/blob/master/assets/tab.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 3. Network 上下文菜单功能\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"networkMenus\": [\n      {\n        \"name\": \"Network Menu1\",\n        \"action\": \"/public/network-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Network Menu2\", // 菜单项名称\n        \"action\": \"/public/network-menu.html\", // 功能页面\n        \"required\": true, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": true, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- 上下文菜单的 `action` 页面可以调用的 API：https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 3. Rules 上下文菜单功能\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"rulesMenus\": [\n      {\n        \"name\": \"Rules Menu1\", // 菜单项名称\n        \"action\": \"/public/rules-menu.html\", // 功能页面\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Rules Menu2\",\n        \"action\": \"/public/rules-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- 上下文菜单的 `action` 页面可以调用的 API：https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 4. Values 上下文菜单功能\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"valuesMenus\": [\n      {\n        \"name\": \"Values Menu1\",\n        \"action\": \"/public/values-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Values Menu2\",\n        \"action\": \"/public/values-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- 上下文菜单的 `action` 页面可以调用的 API：https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 5. Plugins 上下文菜单功能\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"pluginsMenus\": [\n      {\n        \"name\": \"Plugins Menu1\",\n        \"action\": \"/public/plugins-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Plugins Menu2\",\n        \"action\": \"/public/plugins-menu.html\",\n        \"required\": false, // 默认 false，列表模式下不是列表项的上下文菜单是否禁用该项\n        \"requiredTreeNode\": false, // 默认 false，树形模式下不是列表项的上下文菜单是否禁用该项\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- 上下文菜单的 `action` 页面可以调用的 API：https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API：https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n## 特殊路径\n\nWhistle 提供专用路径格式，用于在各类型页面下直接请求插件接口，以及只在 Whistle 环境下可以正常访问的路径。\n\n#### 1. Whistle 界面直接访问\n在 Whistle 界面或插件管理界面中，可直接使用以下格式访问插件接口：\n```\npath/to\n```\n\n- 此路径会自动解析为 `/whistle.xxx/path/to`\n- 推荐使用相对路径格式，避免直接使用绝对路径 `/path/to`\n\n#### 2. 普通页面访问\n若需在非 Whistle 的普通网页中调用插件接口，请使用以下特殊路径格式：\n```\n/.whistle-path.5b6af7b9884e1165./whistle.xxx/path/to\n```\n\n- 请求到达插件时会被标准化为：`/whistle.xxx/path/to`\n- 前缀 `/.whistle-path.5b6af7b9884e1165./` 是 Whistle 的专用标识符，用于标记内部请求路由\n\n#### 3. 环境自适应路径转换\n实现经过或不经过 Whistle 代理时使用不同路径，但确保后台服务接收到统一路径：\n``` txt\n/_WHISTLE_5b6af7b9884e1165_/\n```\n\n- 原始请求：`https://www.example.com/.../_WHISTLE_5b6af7b9884e1165_/path/to?query`\n- 经过 Whistle 后转换为：`https://www.example.com/.../path/to?query`\n\n#### 说明：\n- `xxx` 为您的插件名称\n- `path/to` 是插件接口的具体路径\n- 特殊路径中的哈希值 `/.whistle-path.5b6af7b9884e1165./` 为固定标识符\n\n## 其它 whistleConfig 配置\n``` js\n{\n  ...,\n  \"homepage\": '', // 插件帮助页面地址\n  \"pluginHomepage\": '', // 自定义插件操作页面地址，默认为 `/plugin.xxx/\n  \"whistleConfig\": {\n    \"hideLongProtocol\": false, // 是否隐藏插件的长协议，设置为 true 后，Rules 界面里面配置该插件协议会显示已被删除\n    \"hideShortProtocol\": false, // 是否隐藏插件的短协议，设置为 true 后，Rules 界面里面配置该插件协议会显示已被删除\n    \"priority\": 0, // 默认值为0，插件优先级按更新时间从旧到新排序（越早更新的优先级越高），可通过此字段调整优先级顺\n    \"favicon\": '', // 插件 Tab 页上的 icon，可以为插件相对路径 `/public/xxx.png` 或绝对路径 `https://xxx` 或 `data:image/png;base64,xxx`\n    \"registry\": '', // 插件的 npm registry\n    \"noOption\": false, // 如果不存在操作界面，可以设置为 true，这样 Option 按钮会置灰\n    \"enableAuthUI\": false, // 插件的 auth hook 是否作用于插件操作界面（慎用）\n    \"tunnelKey\": '', // 字符串或字符串组，如果需要继续将请求通过 TUNNEL 代理到其它 HTTP Proxy 时，可以指定哪些请求头跟着过去\n    ...\n  },\n  ...\n}\n```\n\n## 插件页面内部路径规范\n插件页面的 URL 通常为以下格式：\n- `/plugin.xxx/path/yyy.html`\n- `/whistle.xxx/path/yyy.html`\n\n**根目录**为 `/plugin.xxx/` 或 `/whistle.xxx/`，因此：\n\n✅ 推荐使用相对路径，避免使用绝对路径（如 /path/to）。\n\n---\n\n#### **相对路径使用示例**  \n\n**场景 1：页面位于插件根目录**  \n**页面地址**：`/plugin.xxx/yyy.html`  \n**正确写法**：  \n- `./path/to`  \n- `path/to`  \n\n**错误写法**：  \n- ❌ `/path/to`（绝对路径，可能访问错误）  \n\n---\n\n**场景 2：页面位于子目录**  \n**页面地址**：`/plugin.xxx/a/b/c/yyy.html`  \n**正确写法**：  \n- `../../../path/to`（返回 3 级目录再进入目标路径）  \n\n**错误写法**：  \n- ❌ `/path/to`（绝对路径，可能访问错误）  \n- ❌ `path/to`（会基于当前目录 `a/b/c/` 查找，导致路径错误）  \n\n---\n\n**最佳实践**  \n1. **推荐使用 `./` 开头**，明确表示相对当前目录（如 `./assets/style.css`）。  \n2. **避免硬编码 `/` 开头的路径**，防止不同部署环境下路径失效。  \n3. **测试路径引用**：在本地和线上环境均验证资源加载是否正常。  \n\n---\n\n**常见问题**  \n❓ **Q：为什么不能用绝对路径？**  \n📌 **A**：插件可能部署在不同环境（如测试/生产/内嵌到其它项目中），绝对路径可能导致资源加载失败。  \n\n❓ **Q：有些构建工具，如 Vite 默认会将资源路径基于 base 设置进行解析。如果 base 设为 `/`，可能会将 `../` 优化为 `/`，如何处理？**  \n📌 **A**：尝试将 base 改成 `./`：\n  ``` js\n  // vite.config.js\n  export default defineConfig({\n    base: './', // 强制使用相对路径\n  });\n  ```\n  > 建议与后台路由配置同步优化\n\n\n## 发布插件\nWhistle 插件的发布方式与常规 NPM 包完全一致，只需遵循标准 NPM 发布流程：\n1. 登录 NPM（或企业私有源，后者需要设置企业私有源的 `npm config set registry https://xxx`）:\n    ``` sh\n    npm login\n    ```\n2. 在插件根目录执行发布命令：\n    ``` sh\n    npm publish\n    ```\n\n## 参考资料\n1. 示例源码仓库：\nhttps://github.com/whistle-plugins/examples\n1. 脚手架命令：https://github.com/avwo/lack\n"
  },
  {
    "path": "docs/docs/extensions/npm.md",
    "content": "# NPM 模块\nWhistle 可作为 NPM 模块集成到项目中，**强烈建议**在独立子进程中运行 Whistle，以避免与主进程相互影响，同时便于管理。\n\n## 安装依赖\n``` sh\nnpm i --save whistle pfork\n```\n\n## 独立服务模式\n1. 创建 Whistle 启动脚本（`/lib/whistle.js`）：\n    ``` js\n    const startWhistle = require('whistle');\n\n    module.exports = (options, callback) => {\n      startWhistle({ port: options.port }, () => callback());\n    };\n    ```\n    > 禁用插件、自定义插件目录、修改存储目录等启动参数参考：https://github.com/avwo/whistle/blob/master/index.d.ts\n2. 启动 Whistle 进程代码：\n    ``` js\n    const { fork } = require('pfork');\n    const path = require('path');\n\n    const script = path.join(__dirname, './whistle.js');\n    const options = {\n      script,\n      port: 8080,\n    };\n    const forkWhistle = (retry) => {\n      fork(options, (err, result, child) => {\n        if (err) {\n          // 初始化时报错，直接退出服务\n          if (!retry) {\n            throw err;\n          }\n          // 重试报错，延迟 100 毫秒再重试\n          return setTimeout(forkWhistle, 100);\n        }\n        // 子进程退出自动重试\n        child.once('close', () => forkWhistle(true));\n      });\n    };\n\n    forkWhistle();\n    ```\n\n这种方式启动的 Whistle，跟通过命令行独立启动 Whistle 或 Whistle 客户端的用法一样。\n\n\n## 内部服务模式（推荐）\n\n1. 创建 Whistle 启动脚本（`/lib/whistle.js`）：\n    ``` js\n    const startWhistle = require('whistle');\n    const http = require('http');\n\n    let curPort = 30013;\n\n    const getPort = (callback) => {\n      const server = http.createServer();\n      server.on('error', () => {\n        if (++curPort % 5 === 0) {\n          ++curPort;\n        }\n        getPort(callback);\n      });\n      server.listen(curPort, '127.0.0.1', () => {\n        server.removeAllListeners();\n        server.close(() => callback(curPort));\n      });\n    };\n\n    module.exports = (options, callback) => {\n      // 获取一个随机端口\n      getPort((port) => {\n        // 在随机端口启动 Whistle，启动成功后将启动参数传给父进程\n        const options = {\n          port,\n          host: '127.0.0.1',\n          storage: 'xxx', // 建议设置新存储目录，确保不跟其它实例的目录冲突\n        };\n        startWhistle(options, () => callback(options));\n      });\n    };\n    ```\n    > 参考：https://github.com/Tencent/nohost/blob/master/lib/whistle.js\n    > \n    > Whistle 会\n2. 启动 Whistle 进程方法（`/lib/fork.js`）：\n    ``` js\n    const { fork } = require('pfork');\n    const path = require('path');\n\n    const script = path.join(__dirname, './whistle.js');\n    const options = {\n      script,\n    };\n\n    module.exports =() => {\n      return new Promise((resolve, reject) => {\n        fork(options, (err, result) => {\n          if (err) {\n            return reject(err);\n          }\n          resolve(result);\n        });\n      });\n    };\n    ```\n    > 参考：https://github.com/Tencent/nohost/blob/master/lib/main/whistleMgr.js\n3. 将项目中的指定请求转发到 Whistle：\n   ``` js\n    const { createServer } = require('http');\n    const forkWhistle = require('./fork');\n\n    const server = createServer(async (req, res) => {\n      try {\n        const { host, port } = await forkWhistle();\n        // 将请求转发到 host、port\n      } catch (e) {\n        // handle errors\n      }\n    });\n\n    server.listen(8010);\n   ```\n   > 参考：https://github.com/Tencent/nohost/blob/master/lib/index.js#L160\n\n这种方式启动的 Whistle，端口是随机的且是按需启动的，外部请求不直接访问 Whistle，需要通过所在项目的服务进行转发，用法更灵活，更推荐项目使用这种方式集成 Whistle。\n\n## 相关文档\n\n1. Whistle 的所有启动参数可查看源代码的类型定义：https://github.com/avwo/whistle/blob/master/index.d.ts\n2. pfork：https://github.com/avwo/pfork\n3. 完整示例：https://github.com/Tencent/nohost\n\n"
  },
  {
    "path": "docs/docs/extensions/usage.md",
    "content": "# 使用插件\n\n## 安装插件\n\n1. 点击左侧导航栏的 Plugins 标签页\n2. 点击顶部的 Install 按钮\n3. 在弹出窗口中输入插件名称（支持同时安装多个插件）：\n   - 多个插件用空格或换行符分隔\n   - 可指定自定义 npm 镜像源：\n     - 直接在插件名后添加 `--registry=镜像地址`\n     - 或从下拉列表选择历史使用过的镜像源\n\n<img width=\"1000\" alt=\"install plugins\" src=\"/img/install-plugins.png\" />\n\n> 文本框中的冗余内容不会影响操作结果，Whistle 会自动过滤并仅提取插件名称及 registry 安装信息。若对话框未显示 Install 按钮，请重新安装全局 Node.js：https://nodejs.org/\n\n安装成功后，稍等片刻即可在插件列表中查看新安装的插件：\n\n<img width=\"1000\" alt=\"plugins\" src=\"/img/plugin-list.png\" />\n\n\n## 自定义协议\n\n每个插件可注册两种协议类型，配置方式跟普通协议一样：\n``` txt\n# 长协议\npattern whistle.plugin-name://value [inludeFilter://pattern1 ... excludeFilter://pattern2 ...]\n# 短协议\npattern plugin-name://value [inludeFilter://pattern1 ... excludeFilter://pattern2 ...]\n```\n\n## Hooks 功能\n\nWhistle 将匹配插件规则的请求与插件对应 Hook 进行交互，可实现以下功能：\n> 插件协议的具体功能看各个插件的帮助文档，插件也可以选择隐藏这些协议\n1. 自动生成并下发 HTTPS 证书\n2. 对代理请求的认证控制，要求用户提供凭证才能访\n3. 实时获取请求头、响应状态码等元数据\n4. 对请求动态设置规则\n5. 将请求转发给插件，由插件完全控制请求处理流程（需要匹配短协议才能实现）\n\n\n## pipe 功能\n有些请求/响应内容可能被加密或 protobuf 序列化无法看到明文，可通过以下两种方式实现内容查看与修改：\n1. **插件全权处理**\n   - 将请求直接转发至插件\n   - 插件完全控制处理流程\n   - 注意事项：\n     - Whistle 内置规则将失效\n     - 需插件自行实现所有处理逻辑\n     - 如需在抓包界面显示解密内容，插件必须调用 Whistle API 回传数据\n2. **管道流式处理（推荐）**\n   - 通过 pipe 协议建立处理管道\n   - 请求/响应流程：\n     - 进入 Whistle 时：插件解密\n     - 离开 Whistle 时：插件加密\n   - 优势：\n     - 保持明文请求特性\n     - 完整支持 Whistle 内置规则\n     - 抓包界面可直接查看明文\n\n**技术说明：**\n- pipe 流功能为可选实现，各插件支持情况请查阅具体文档\n- 方案二处理后的请求完全等同于普通明文请求\n\n## 操作界面\n\n#### 访问插件操作界面\n在 Whistle 插件管理面板中，您可以通过以下任一方式访问插件功能：\n1. 点击插件条目右侧的 `Option` 按钮\n2. 在插件列表直接单击插件名称\n\n#### 界面特性\n**呈现形式：** 模态对话框或独立标签页\n\n**核心能力：**\n1. 提供可视化配置和管理功能\n2. 支持与 Whistle 核心的实时交互\n3. 显示插件运行状态、请求统计数据、日志信息等\n\n#### 技术说明\n1. 插件本质上是与 Whistle 直接交互的 HTTP Server\n2. 操作界面的具体功能由各插件自行实现\n3. 插件可选择不提供操作界面（仅提供后台服务或[命令行操作](../cli#w2-exec)）\n\n## 扩展 Whistle 界面\n\n插件可通过以下方式增强 Whistle 的用户界面功能：\n\n#### Network 扩展\n1. 抓包列表上下文菜单\n    > 在抓包列表项右键菜单中添加自定义操作\n2. 主视图标签页\n    > 在右侧主面板区创建一级功能标签页\n2. Inspectors 分析工具扩展\n    > 在 Inspectors 面板下添加：\n    >   - 二级功能标签页\n    >   - 三级详细视图标签页\n3. Composer 请求构造器扩展\n    > 在 Composer 工具区添加二级功能标签页\n4. Tools 工具集扩展\n    > 在 Tools 功能区添加二级功能标签页\n\n#### Rules 扩展\n左侧列表上下文菜单\n> 在左侧规则文件列表的右键菜单中添加功能项\n\n#### Values 扩展\n左侧列表上下文菜单\n> 在左侧变量文件列表的右键菜单中添加功能项\n\n**实现说明：**\n- 各插件可根据功能需求选择适当的扩展位置\n- 具体扩展功能和实现方式请参阅各插件的开发文档\n- 未使用的扩展点不会影响插件基础功能\n\n## 插件内置规则\n\n插件支持通过以下规则文件扩展系统功能：\n\n1. **全局规则 (rules.txt)**\n   - 自动加载时机： 插件初始化时\n   - 作用范围： 全局请求\n   - 优先级： 低于 Rules 界面配置的规则\n   - 典型用途： 插件默认规则配置\n2. **私有规则 (_rules.txt)**\n   - 触发条件： 匹配插件自定义协议的请求\n   - 生效阶段： 请求处理流程\n   - 执行顺序： 在全局规则之后应用\n   - 典型用途： 请求预处理/改写\n3. **响应阶段规则 (resRules.txt)**\n   - 触发条件： 匹配插件自定义协议的请求\n   - 生效阶段： 响应处理流程\n   - 执行顺序： 在全局规则之后应用\n   - 典型用途： 响应后处理/改写\n\n\n插件支持灵活的内置规则配置机制：\n- **可选性：** 所有内置规则均为可选配置\n- **按需设置：** 插件只需声明必要的规则\n- **规则查看：** 通过插件管理界面的 Rules 按钮可查看当前插件配置的所有规则\n> 注意：未配置规则的插件仍可正常使用其他功能\n\n## 更新卸载插件\n\n**在 Whistle 插件管理界面中：**\n- **更新插件：** 点击插件条目右侧的 `Update` 按钮\n- **卸载插件：** 点击插件条目右侧的 `Uninstall` 按钮\n"
  },
  {
    "path": "docs/docs/faq.md",
    "content": "# 常见问题\n\n遇到问题或建议请提 [issue](https://github.com/avwo/whistle/issues/new)\n\n## Q：抓包列表中为何显示 `Tunnel to`？ {#tunnel-to}\n\n通常出现该提示源于以下几种情况。请根据您的具体场景参照对应的解决方案：\n\n1.  **普通的 TCP 连接（无法解析为 HTTP/HTTPS 请求）**\n2.  **未启用 HTTPS 抓包（最常见）**\n    *   **原因**：您未安装根证书或未开启 `Enable HTTPS` 选项。因此，Whistle 无法解密 HTTPS 流量，只能将其显示为加密隧道。\n    *   **解决方案**：请安装根证书并开启 `Enable HTTPS` 选项。\n        *   **Whistle 客户端 (GUI)**：请遵循官方仓库中的安装配置指南：[https://github.com/avwo/whistle-client](https://github.com/avwo/whistle-client)\n        *   **命令行版本**：请参考官方仓库说明：[https://github.com/avwo/whistle](https://github.com/avwo/whistle)\n        *   **移动端抓包**：移动设备配置步骤，请参见：[移动端抓包](./mobile)\n\n3.  **捕获错误（`captureError`）**\n    *   **原因**：HTTPS 解密过程中发生错误。\n    *   **解决方案**：请参阅下方的专门问答进行故障排查：[Q：如何解决 `captureError`？](#capture-error)\n\n4.  **发往 IP 地址的请求**\n    *   **原因**：该请求使用了字面 IP 地址（例如 `https://192.168.1.1`）而非域名。对于此类请求，大多数客户端不会发送 SNI 信息，导致 Whistle 无法自动将其识别为 HTTPS 请求。\n    *   **解决方案**：您需要显式配置规则来告知 Whistle 对该 IP 进行 HTTPS 捕获。详情请参见下方问答：[Q：如何捕获发往 IP 地址的 HTTPS 请求？](#capture-ip)\n\n## Q：抓包列表中出现 `captureError` 的原因？{#capture-error}\n1. 发出请求的客户端没有安装根证书，安装方法参考：\n   - PC 端：[安装根证书](./)\n   - 移动端：[安装根证书](./mobile)\n2. `ssl pinning` 问题\n   - 对该域名的 HTTPS 请求不解密：`域名 disable://capture` 或只针对指定客户端的请求 `域名 disable://capture includeFilter://reqH.user-agent=/xiaomi/i`\n   - 使用可以规避 `ssl pinning` 的系统或模拟器运行客户端\n   - 寻找其它规避措施 https://blog.csdn.net/chiehfeng/article/details/134033846\n3. 系统信任的根证书默认对 Firefox 无效，需要单独为 Firefox 配置证书\n  > **方案1：为 Firefox 单独安装证书**\n  >\n  > 在 Firefox 设置中：\n  > - 进入 选项 > 隐私与安全 > 证书\n  > - 点击 \"查看证书\" → \"证书机构\"\n  > - 导入下载的 .cer 文件\n  > - 勾选 \"信任此CA\" 所有选项\n  >\n  > **方案2：强制 Firefox 使用系统证书（推荐）**\n  >\n  > - 搜索首选项：security.enterprise_roots.enabled\n  > - 将值改为 true\n  > - 重启浏览器生效\n\n## Q：为何安装了根证书并开启了 `Enable HTTPS`，对 IP 地址的 HTTPS 请求仍无法解密？{#capture-ip}\n这是因为对于直接使用 IP 地址发起的 HTTPS 请求，客户端通常在握手阶段不会发送 SNI 信息。缺少 SNI 使得 Whistle 无法快速判断这是一个需要解密的 HTTPS 请求，且大部分这类请求都是普通 TCP 请求，从而可能将其视为普通的 TCP 流量处理。可以通过配置规则，显式地告知Whistle将特定IP的请求识别为HTTPS并进行解密捕获：\n``` txt\nip[:port] enable://captureIp\n\n# 或\nip[:port] enable://capture\n```\n\n## Q：如何配置双向认证（mTLS）的 HTTPS 请求？\n\n客户端证书设置参考：[@clientCert://](./rules/@)\n\n## Q：如何查看 Whistle 运行过程中的日志？\n1. 界面 Network > Tools > Server 查看错误日志\n2. 导致进程 Crash 的异常日志文件：`~/.WhistleAppData/whistle.log`\n \n## Q：如何同时启多个 Whistle 实例？\n多实例运行时需满足：\n- 每个实例使用独立目录\n- 配置不同监听端口\n``` sh\nw2 start\n2 start -p 8100 -S storageName2\n```\n\n## Q：普通请求如何不通过代理直接访问 Whistle 不被当成内部请求？\nWhistle 默认会将所有发往代理端口（如 127.0.0.1:8899）的请求视为内部管理请求，可以使用 `/-/` 路径前缀绕过内部请求识别，如：\n1. `http://127.0.0.1:8899/-/xxx`：Whistle 自动转成普通请求 `http://127.0.0.1:8899/xxx`\n2. 通过配置规则将该请求转发到目标 URL：\n    ``` txt\n    http://127.0.0.1:8899/xxx https://www.test.com/xxx\n    ```\n\n## Q：Rules 如何支持多选？\n\n在 Rules 界面中打开 Settings 对话框，勾选 `Use multiple rules` 即可\n\n## Q：如何根据请求内容匹配规则？\n\n利用过滤器：\n- [includeFilter](./rules/includeFilter)\n- [excludeFilter](./rules/excludeFilter)\n\n## Q：iOS 安装完根证书还是无法打开 HTTPS 页面的可能原因？\n\n检查是否完成\"完全信任\"设置：设置 → 通用 → 关于本机 → 证书信任设置\n\n## Q：Android 安装完根证书还是无法打开 HTTPS 页面的可能原因？\n1. `ssl pinning` 问题\n   - 对该域名的 HTTPS 请求不解密：`域名 disable://capture` 或只针对指定客户端的请求 `域名 disable://capture includeFilter://reqH.user-agent=/xiaomi/i`\n   - 使用可以规避 `ssl pinning` 的系统或模拟器运行客户端\n   - 寻找其它规避措施 https://blog.csdn.net/chiehfeng/article/details/134033846\n2. 如果是自己公司的 APP，可以参考[Android 开发文档](https://developer.android.com/training/articles/security-config#base-config) 开启信任用户自定义根证书\n\n## Q：如何对 Whistle 转发的请求设置用户名和密码？\n1. Whistle 内部请求认证：`w2 start -n 用户名 -w 密码` 或自己[开发插件](./extensions/dev)，防止未授权操作规则和配置\n2. 代理请求的权限控制：需要借助插件 [whistle.proxyauth](https://github.com/whistle-plugins/whistle.proxyauth) 或自己[开发插件](./extensions/dev)\n\n## Q：如何添加自定义证书？{#custom-certs}\n\n进入证书管理页面\n1. 点击顶部菜单栏 HTTPS > View Custom Certs > Upload\n2. 上传证书文件\n     - 证书文件：必须使用 `.crt` 后缀\n     - 私钥文件：必须使用 `.key` 后缀\n     > 文件名要求：\n     >\n     > 普通域名证书\n     >\n     > `example.com.crt` ↔ `example.com.key`\n     > \n     > 根证书（必须严格命名）\n     > \n     > `root.crt` ↔ `root.key`\n\n## Q：版本更新问题{#update}\n> 客户端版本只需重新下载最新版本并安装即可：https://github.com/avwo/whistle-client\n\n**命令行版本更新：**\n``` sh\nnpm i -g whistle && w2 restart\n```\n> 遇到安装慢或安装失败的问题可以尝试改镜像：`npm i -g whistle --registry=https://registry.npmmirror.com && w2 restart`\n>\n> 遇到权限问题可以加 `sudo`：\n>\n> ``` sh\n> sudo npm i -g whistle\n> w2 restart\n> ```\n\n重启后命令行显示的 Whistle 版本与当前安装版本不符，可能是 Node.js 版本更新导致 PATH 路径变更。\n\n**解决方法：**\n1. 确认版本是否有问题：\n    ``` sh\n    w2 -V\n    ```\n2. 找命令路径（所有系统通用）\n    ``` sh\n    which w2  # Linux/Mac\n    where w2  # Windows\n    ```\n3. 清理冲突\n    ``` sh\n    # 删除旧版本（根据上一步找到的路径）\n    rm -f /usr/local/bin/w2  # 示例路径\n\n    # Windows示例（需管理员权限）\n    del \"C:\\Program Files\\nodejs\\w2.cmd\"\n    ```\n4. 恢复运行，操作完成后执行 `w2 -V` 查看版本是否更新：\n   - 如果输出的版本还有问题重复上述操作\n   - 如果命令 `w2` 找不到，请手动配置 PATH\n\n## Q：如何过滤抓包界面中的频繁轮询请求？\n\n频繁的轮询请求（如心跳检测、状态上报）会刷屏并干扰主要请求的分析。您可以通过以下步骤快速隐藏它们：\n\n在抓包列表中找到任意一个轮询请求，右键菜单选择合适的过滤规则：\n- `Settings / Exclude All Matching Hosts`：隐藏所有来自对应域名的请求（只对当前浏览器生效）\n- `Settings / Exclude All Matching URLs`：隐藏与当前 URL 匹配的所有请求（不包含请求参数，且只对当前浏览器生效）\n\n## Q：如何调试 WebSocket/TCP 请求？\n\n1. 通过界面操作：[Network](./gui/network#websocket)\n2. 通过规则：[frameScript](./rules/frameScript)\n3. 通过插件：[插件开发](./extensions/dev)\n\n## Q：如何修改 Whistle 文档？\n\nWhistle 文档源文件地址：https://github.com/avwo/whistle/tree/master/docs\n\n本地启动文档服务：\n``` sh\nnpm run docs:dev\n```\n\n## Q：如何反馈问题？\n\nNew issue：https://github.com/avwo/whistle/issues\n"
  },
  {
    "path": "docs/docs/getting-started.md",
    "content": "# 快速上手\n\n安装完成后\n- **客户端版本**：直接启动桌面应用程序\n- **命令行版本**：在浏览器中访问 http://local.whistlejs.com\n\n即可进入操作界面：\n\n<img width=\"1000\" alt=\"network\" src=\"/img/whistle.png\" />\n\n操作界面主要功能：\n- **Network**：实时抓包分析与请求重放/编辑等界面\n- **Rules**：修改请求/响应的规则配置界面\n- **Values**：操作内容配置界面（支持规则变量调用）\n- **Plugins**：插件管理界面\n\n## 界面操作示例\n\n1. 重放请求\n   > 点击请求并点击顶部 `Replay` 按钮，或请求列表右键菜单 Actions -> Replay\n   \n   <img width=\"720\" alt=\"replay request\" src=\"/img/replay-req.png\" />\n2. 编辑或构造请求\n   > 点击请求并点击顶部 `Edit` 按钮，或请求列表右键菜单 Actions -> Edit Request\n   \n   <img width=\"1000\" alt=\"edit request\" src=\"/img/edit-req.png\" />\n\n## 规则简介\n\n<img width=\"1000\" alt=\"set rules\" src=\"/img/rules.png\" />\n\nWhistle 通过配置简洁的规则，即可高效地修改与调试网络请求和响应。其所有规则都基于一个核心语法结构：\n\n``` txt\npattern operation [lineProps...] [filters...]\n```\n\n**规则解读**：当一个请求的 URL 与 `pattern` 匹配时，Whistle 就会对其执行 `operation` 所定义的操作。此外，您还可以：\n- 通过可选的 `lineProps` 为当前同一行的规则设置特殊属性\n- 通过可选的 `filters` 对已匹配的请求进行二次筛选\n\n| 名称      | 功能                                                         |\n| --------- | ------------------------------------------------------------ |\n| pattern   | 匹配请求 URL 的表达式（支持域名、路径、正则、通配符）：<br/>1.  `www.test.com`（域名）<br/>2. `https://www.test.com`（带协议域名）<br/>3. `www.test.com:8080`（带端口域名）<br/>4. `*.test.com`（带通配符域名）<br/>5. `www.test.com/path`（路径）<br/>6.  `https://www.test.com/path`（带协议路径）<br/>7.  `www.test.com/path?query`（带参数域名）<br/>8. `/api/i`（正则表达式）<br />9. ``^**.test.com/path/index.*.js``（通配符）<br />10. ``^https://**.test.com/path/index.*.js``（带协议通配符） |\n| operation | 要执行的具体操作，格式为 `操作协议://操作内容`：<br/>1. `reqHeaders://x-proxy=whistle`（设置请求头）<br/>2. `file:///User/xxx`（映射到本地文件） |\n| lineProps | 仅对当前规则生效的附加配置，用于提升规则优先级、细化匹配规则功能等行为（支持组合使用）：<br/>1. `lineProps://important`（提升当行规则优先级）<br/>2. `lineProps://safeHtml`（确保响应内容不是 JSON 对象）<br/>3. `lineProps://proxyHost`（让 `proxy` 和 `host` 协议同时生效） |\n| filters   | 在 pattern 匹配的基础上进行更精确的筛选（支持组合使用）：<br/>1. `includeFilter://b:cmdname=xxx`（保留请求体中含 `cmdname=xxx` 的请求，不区分大小写）<br/>2. `excludeFilter://reqH.user-agent=/android/i`（排除 User-Agent 包含 android 的请求，不区分大小写） |\n\n\n我们先从几个常见功能开始，直观感受 Whistle 规则的基本用法，详细解读稍后说明。\n\n## 本地替换\n\n在开发或问题排查过程中，经常需要将页面的静态资源、接口等请求转发到本地环境或指定的测试服务器。通过 Whistle 规则，可以方便地实现这类映射与代理。\n\n```txt\n# 1. 将页面请求转发到本地开发服务\nwww.example.com http://localhost:5173 excludeFilter://*/static excludeFilter://*/api\n\n# 2. 将静态资源映射到本地文件目录\nwww.example.com/static file:///User/xxx/statics\n\n# 3. 将接口请求转发到测试环境服务器\nwww.example.com/api 10.1.0.1:8080\n```\n\n#### 规则说明\n\n| 规则 | 作用 | 注意事项 |\n|------|------|----------|\n| `www.example.com/static file:///User/xxx/statics` | 将 `www.example.com/static/…` 路径下的所有请求映射到本地的 `/User/xxx/statics/…` 目录。 | 若本地无对应文件，会返回 404 错误。 |\n| `www.example.com/api 10.1.0.1:8080` | 将所有 `www.example.com/api/…` 的请求转发到测试服务器 `10.1.0.1:8080`。 | 转发后服务器收到的 URL 不变，仅主机和端口部分被替换。 |\n| `www.example.com http://localhost:5173 excludeFilter://*/static excludeFilter://*/api` | 将 `www.example.com` 的其他请求（除 static 和 api 路径外）全部转发到本地服务 `localhost:5173`。 | 该规则会将 **http 与 https** 请求都转发到 `http://localhost:5173`，服务器获取到的 URL 为转发后的地址。 |\n\n根据实际开发环境调整上述规则中的域名、本地路径或测试服务器地址即可快速实现请求转发，提升开发与调试效率。\n## 修改请求/响应内容\n在开发或调试时，将线上请求指向本地或测试环境。\n\n```txt\n# 场景一：将整个站点代理到本地开发服务器（排除静态和API路径）\nwww.example.com http://localhost:5173 excludeFilter://*/static excludeFilter://*/api\n\n# 场景二：将特定路径的静态资源映射到本地目录\nwww.example.com/static file:///User/xxx/statics\n\n# 场景三：将 API 接口转发到测试服务器\nwww.example.com/api 10.1.0.1:8080\n```\n\n**关键区别**：\n*   `file://`：将请求映射到**本地文件系统**，若文件不存在则返回404。\n*   `http://` 或 `ip:port`：将请求**代理转发**到另一台服务器，服务器看到的是原始URL。\n\n## 远程调试：查看 DOM 与日志\n\n使用内置的 **weinre** 和 **log** 协议，远程调试页面。\n\n```txt\n# 同时启用远程DOM调试(weinre)和日志捕获(log)\nhttps://www.qq.com weinre://test log://\n```\n\n1.  **访问 weinre 调试器**：启动规则后，将鼠标悬停在顶部菜单 `Weinre` 上，点击会话名（如 `myDebugSession`）即可打开调试界面。\n    <img src=\"/img/weinre-menu.png\" alt=\"打开weinre菜单\" width=\"360\" />\n2.  **开始调试**：在浏览器中访问目标页面（如 `https://www.qq.com`），在 weinre 界面选择该页面，即可像使用浏览器开发者工具一样检查 **Elements**、**Console** 等。\n    <img src=\"/img/weinre-main.png\" alt=\"weinre主界面\" width=\"800\" />\n3.  **查看完整日志**：所有页面 Console 输出及 JavaScript 错误会同步显示在 Whistle 主控制台的 **Network → 右侧 Tools → Console** 标签页中。\n    ![log功能演示](/img/log-basic.gif)\n  \n## 详细文档\n1. **[界面功能详解](./gui/network)**：深入了解 Network 等面板的所有功能。\n2. **[规则配置全集](./rules/rule)**：查阅所有支持的规则协议和高级用法。\n"
  },
  {
    "path": "docs/docs/gui/composer.md",
    "content": "# Composer 界面\n\nComposer 是 Whistle 提供的 HTTP 请求构造工具，可用于快速创建、修改和发送自定义请求，主要用途是对抓包数据快速编辑及模拟请求，满足联调过程中快速定位接口问题的需求。\n\n<img src=\"/img/composer.png\" alt=\"Composer 界面\" width=\"420\" />\n\n右键点击发送按钮：\n\n<img src=\"/img/right-click-send.png\" alt=\"右键点击发送按钮\" width=\"260\" />\n\n| 组件                     | 功能 |\n| ------------------------ | ---- |\n| **历史记录按钮**         | 显示或隐藏历史记录 |\n| **选择方法**       |   选择请求方法，支持 GET/POST/PUT 等多种种方法   |\n| **URL 输入框**           | 编辑或输入请求 URL径 |\n| **Params**               | 添加、修改、删除请求参数 |\n| **发送按钮**             | 执行当前请求 |\n| **Send Body Via File**  | 从本地文件加载请求内容，用于发送大块请求内容 |\n| **Replay Times**             | 设置重放次数，最多 100 次 |\n| **Show History**             | 显示历史记录，跟左侧的图标按钮功能一样 |\n| **Rules** | 是否启用下方输入框里面的规则|\n| **Whistle** | 否启用 Whistle 里面配置的规则|\n| **Pretty** |   格式化显示内容   |\n| **Body** |   是否包含请求体（GET/HEAD/OPTIONS 等方法会自动忽略请求体）   |\n| **HTTP/2** | 是否使用 HTTP/2 协议 |\n| **Import**  | 导入请求数据 |\n| **Export**  | 导出请求数据 |\n| **AsCURL** | 生成可执行的 cURL 命令 |\n| **规则输入框** |   自定义规则，只对当前构造的请求生效   |\n| **请求头** |   自定义请求头   |\n| **请求内容** |  自定义请求内容    |\n\n## 保存请求/响应数据\n1. 点击发送时自动保存到 Composer 历史记录里面（最多保存 64 条记录）\n2. 在抓包列表中右键抓包数据并点击 Save 保存\n"
  },
  {
    "path": "docs/docs/gui/console.md",
    "content": "# Console 界面\n\nConsole 界面是 [log 规则](../rules/log) 的可视化展示平台，专门用于监控和分析：\n- 页面 JavaScript 错误\n- `console.log`、`console.warn` 等控制台输出\n\n<img src=\"/img/console.png\" alt=\"Console 界面\" width=\"360\" />\n\n| 能区                 | 功能说明                                                     |\n| -------------------- | ------------------------------------------------------------ |\n| **All logs**         | 显示所有日志分类（对应 `log://id` 中的 id），点击分类快速过滤特定页面的日志 |\n| **All levels**       | 按日志级别过滤（Debug/Log/Info/Warn/Error/Fatal）            |\n| **Expand JSON Root** | 自动展开 JSON 数据的首层结构                                 |\n| **ecord**            | 开始/停止/暂停日志记录                                       |\n| **Import**           | 导入历史日志文件（.json/.txt）                               |\n| **Export**           | 导出当前日志                       |\n| **Clear**            | 清空当前日志                                               |\n\n"
  },
  {
    "path": "docs/docs/gui/https.md",
    "content": "# HTTPS 对话框\n\nHTTPS 设置对话框用于配置 HTTPS 抓包和证书管理，界面如下：\n\n<img src=\"/img/https.png\" alt=\"HTTPS 对话框\" width=\"380\" />\n\n| 功能/选项                                 | 说明                                 | 备注                                         |\n| ----------------------------------------- | ------------------------------------ | ------------------------------------------------- |\n| **Download RootCA**                       | 下载 Whistle 根证书到本地            | 首次安装或证书过期时使用                          |\n| **rootCA.xxx 选择框**                     | 切换证书格式：`.crt`、`.cer`、`.pem` | 当某种格式证书安装失败时，尝试其他格式            |\n| **二维码地址选择框**                      | 切换移动端证书下载地址               | 方便手机扫码下载证书，详见[移动端抓包](../mobile) |\n| **Enable HTTPS (Capture Tunnel Traffic)** | 开启 HTTPS 流量解密（需安装根证书）  | 也可以通过规则控制： `enable://https` `disable://https` |\n| **Enable HTTP/2**                         | 启用 HTTP/2 协议支持（默认开启）     | 也可以通过规则控制： `enable://http2` `disable://http2` |\n| **Custom Certs Settings**                 | 查看、上传或删除用户自定义证书    | 不支持直接上传根证书，如果需要自定义根证书，需放到自定义证书目录并重启 Whistle 才能生效 |\n\n\n## Custom Certs Settings {#custom-certs}\n点击 **Custom Certs Settings** 按钮，即可打开自定义证书管理面板。在此面板中，您可以上传自己正式业务使用的服务器证书（常用于解决 SSL Pinning 检测问题）。\n\n证书文件必须**成对上传**，即每次上传需包含一个密钥文件和一个证书文件。支持的文件配对格式如下（实际文件名可自定义，但扩展名需符合要求）：\n\n1. `.key` 文件 + `.cer` 文件  \n2. `.key` 文件 + `.crt` 文件  \n3. `.key` 文件 + `.pem` 文件\n\n例如：\n- `server.key` 与 `server.cer`\n- `mycert.key` 与 `mycert.crt`\n- `cert.key` 与 `cert.pem`\n\n请确保每一对证书与密钥文件在内容上相匹配，否则可能导致证书配置失败。\n"
  },
  {
    "path": "docs/docs/gui/network.md",
    "content": "# Network 界面\n\n查看、管理抓包数据的界面\n\n<img src=\"/img/network.png\" alt=\"Network 界面\" width=\"1000\" />\n\n\n## 底部搜索框{#filter}\n\n搜索框支持多种高级过滤方式，可通过前缀快速定位特定类型的请求：\n\n| 前缀            | 过滤目标                            | 示例                              |\n| --------------- | ----------------------------------- | --------------------------------- |\n| `无前缀`        | 请求 URL                            | `example.com/api` `/abc=123/i`    |\n| `m:pattern`     | 请求方法                            | `m:pos` `m:/get\\|post/i`  |\n| `h:pattern`     | 请求 / 响应头原始文本               | `h:image/` `h:/cookie:\\s*test=123/i` |\n| `H:pattern`     | 请求头 Host 字段                    | `H:example.com` `H:/test/i`       |\n| `b:pattern`     | 请求 / 响应体原始内容               | `b:\"error\":true` `b:/\\d{3}/`     |\n| `i:pattern`     | 客户端 IP 或服务的 IP               | `i:11.2`  `i:/^11\\.2/`         |\n| `s:pattern`     | 响应状态码                          | `s:404`  `s:/5\\d{2}/`             |\n| `t:pattern`     | 响应头 Content-Type 内容              | `t:json` `t:/html\\|xml/i` |\n| `mark:pattern`  | 右键菜单手动 Mark 的请求 URL        | `mark:example.com` `mark:/\\d{5}/` |\n| `app:pattern`   | 按 APP 名称过滤                     | `app:wechat` `app:chrome`         |\n| `fc:pattern`    | Composer发出的请求 URL              | `fc:/test/` `fc:www.test.com`     |\n| `e:pattern`     | 出错的请求 URL                      | `e:timeout`   `e:/abort/i`                  |\n| `style:pattern` | [style](../rules/style) 操作内容 | `style:italic` `style:/ita/i`       |\n\n`pattern` 为**关键字**或**正则表达式**，多条件 \"与\" 搜索：\n``` txt\nb:/\"success\":false/ m:POST s:200 H:api.example.com\n```\n\n## Settings\n**Exclude Filter**（排除过滤器） 和 **Include Filte**（包含过滤器） 允许对未在 Network 显示过的抓包数据进行精细化筛选，支持多条件组合过滤：\n\n| **过滤类型**       | **作用**                                 |      |\n| ------------------ | ---------------------------------------- | ---- |\n| **Include Filter** | **只保留**满足条件的请求（相当于白名单） |      |\n| **Exclude Filter** | **排除**满足条件的请求（相当于黑名单）   |      |\n\n1. 单个输入框内的条件\n   - 分隔方式：用 空格 或 换行符 分隔多个条件\n   - 逻辑关系：条件间是 \"或\"（OR） 关系\n    > 示例：`example.com api.test.com` → 匹配 example.com 或 api.test.com 的请求\n2. 多个输入框之间的条件\n   - 逻辑关系：不同输入框之间是 \"与\"（AND） 关系\n   - 示例：\n      - Include Filter 输入：`m:GET`\n      - Exclude Filter 输入：`h:/cookie:[^\\r\\n]*test=123/`\n      - 最终效果：只保留 GET 请求，且排除请求头 cookie 里面包含 `test=123`的请求\n\n**支持的过滤条件**：\n\n| **语法**    | **作用**                        | **示例**                                     |\n| ----------- | ------------------------------- | -------------------------------------------- |\n| `无前缀`    | 匹配请求 URL 包含该关键字的请求 | `.example.com`                               |\n| `m:pattern` | 匹配请求方法                    | `m:POST`（匹配 POST 请求）                   |\n| `h:pattern` | 匹配原始请求头                  | `h:/cookie:[^\\r\\n]*test=123/`（匹配 cookie） |\n| `H:pattern` | 匹配请求头 Host 字段            | `H:example.com` `H:/test/i`                  |\n| `i:pattern` | 匹配客户端 IP                   | `i:11.2`  `i:/^11\\.2/`                       |\n\n与 Network 列表底部搜索框不同：\n\n| **功能**         | **Network 底部搜索框**     | **Settings 中的 Filter**             |\n| ---------------- | -------------------------- | ------------------------------------ |\n| **匹配范围**     | 请求 + 响应阶段的所有数据  | **仅匹配请求阶段**（不包含响应数据） |\n| **生效时机**     | 实时过滤已显示和未来的请求 | **仅对未来新请求生效**               |\n| **历史数据影响** | 可过滤已存在的抓包记录     | **不影响已显示的请求**               |\n\n**其它选项：**\n\n| 项名称                                    | 功能说明                                                 |\n| ----------------------------------------- | -------------------------------------------------------- |\n| **Network Columns**                       | 自定义抓包列表显示的列（如状态码、方法、大小等）         |\n| **Maximum Rows**                          | 设置同时显示的最大抓包数量（防止内存溢出）               |\n| **Viewing only your computer's requests** | 只显示本机发出的请求（过滤其他设备/远程请求）            |\n| **ViewAll in new window**                 | 点击\"View All\"时在新窗口打开完整内容（适合大响应体查看） |\n| **Show Tree View**                        | 以树状结构展示请求（按域名/路径分组）                    |\n\n## 详情面板{#detail}\n\n1. Overview：匹配的规则及请求的一些基本信息\n2. Inspectors：请求/响应头及内容的详细信息\n3. Timeline：请求的性能信息\n4. Composer：[Componser 界面](./https)\n5. Tools：一些工具\n   - Console：显示远程界面 console 日志的平台，详见：[Console](./console)\n   - Server：Whistle 运行过程发生的一些异常\n   - Toolbox：一些常用的工具方法\n\n## 其它菜单\n1. Replay：重新请求选中的抓包数据\n2. Edit：将抓包数据填充到右侧的 Composer\n3. 右上角箭头按钮：切换到上下面板的模式，适合竖屏显示器\n\n## 调试 WebSocket/TCP {#websocket}\n选中 WebSocket/TCP 请求后可以通过详情面板 Inspectors/Frames 调试请求/响应数据：\n\n![Frames](/img/frames.png)\n\n支持：\n1. 通过顶部菜单按钮 `Send/Receive`、`Pause`、`Ignore` 开启、暂停、忽略 WebSocket/TCP 的数据传输\n2. 通过底部详情面板的 Composer 发送自定义数据到客户端或服务器\n\n如果需要在建立连接后就设置 `Ignore` 或 `Pause` 可以用规则：\n``` txt\nwww.example.com/path enable://ignoreReceive\nwww.example.com/path enable://ignoreSend\nwww.example.com/path enable://pauseReceive\nwww.example.com/path enable://pauseSend\n```\n\n其它调试方式：\n1. 通过规则：[frameScript](../rules/frameScript)\n2. 通过插件：[插件开发](../extensions/dev)\n"
  },
  {
    "path": "docs/docs/gui/online.md",
    "content": "# Online 对话框\n\nOnline 对话框提供 Whistle 代理服务的实时运行状态仪表盘，帮助您全面掌握代理服务健康状况。\n\n<img src=\"/img/online.png\" alt=\"Online 对话框\" width=\"1000\" />\n\n## 服务状态概览\n\n| 指标           | 说明                                                |\n| -------------- | --------------------------------------------------- |\n| **运行时间**   | Whistle 服务的持续运行时长（格式：天:时:分:秒）     |\n| **内存占用**   | 当前进程内存使用情况（包含堆内存/外部内存详细数据） |\n| **CPU 使用率** | 进程 CPU 占用百分比（实时刷新）                     |\n\n## 流量统计\n\n| 指标           | 说明                                        |\n| -------------- | ------------------------------------------- |\n| **请求总数**   | 代理服务累计处理的请求数量                  |\n| **实时吞吐量** | 当前每秒处理的请求数（QPS）                 |\n\n\n## 功能选项\n\n| 选项  | 说明 | 技术细节 |\n| ---- | ---- | -------- |\n| **Disable dark mode** | 关闭暗黑主题，使用浅色界面 | 默认开启暗黑模式 |\n| **IPv6-only network** | 强制返回 IPv6 地址（AAAA记录） | 需要网络支持 IPv6，失败时不会回退 IPv4 |\n| **Verbatim network** | 严格按 DNS 配置顺序返回 IP | 勾选：保持原始 DNS 响应顺序，勾选：智能排序（IPv4优先） |\n| **IPv4-first network** | 优先返回 IPv4 地址（A记录） | 兼容性优先模式，IPv6 作为备选 |\n"
  },
  {
    "path": "docs/docs/gui/plugins.md",
    "content": "# Plugins 界面\n\nPlugins 界面是 Whistle 的插件管理中枢，支持插件的全生命周期管理，包括安装、配置、更新和卸载等操作。\n\n<img src=\"/img/plugins.png\" alt=\"Plugins 界面\" width=\"1000\" />\n\n\n## 界面基本功能\n\n\n\n| 控件         | 功能 |\n| ------------ | ---- |\n| **ON**       | 关闭或打开所有插件，红色：开启中，灰色：关闭中 |\n| **Install**  |  安装插件，详见下文    |\n| **Checkbox** | 是否启用该插件  |\n| **插件名称** |  点击打开插件管理界面 |\n| **版本号**       |  点击打开插件帮助文档  |\n| **Option**       |   点击打开插件管理界面   |\n| **Rules**       |  打开插件自带的静态规则（`rules.txt`、`reqRules.txt`、`resRules.txt` 文件规则）  |\n| **Update**       |  更新插件  |\n| **Uninstall**       |  卸载插件   |\n| **Help**       |  点击打开插件帮助文档   |\n| **Sync**       |  同步插件里面的 Rules 和 Values  |\n\n\n## 安装流程\n1. 点击顶部 `Install` 按钮\n2. 输入插件名称（如 `whistle.inspect`）\n3. 选择 `npm registry`（默认 `--registry=https://registry.npmjs.org`）\n4. 点击 `Install` 开始安装\n\n<img width=\"1000\" alt=\"install plugins\" src=\"/img/install-plugins.png\" />\n"
  },
  {
    "path": "docs/docs/gui/rules.md",
    "content": "# Rules 界面\n\nRules 界面是 Whistle 的核心配置区域，用于管理所有代理规则及其分组。\n\n<img src=\"/img/rules.png\" alt=\"Rules 界面\" width=\"1000\" />\n\n## 界面基本功能\n\n| 控件         | 功能                                           |\n| ------------ | ---------------------------------------------- |\n| **ON**       | 关闭或打开规则，红色：开启中，灰色：关闭中     |\n| **Import**   | 导入规则                                       |\n| **Export**   | 导出规则                                       |\n| **Save**     | 保存并启用规则                                 |\n| **Create**   | 新建规则/分组                                  |\n| **Delete**   | 删除规则/分组                                  |\n| **Rename**   | 重命名规则/分组                                |\n| **Settings** | 设置编辑样式                                   |\n| **右键菜单** | 除了集成上面的菜单功能，还支持插件扩展菜单功能 |\n\n\n\n## 高级编辑功能\n\n**临时文件编辑**\n\n``` txt\n... protocol://temp/blank.js\n```\n- `protocol`：为[操作协议](../rules/protocols)\n- `temp/blank.js`：空白的临时文件\n\n**操作流程：**\n1. 输入协议操作内容的路径（如 `file://temp/blank.js`）\n2. Ctrl + 鼠标点击（Win）/ Cmd + 鼠标点击（Mac）路径\n3. 在弹出编辑器修改内容\n4. 保存后自动关联到规则\n\n<img src=\"/img//temp.png\" width=\"600\" />\n\n\n## 底部搜索框\n| 前缀            | 过滤目标                            | 示例                              |\n| --------------- | ----------------------------------- | --------------------------------- |\n| `无前缀`        | 规则名称和内容 | `test` `/test\\d/i`|\n| `k:pattern`    | 规则名称      | `k:test` `k:/test\\d/i` |\n| `v:pattern`    | 规则内容         | `v:test` `v:/test\\d/i` |\n"
  },
  {
    "path": "docs/docs/gui/shortcut.md",
    "content": "# 快捷键\n\n## Network\n1. `Ctrl[Command] + I`: 导入抓包数据\n2. `Ctrl[Command] + E`: 导出抓包数据\n3. `Ctrl[Command] + S`: 保存抓包数据\n4. `Ctrl[Command] + O`: 关闭或打开抓包\n5. `Ctrl[Command] + L`: 切换 Network 面板的左右或上下布局\n6. `Ctrl[Command] + .`: 打开 Settings\n7. `Ctrl[Command] + D`: 删除选中的抓包数据\n8. `Ctrl[Command] + B`: 切换抓包数据的树形或列表形式\n9. `Ctrl[Command] + Enter`: 重新请求选中的抓包数据\n10. `Ctrl[Command] + Shift + Enter`: 设置重新请求选中抓包数据的次数\n11. `Ctrl[Command] + A`: 强制中断选中的请求\n12.  `Ctrl[Command] + X`: 清空抓包数据\n13. `/ `: 聚焦到底部搜索框\n\n\n## Frames\n1. `Ctrl[Command] + Enter`: 重新请求 Frames 面板选中的请求数据\n2. `Ctrl[Command] + X`: 清空 Frames 面板的数据\n\n## Rules\n1. `Ctrl[Command] + I`: 导入 Rules\n2. `Ctrl[Command] + E`: 导出 Rules\n3. `Ctrl[Command] + S`: 保存 Rules\n4. `Ctrl[Command] + O`: 关闭或打开 Rules\n5. `Ctrl[Command] + L`: 切换显示编辑器的行数\n6. `Ctrl[Command] + .`: 打开 Settings\n7. `Ctrl[Command] + D`: 删除选中的 rules\n8. `/ `: 聚焦到底部搜索框\n\n## Values\n1. `Ctrl[Command] + I`: 导入 Values\n2. `Ctrl[Command] + E`: 导出 Values\n3. `Ctrl[Command] + S`: 保存 Values\n4. `Ctrl[Command] + L`: 切换显示编辑器的行数\n5. `Ctrl[Command] + .`: 打开 Settings\n6. `Ctrl[Command] + D`: 删除选中的 values\n7. `/ `: 聚焦到底部搜索框\n\n\n## Plugins\n1. `Ctrl[Command] + I`: 打开安装插件对话框\n2. `Ctrl[Command] + O`: 关闭或打开所有 Plugins\n\n## Global\n1. `Ctrl[Command] + <--`: 反向切换 Network、Rules、Values、Plugins\n2. `Ctrl[Command] + -->`: 切换 Network、Rules、Values、Plugins\n\n<!-- ## Service\n1. `Ctrl[Command] + J`: 打开服务（需要登录） -->\n\n## Client\n1. `Ctrl[Command] + R`：打开或关闭系统代理\n2. `Ctrl[Command] + Shift + R`：重启客户端\n3. `Ctrl[Command] + Q`：退出客户端\n4. `Ctrl[Command] + ,`：打开 Proxy Settings\n"
  },
  {
    "path": "docs/docs/gui/values.md",
    "content": "# Values 界面\n\n操作内容快捷配置界面\n\n<img src=\"/img/values.png\" alt=\"Values 界面\" width=\"1000\" />\n\n## 界面基本功能\n\n| 控件         | 功能                                           |\n| ------------ | ---------------------------------------------- |\n| **Import**   | 导入 Values                                       |\n| **Export**   | 导出 Rules                                      |\n| **Create**   | 新建操作内容/分组                                  |\n| **Delete**   | 删除操作内容/分组                                  |\n| **Rename**   | 重命名操作内容/分组                                |\n| **Settings** | 设置编辑样式                                   |\n| **右键菜单** | 除了集成上面的菜单功能，还支持插件扩展菜单功能 |\n\n## 底部搜索框\n| 前缀            | 过滤目标                            | 示例                              |\n| --------------- | ----------------------------------- | --------------------------------- |\n| `无前缀`        | 规则名称和内容 | `test` `/test\\d/i`|\n| `k:pattern`    | 规则名称      | `k:test` `k:/test\\d/i` |\n| `v:pattern`    | 规则内容         | `v:test` `v:/test\\d/i` |\n"
  },
  {
    "path": "docs/docs/gui/weinre.md",
    "content": "# Weinre 远程调试功能\n\nWeinre（Web Inspector Remote）是 Whistle 集成的网页远程调试工具，允许开发者在电脑上直接调试移动设备上的网页。\n\n<img src=\"/img/weinre.png\" alt=\"Weinre 菜单\" width=\"1000\" />\n\n## 使用方法\n1. 配置调试规则，在 Whistle 规则配置中添加：\n   ``` txt\n    pattern weinre://your-debug-id\n   ```\n2. 访问目标页面\n   - 在移动设备上打开/刷新需要调试的网页\n   - 确保设备与调试电脑在同一网络环境\n3. 在 Whistle 顶部菜单栏找到 Weinre 选项\n   - 鼠标悬停后选择你设置的 your-debug-id 子菜单\n   - 点击即可打开 Weinre 调试页面\n4. 可以为不同页面设置独立的debug-id，实现并行调试：\n    ``` txt\n    mobile.example.com weinre://mobile-debug\n    tablet.example.com weinre://tablet-debug\n    ```\n\n<img src=\"/img/weinre-menu.png\" width=\"360\" />\n\n"
  },
  {
    "path": "docs/docs/index.md",
    "content": "# 安装\n\n请根据您的操作系统，参考对应 GitHub 仓库的 README 文档完成安装。\n\n## 🖥️ 桌面系统安装\n1. 适用系统：macOS / Windows / Linux (Ubuntu / Fedora 等带图形界面系统)\n2. 推荐使用 Whistle 官方客户端：[https://github.com/avwo/whistle-client](https://github.com/avwo/whistle-client)\n\n## 💻 命令行安装\n1. 适用场景：无图形界面的 Linux 服务器 / Docker 容器环境 / 嵌入式 / IoT 设备等\n2. 使用 Whistle 命令行版本：[https://github.com/avwo/whistle](https://github.com/avwo/whistle)\n"
  },
  {
    "path": "docs/docs/mobile.md",
    "content": "# 移动端抓包\n\n移动端（含手机/平板）抓包调试需完成以下配置：\n1. 安装根证书（HTTPS 抓包必需）\n2. 设置系统代理\n\n## 安装根证书（HTTPS 抓包必需）\n\n#### 下载根证书：\n\n1. 点击 Whistle 界面顶部点击 HTTPS 按钮，弹出 HTTPS 设置对话框\n   \n   <img width=\"320\" alt=\"HTTPS Settings\" src=\"/img/https-settings.png\" />\n2. 使用手机相机扫描对话框中的二维码 → 点击弹出的链接（下载失败尝试切换其它二维码，直到扫描下载成功为止）\n   \n   <img width=\"320\" alt=\"Scan qrcode\" src=\"/img/https-qrcode.png\" />\n\n   > 如果 Android 自带浏览器无法下载，可尝试使用 Chrome 浏览器动下载地址：http://[电脑IP]:[Whistle端口]/cgi-bin/rootca，或通过 PC 下载后传输到手机\n   > \n   > 如果所有二维码都无法下载：\n   > - 检查设备与 Whistle 主机是否在同一局域网\n   > - 确认防火墙未拦截代理端口\n3. 下载成功后记录二维码地址的 IP 和端口供下面设置系统代理用，并按下面的方法安装和信任根证书\n\n#### 安装根信任证书：\n\n**iOS**\n\n1. 安装描述文件\n   - 前往：设置 → 通用 → VPN与设备管理\n   - 找到\"已下载的描述文件\"并安装\n2. 启用完全信任（此步骤必不可少）\n   - 前往：设置 → 通用 → 关于本机 → 证书信任设置\n   - 开启对 Whistle 根证书的完全信任\n\n    <img width=\"320\" alt=\"证书信任设置\" src=\"/img/https-trust.png\" />\n\n**Android**\n\n1. 前往：设置 → 安全 → 加密与凭据 → 安装证书 → CA证书\n2. 选择下载的证书文件\n3. 输入锁屏密码确认\n4. 为证书命名（如 \"Whistle\"）\n\n> **版本差异说明：**\n> \n> Android 12+：需在「更多安全设置」中操作\n> \n> 华为EMUI：需先关闭\"纯净模式\"\n> \n> 其他品牌：路径可能略有不同\n> \n\n## 设置系统代理\n\n1. 进入WiFi设置\n   - 前往：设置 → Wi-Fi（无线局域网）\n   - 点击当前连接网络旁的图标（Android 可能需要长按 Wi-Fi 名称）\n2. 配置手动代理\n   - 代理类型选择\"手动\"\n   - 服务器：填写上面成功下载证书二维码的 IP\n   - 端口：填写面成功下载证书二维码的端口（Whistle 默认端口为 `8899`）\n   - 保存设置\n\n<img width=\"320\" alt=\"设置代理\" src=\"/img/proxy-settings.jpg\" />\n\n\n## 安装根证书和设置代理成功标志\n\n1. 手机访问网页正常\n2. Whistle 能捕获 HTTPS 请求\n3. 无安全警告提示\n\n## 常见问题\n\n1. 证书不受信任\n   - 检查是否完成\"完全信任\"设置（iOS）\n   - 确认证书安装位置正确（Android）\n2. 代理不生效\n   - 打开看 Whistle 界面右上角 Online 对话框查看 IP 和端口是否有变更\n   - 不确定哪个 IP 可用，可以一个个试看看\n3. 特定App无法抓包\n   - 检查App是否使用自定义证书\n   - 尝试在AndroidManifest.xml中添加网络配置，详见：[https://developer.android.com/training/articles/security-config#base-config](https://developer.android.com/training/articles/security-config#base-config)\n"
  },
  {
    "path": "docs/docs/rules/@.md",
    "content": "# `@` 符号用法\n\n当您希望将规则存放在远程服务器或本地项目文件中，并让 Whistle 自动同步更新时，可以利用规则中的 `@` 符号实现。\n\n## 语法规则\n\n## 1. 引入规则文件 (`@path`)\n动态加载外部规则文件内容，Whistle 会定时从规则文件更新内容。\n\n**使用格式：** `@文件路径或URL`\n\n**支持类型：**\n- 远程 URL：`@https://example.com/rules.txt`\n- 本地文件：`@~/projects/rules.txt`（支持所有操作系统路径）\n- 插件接口：`@whistle.nohost/api/rules`（需插件实现接口）\n\n> **Windows 路径兼容性**：支持混用 `/` 和 `\\` 作为路径分隔符：\n> ```txt\n> @C:/whistle/rules.txt\n> @D:\\config\\proxy.rules\n> ```\n\n## 配置示例\n\n### 引入远程规则文件\n```txt\n@https://raw.githubusercontent.com/your-org/configs/master/whistle-rules.txt\n```\n\n### 引入本地项目规则\n```txt\n@/Users/username/projects/my-app/whistle-config.txt\n@~/dev/configs/rules.txt  # 使用用户目录\n```\n\n### 引入插件提供的规则\n```txt\n@whistle.nohost/api/rules\n```\n\n### 混合使用示例\n```txt\n# 引入团队共享的基础规则\n@https://team.example.com/configs/base-rules.txt\n\n# 引入个人项目特定规则\n@~/projects/my-app/whistle-overrides.txt\n\n```\n\n## 文件格式要求\n\n引入的外部文件应包含标准的 Whistle 规则语法：\n\n```txt\n# 远程规则文件示例\nwww.example.com proxy://127.0.0.1:8080\napi.example.com resCors://*\nstatic.example.com cache://3600\n```\n"
  },
  {
    "path": "docs/docs/rules/attachment.md",
    "content": "# attachment\n`attachment` 协议用于设置响应头中的 `Content-Disposition` 字段，使浏览器将服务器的响应直接视为附件下载，并将文件保存为 `attachment` 设定的名称，而不是在页面内展示。\n> 类似 Koa 的 `attachment` 方法：https://koajs.com/\n\n## 规则语法\n\n`attachment` 支持多种方式来指定下载文件名：\n\n### 1. 内联值（直接指定）\n直接在规则中写明文件名。\n```txt\npattern attachment://filename [lineProps...] [filters...]\n```\n**示例：**\n```txt\nhttps://example.com/report attachment://年度报告.pdf\n```\n\n### 2. 内嵌值（使用代码块）\n当文件名较长或需要复用时可使用此方式。在规则中引用一个自定义键，并在随后的代码块中定义其值。\n````txt\npattern attachment://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n季度数据-2024Q1.csv\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values` 中预先定义好的值。\n```txt\npattern attachment://{key-of-values} [lineProps...] [filters...]\n```\n**前提：** 在 `Values` 中存在一个名为 `key-of-values` 的键，其值为目标文件名。\n\n### 4. 文件/远程 URL 加载\n**当前不支持** 从本地文件或远程 URL 动态加载文件名。\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **filename** | 是 | 下载时显示的文件名。<br>• 例如：`document.pdf`, `export.xlsx`。<br>• 支持通过上述三种方式（内联/内嵌/Values）指定。<br>• ⚠️ 不支持从文件或远程 URL 加载。 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n\n## 配置示例\n\n### 基础示例\n让访问 example.com 首页时自动下载一个名为 `example.html` 的文件。\n```txt\nhttps://www.example.com/ attachment://example.html\n```\n\n### 配合过滤器使用\n仅当请求包含特定查询参数 `download=true` 时，才触发 PDF 文件下载。\n```txt\nhttps://api.example.com/document attachment://用户手册.pdf includeFilter:///[?&]download=true/\n```\n\n### 使用内嵌值\n当文件名比较复杂或需要注释时，使用内嵌代码块更清晰。\n````txt\nhttps://assets.example.com/data.json attachment://{my-file-name}\n\n``` my-file-name\n配置备份-20240514.json\n```\n````\n\n## 实现远程加载\n\n`attachment` 协议本身不支持从文件路径或远程 URL 加载值。如果需要实现远程加载功能，有以下两种替代方案：\n\n### 1. 通过插件扩展功能\n你可以开发插件来实现更复杂的文件名获取逻辑，比如从远程API动态获取文件名。具体开发方法请参考：[插件开发文档](../extensions/dev)\n\n### 2. 使用 @url 加载完整规则\n将包含 `attachment` 规则的完整配置文件存储在远程服务器上，然后通过 [`@url`](./@) 协议加载整个规则集。\n\n**示例：**\n1. 在远程服务器创建规则文件 `xxx.txt`：\n    ```txt\n    https://api.example.com/export attachment://remote-file.csv\n    https://static.example.com/docs attachment://文档.pdf\n    ```\n2. 在本地 Rules 配置中引用远程规则：\n    ```txt\n    @https://remote-url/xxx.txt\n    ```\n\n这样，远程文件中的 `attachment` 规则就会被加载生效。\n\n## 工作原理与关联协议\n\n1.  **核心原理**：`attachment` 协议本质上是自动设置响应头。\n    上面的示例完全等价于使用 [`resHeaders`](./resHeaders) 协议手动设置：\n    ```txt\n    https://www.example.com/ resHeaders://content-disposition=attachment;filename=\"example.html\"\n    ```\n\n2.  **优势**：相比于直接使用 `resHeaders`，`attachment` 语法更简洁、易读，专注于文件下载场景，自动处理文件名转义等细节。\n\n3.  **注意**：服务器本身返回的响应体内容不会改变。此规则只是通过修改响应头，指示浏览器如何处理这些内容。\n"
  },
  {
    "path": "docs/docs/rules/auth.md",
    "content": "# auth\n\n`auth` 协议用于快速修改请求头的 `Authorization` 字段，为匹配的请求自动添加 HTTP 基本认证（Basic Authentication）所需的凭证信息。\n\n## 规则语法\n\n`auth` 支持多种方式设置请求鉴权头：\n\n### 1. 内联值（直接指定）\n直接在规则中写明用户名和密码。\n```txt\npattern auth://username:password [lineProps...] [filters...]\n# 或\npattern auth://username=test&password=123 [lineProps...] [filters...]\n```\n**示例：**\n```txt\nhttps://example.com/api1/ auth://admin:secret\nhttps://example.com/api2/ auth://username=admin&password=secret\n```\n\n### 2. 内嵌值（使用代码块）\n当凭证信息较复杂或需要复用时可使用此方式。在规则中引用一个自定义键，并在随后的代码块中定义其值。\n````txt\npattern auth://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\nusername: admin\npassword: my secret password\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values` 中预先定义好的值。\n```txt\npattern auth://{key-of-values} [lineProps...] [filters...]\n```\n**前提：** 在 `Values` 中存在一个名为 `key-of-values` 的键，其值为包含用户名和密码的对象。\n\n### 4. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern auth://temp.json\n```\n\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `auth://temp.json`\n3. 在弹出的编辑对话框中输入响应内容\n4. 点击 `Save` 保存\n\n保存后规则会自动变为类似以下格式：\n```txt\nhttps://example.com/report auth://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.json\n```\n\n需要再次编辑时，用同样的方式点击该临时文件链接即可。\n\n### 5. 从文件或远程 URL 加载\n从本地文件或远程 URL 加载包含鉴权信息的 JSON 或简单 YAML 文件。\n```txt\n# 从本地文件加载\npattern auth:///User/xxx/auth.json\n\n# 从远程 URL 加载（支持 http 和 https）\npattern auth://https://config.example.com/auth.json\n```\n\n**文件格式要求：**\n文件内容应为 JSON 或简单 YAML 格式，包含 `username` 和 `password` 字段：\n```json\n{\n  \"username\": \"admin\",\n  \"password\": \"secret\"\n}\n```\n或\n```yaml\nusername: admin\npassword: secret\n```\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 鉴权凭证信息，支持多种格式：<br>• **直接格式**：`username:password` 或 `username=test&password=123`<br>• **对象格式**：包含 `username` 和 `password` 字段的对象<br>• 支持从本地文件或远程 URL 加载<br>• 支持内联、内嵌、Values、本地文件路径、远程 URL 引用 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n## 配置示例\n\n### 基础示例\n为 example.com 的 API 接口添加基本认证：\n```txt\nhttps://api.example.com/ auth://admin:secret\n```\n\n### 使用内嵌值\n当密码包含特殊字符或空格时，使用内嵌方式更安全：\n````txt\nhttps://internal.example.com/ auth://{prod-credentials}\n\n``` prod-credentials\nusername: service-account\npassword: P@ssw0rd!2024\n```\n````\n\n### 从文件加载\n从本地配置文件加载鉴权信息：\n```txt\nhttps://example.com/api/ auth:///Users/john/config/auth.json\n```\n\n### 配合过滤器使用\n仅对 POST 请求添加鉴权：\n```txt\nhttps://example.com/api/ auth://admin:secret includeFilter://m:POST\n```\n\n### 引用 Values 中的配置\n假设在 Values 中已有 `api-auth` 配置：\n```txt\nhttps://example.com/api/ auth://{api-auth}\n```\n\n## 工作原理与关联协议\n\n1. **核心原理**：`auth` 协议会自动计算用户名和密码的 Base64 编码，并设置 `Authorization` 请求头。\n\n   上面的示例等价于使用 [`reqHeaders`](./reqHeaders) 协议手动设置：\n   ```` txt\n   https://example.com/api/ reqHeaders://{auth.txt} # 内容有空格，不能内联\n\n   ``` auth.txt\n   authorization: Basic YWRtaW46c2VjcmV0\n   ```\n   ````\n   其中 `YWRtaW46c2VjcmV0` 是 `admin:secret` 的 Base64 编码。\n\n2. **优势**：相比于直接使用 `reqHeaders`，`auth` 语法更简洁直观，无需手动计算 Base64 编码值。\n\n## 注意事项\n- `auth` 协议仅支持 HTTP 基本认证（Basic Authentication）\n- 对于更复杂的认证方式（如 Bearer Token、OAuth 等），请使用 [`reqHeaders`](./reqHeaders) 协议直接设置相应的 `Authorization` 头\n- 从远程 URL 加载时，请确保目标 URL 安全可靠\n"
  },
  {
    "path": "docs/docs/rules/cache.md",
    "content": "# cache\n\n`cache` 协议用于快速设置响应的缓存控制头，帮助控制浏览器和服务器的缓存行为。可以为匹配的响应设置缓存策略，包括：\n- 设置固定的缓存时间\n- 禁用缓存（`no-cache`）\n- 完全禁止存储（`no-store`）\n\n这可以帮助优化网站性能或确保特定资源不会被缓存。\n\n## 规则语法\n\n`cache` 支持多种方式设置缓存策略：\n\n### 1. 内联值（直接指定）\n直接在规则中指定缓存策略。\n```txt\n# 设置缓存5秒，支持正整数、0、负整数\npattern cache://5 [lineProps...] [filters...]\n\n# 设置缓存头：`cache-control: no-cache`、`pragma: no-cache`\n# `no-cache` 可简写为 `no`\npattern cache://no-cache [lineProps...] [filters...]\n\n# 设置缓存头：`cache-control: no-store`、`pragma: no-cache`\npattern cache://no-store [lineProps...] [filters...]\n```\n\n**示例：**\n```txt\nhttps://example.com/api/data cache://no-cache\nhttps://example.com/static/js cache://86400  # 缓存24小时\n```\n\n### 2. 内嵌值（使用代码块）\n当需要复用缓存策略或策略较复杂时可使用此方式。\n````txt\npattern cache://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\nno-cache\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values` 中预先定义好的缓存策略。\n```txt\npattern cache://{key-of-values} [lineProps...] [filters...]\n```\n**前提：** 在 `Values` 中存在一个名为 `key-of-values` 的键，其值为缓存策略。\n\n### 4. 文件/远程 URL 加载\n**当前不支持** 从本地文件或远程 URL 动态加载缓存配置。\n\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 缓存策略值：<br>• **数字**：缓存秒数（如 `60` 表示缓存60秒）<br>• **no-cache** 或 **no**：允许缓存但每次需重新验证<br>• **no-store**：完全禁止缓存<br>• 支持内联、内嵌、Values 引用<br>• ⚠️ 不支持从文件或远程 URL 加载 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n**影响的响应头：**\n- `Cache-Control`\n- `Expires`\n- `Pragma`\n\n## 缓存策略说明\n\n| 策略 | 含义 | 典型使用场景 |\n| :--- | :--- | :--- |\n| **数字（如 `60`）** | 设置资源缓存指定秒数 | 静态资源、不经常变化的API响应 |\n| **no-cache**（或 **no**） | 允许缓存但每次使用前必须验证 | 经常变化的内容，如用户数据、实时报价 |\n| **no-store** | 完全禁止缓存任何版本 | 敏感数据、支付页面、一次性令牌 |\n\n## 配置示例\n\n### 基础示例\n```txt\n# 静态资源缓存1小时\nhttps://example.com/static cache://3600\n\n# API接口禁用缓存\nhttps://example.com/api cache://no-cache\n\n# 敏感数据禁止缓存\nhttps://example.com/account cache://no-store\n```\n\n### 使用内嵌值\n````txt\nhttps://example.com/reports cache://{report-cache-policy}\n\n``` report-cache-policy\nno-cache\n```\n````\n\n### 配合过滤器使用\n仅对特定响应状态码设置缓存：\n```txt\nhttps://example.com/api  cache://300 includeFilter://s:200\n```\n\n### 引用 Values 中的配置\n假设在 Values 中已有 `static-cache` 配置：\n```txt\nhttps://example.com/assets cache://{static-cache}\n```\n\n\n## 工作原理与关联协议\n\n1. **核心原理**：`cache` 协议自动设置相应的缓存控制头：\n\n   - `cache://5` 设置：\n     ```http\n     Cache-Control: max-age=5\n     Expires: [当前时间+5秒]\n     ```\n   \n   - `cache://no-cache` 设置：\n     ```http\n     Cache-Control: no-cache\n     Pragma: no-cache\n     ```\n   \n   - `cache://no-store` 设置：\n     ```http\n     Cache-Control: no-store\n     Pragma: no-cache\n     ```\n\n2. **关联协议**：\n   - **禁用缓存**：更彻底的缓存禁用方案，同时移除请求和响应中的缓存相关头。详见：[disable://cache](./disable)\n   - **手动设置头部**：对于更复杂的缓存控制，可以使用 [`resHeaders`](./resHeaders) 协议手动设置缓存头。\n\n## 注意事项\n- 缓存时间以秒为单位，不支持小数\n- 设置 `no-cache` 不代表\"不缓存\"，而是\"使用前必须验证\"\n- 对于需要完全禁用缓存的场景，建议同时使用 [`disable://cache`](./disable) 协议\n- 实际缓存行为还受服务器配置和浏览器实现的影响\n"
  },
  {
    "path": "docs/docs/rules/cipher.md",
    "content": "# tlsOptions\n\n`tlsOptions` 协议用于配置 HTTPS/TLS 连接的安全参数，包括加密算法套件、客户端证书等，用于建立加密通信通道和服务器身份验证。\n\n> **版本要求**：仅 Whistle 最新版本（≥ v2.9.101）支持此功能\n\n通过 `tlsOptions` 协议，你可以：\n- 自定义 TLS 加密算法套件\n- 配置双向认证（mTLS）所需的客户端证书\n- 设置 TLS 连接的其他安全参数\n- 控制 TLS 版本和连接选项\n\n**规则合并机制**：`tlsOptions` 支持设置任意多个配置规则，Whistle 会根据从上到下的顺序自动合并这些规则，允许你灵活组合不同的 TLS 配置。\n\n## 规则语法\n\n`tlsOptions` 支持多种方式配置 HTTPS/TLS 连接的安全参数：\n\n### 1. 内联值（直接指定）\n直接在规则中写明操作。\n```txt\n# 设置客户端证书\npattern tlsOptions://key=/path/to/client.key&cert=/path/to/client.crt\n\n# 自定义加密套件\npattern tlsOptions://ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384 [lineProps...] [filters...]\n\n# 或使用 ciphers 参数\npattern tlsOptions://ciphers=ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384 [lineProps...] [filters...]\n```\n\n### 2. 内嵌值（使用代码块）\n当配置较复杂或需要复用时可使用此方式。\n````txt\npattern tlsOptions://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n{\n  passphrase: \"123456\",\n  pfx: \"-----BEGIN PKCS7-----\\n...\"\n}\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values`（中央配置区）中预先定义好的配置。\n```txt\npattern tlsOptions://{key-of-values} [lineProps...] [filters...]\n```\n**前提：** 在 `Values` 中存在一个名为 `key-of-values` 的键，其值为 TLS 配置对象。\n\n### 4. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern tlsOptions://temp.json\n```\n\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `tlsOptions://temp.json`\n3. 在弹出的编辑对话框中输入响应内容\n4. 点击 `Save` 保存\n\n保存后规则会自动变为类似以下格式：\n```txt\nhttps://example.com/report tlsOptions://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.json\n```\n\n需要再次编辑时，用同样的方式点击该临时文件链接即可。\n\n### 5. 从文件或远程 URL 加载\n从本地文件或远程 URL 加载包含 TLS 配置的 JSON 或 YAML 文件。\n```txt\n# 从本地文件加载\npattern tlsOptions:///User/xxx/tlsOptions.json\n\n# 从远程 URL 加载（支持 http 和 https）\npattern tlsOptions://https://config.example.com/tlsOptions.json\n```\n\n**文件格式要求：**\n文件内容应为 JSON 或 YAML 格式：\n```json\n{\n  \"ciphers\": \"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\",\n  \"minVersion\": \"TLSv1.2\",\n  \"maxVersion\": \"TLSv1.3\",\n  \"secureProtocol\": \"TLSv1_2_method\"\n}\n```\n或\n```yaml\nciphers: ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\nminVersion: TLSv1.2\nmaxVersion: TLSv1.3\nsecureProtocol: TLSv1_2_method\n```\n\n---\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | TLS 配置数据，支持多种格式：<br>• 加密算法套件名称（用 `:` 分隔）<br>• `tls.connect(options)` 参数对象<br>• 支持从本地文件、远程 URL、内联、内嵌、Values 引用 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n---\n\n## 配置对象参考\n\n完整的 TLS 连接配置对象，支持 Node.js [`tls.connect()`](https://nodejs.org/docs/latest/api/tls.html#tlscreatesecurecontextoptions) 的所有参数：\n\n```js\n{\n  // 加密算法套件\n  ciphers: \"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\",\n  \n  // TLS 协议版本控制\n  minVersion: \"TLSv1.2\",\n  maxVersion: \"TLSv1.3\",\n  secureProtocol: \"TLSv1_2_method\",\n  \n  // 证书相关配置\n  ca: \"-----BEGIN CERTIFICATE-----\\n...\",  // CA 证书\n  cert: \"-----BEGIN CERTIFICATE-----\\n...\", // 客户端证书\n  key: \"-----BEGIN PRIVATE KEY-----\\n...\",  // 私钥\n  pfx: \"-----BEGIN PKCS7-----\\n...\",       // PFX/P12 格式证书\n  passphrase: \"cert-password\",             // 证书密码\n  \n  // 其他安全选项\n  honorCipherOrder: true,     // 优先使用服务器端的加密套件顺序\n  requestCert: true,          // 请求客户端证书（双向认证）\n  rejectUnauthorized: true,   // 拒绝未授权的证书\n  \n  // 高级选项\n  ecdhCurve: \"auto\",         // ECDH 曲线\n  dhparam: \"-----BEGIN DH PARAMETERS-----\\n...\",\n  secureOptions: 0,          // SSL 选项标志\n  sessionTimeout: 300,       // 会话超时时间（秒）\n  sessionIdContext: \"whistle\"\n}\n```\n\n---\n\n## 配置示例\n\n### 1. 自定义加密套件\n限制特定域名的 TLS 加密算法：\n```txt\nwww.example.com/path tlsOptions://ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384\n```\n\n> **使用场景**：解决特定服务器的兼容性问题。参考：[GitHub Issue #963](https://github.com/avwo/whistle/issues/963)\n\n### 2. 配置客户端证书（双向认证 mTLS）\n\n**cert 格式证书：**\n```txt\nwww.exaple.com/path tlsOptions://key=/User/xxx/test.key&cert=/User/xxx/test.crt\n```\n\n**pem 格式证书：**\n```txt\nwww.exaple.com/path tlsOptions://key=E:\\test.key&cert=E:\\test.pem\n```\n\n**pfx 格式证书：**\n```txt\nwww.exaple.com/path tlsOptions://passphrase=123456&pfx=/User/xxx/test.pfx\n```\n\n**p12 格式证书：**\n```txt\nwww.exaple.com/path tlsOptions://passphrase=123456&pfx=E:/test.p12\n```\n\n> **Windows 路径**：支持混用 `/` 和 `\\` 作为路径分隔符\n\n### 3. 内嵌证书内容\n直接将证书内容嵌入配置中：\n````txt\n``` test.json\n{\n  key: '----xxx----- ... ----xxx-----',\n  cert: '----yyy----- ... ----yyy-----'\n}\n```\n\nwww.exaple.com/path tlsOptions://{test.json}\n````\n\n### 4. 从本地或远程文件加载配置\n**从本地文件加载：**\n```txt\nwww.example.com/path1 tlsOptions:///User/xxx/test.json\n```\n\n**从远程 URL 加载：**\n```txt\nwww.example.com/path2 tlsOptions://https://www.xxx.com/xxx/params.json\n```\n\n**使用临时文件编辑：**\n```txt\nwww.example.com/path3 tlsOptions://temp/blank.json\n```\n\n### 5. 规则合并示例\n利用规则合并特性，逐步细化配置：\n```txt\n# 第一层：为整个域名设置基础TLS版本\n*.example.com tlsOptions://minVersion=TLSv1.2\n\n# 第二层：为API子域添加客户端证书\napi.example.com tlsOptions://key=/certs/client.key&cert=/certs/client.crt\n\n# 第三层：为特定路径进一步限制加密套件\napi.example.com/secure tlsOptions://ciphers=ECDHE-RSA-AES256-GCM-SHA384\n```\n\n---\n\n## 注意事项\n\n1. **版本兼容性**：确保使用的 Whistle 版本 ≥ v2.9.101\n2. **证书格式**：不同格式的证书需要使用对应的参数：\n   - PEM 格式：使用 `key` 和 `cert` 参数\n   - PFX/P12 格式：使用 `pfx` 和 `passphrase` 参数\n3. **路径处理**：\n   - 相对路径相对于 Whistle 配置文件目录\n   - Windows 路径可以混用 `/` 和 `\\`\n4. **安全警告**：\n   - 谨慎使用 `rejectUnauthorized: false`，仅限开发环境\n   - 妥善保管客户端私钥，避免泄露\n5. **性能影响**：复杂的加密算法可能影响连接性能\n6. **调试建议**：遇到连接问题时，可先用简单的配置测试，逐步添加复杂选项\n\n---\n\n## 故障排除\n\n### Q: 证书加载失败\n**A:** 检查：\n1. 证书文件路径是否正确\n2. 证书格式是否匹配参数类型\n3. PFX 证书密码是否正确\n\n### Q: 连接被拒绝\n**A:** 检查：\n1. TLS 版本是否被服务器支持\n2. 加密算法套件是否被服务器支持\n3. 客户端证书是否有效且被服务器信任\n\n### Q: 配置未生效\n**A:** 检查：\n1. 规则 pattern 是否正确匹配目标 URL\n2. Whistle 版本是否满足要求\n3. 配置语法是否正确，特别是 JSON 格式\n4. 是否存在冲突的规则（规则按从上到下顺序合并）\n\n### Q: 规则合并不符合预期\n**A:** 检查：\n1. 规则的顺序是否正确（从上到下合并）\n2. 不同规则中的配置是否冲突\n3. 使用更具体的 pattern 是否覆盖了通用配置\n"
  },
  {
    "path": "docs/docs/rules/cssAppend.md",
    "content": "# cssAppend\n\n`cssAppend` 协议用于在现有的 CSS 类型响应内容体后面插入指定内容。\n\n> **生效条件**：仅对响应类型 `content-type` 包含 `html` 或 `css`，且包含响应内容体的状态码（如 `200`/`500` 等）才生效\n> \n> ⚠️ 注意：`204`、`304` 等无响应内容体的请求不受影响\n> \n> html 页面会自动包裹 `<style>...</style>`\n\n通过 `cssAppend` 协议，你可以：\n- 在现有 CSS 文件的末尾追加新的样式规则\n- 动态添加 CSS 覆盖规则\n- 为特定页面注入自定义样式\n- 在不修改源文件的情况下扩展样式功能\n\n## 规则语法\n\n`cssAppend` 支持多种方式指定要追加的内容：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要追加的 CSS 内容。\n```txt\n# `value` 小括号内不能用空格或换行符\npattern cssAppend://(value) [lineProps...] [filters...]\n```\n\n**示例：**\n```txt\nwww.example.com/index.html cssAppend://(.custom-btn{background:red;})\n```\n\n### 2. 内嵌值（使用代码块）\n当要追加的 CSS 内容有空格或换行符或需要复用时可使用此方式。\n````txt\npattern cssAppend://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n.dark-mode {\n  background: #333;\n  color: #fff;\n}\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values`（中央配置区）中预先定义好的 CSS 内容。\n```txt\npattern cssAppend://{key-of-values} [lineProps...] [filters...]\n```\n**前提：** 在 `Values` 中存在一个名为 `key-of-values` 的键，其值为 CSS 内容。\n\n### 4. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern cssAppend://temp.css\n```\n\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `cssAppend://temp.css`\n3. 在弹出的编辑对话框中输入响应内容\n4. 点击 `Save` 保存\n\n保存后规则会自动变为类似以下格式：\n```txt\nhttps://example.com/test.css cssAppend://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.css\n```\n\n需要再次编辑时，用同样的方式点击该临时文件链接即可。\n\n### 5. 从文件或远程 URL 加载\n从本地文件或远程 URL 加载要追加的 CSS 内容。\n```txt\n# 从本地文件加载\npattern cssAppend:///User/xxx/custom.css\n\n# 从远程 URL 加载（支持 http 和 https）\npattern cssAppend://https://cdn.example.com/styles/override.css\n```\n# 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 要追加的 CSS 内容，支持多种格式：<br>• 纯 CSS 代码文本<br>• 支持从本地文件、远程 URL、内联、内嵌、Values 引用 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n**特殊说明：**\n- 仅对 `content-type` 响应头包含 `css` 的请求生效\n- 对 `204`、`304` 等无响应体的状态码不生效\n- 追加的内容会直接拼接到原 CSS 文件的末尾\n\n\n## 配置示例\n\n#### 场景 1：添加响应式断点\n```` txt\nwww.example.com/responsive.css cssAppend://{test.css}\n\n``` test.css\n@media (max-width: 768px) {\n  .sidebar { display: none; }\n  .main-content { width: 100%; }\n}\n```\n\n````\n\n#### 场景 2：覆盖第三方库样式\n```` txt\ncdn.example.com/bootstrap.min.css cssAppend://{test2.css}\n\n``` test2.css\n/* 覆盖 Bootstrap 默认样式 */\n.btn-primary { background-color: #007bff !important; }\n.container { max-width: 1400px; }\n```\n````\n\n#### 场景 3：添加打印样式\n````txt\nwww.example.com/print.css cssAppend://{test3.css}\n\n``` test3.css\n@media print {\n  .no-print { display: none; }\n  a::after { content: \" (\" attr(href) \")\"; }\n}`\n```\n````\n\n## 关联协议\n\n1. **在响应内容前面注入内容**：[reqAppend](./reqAppend)\n   - 在所有类型响应内容的前面插入内容\n\n2. **在 CSS 响应内容前面注入内容**：[cssPrepend](./cssPrepend)\n   - 在 CSS 响应内容的前面插入内容（与 `cssAppend` 相反）\n\n3. **替换 CSS 响应内容**：[cssBody](./cssBody)\n   - 完全替换 CSS 响应内容（而非追加）\n\n4. **其他内容类型操作**：\n   - [jsAppend](./jsAppend)：在 JavaScript 响应内容后追加\n   - [htmlAppend](./htmlAppend)：在 HTML 响应内容后追加\n"
  },
  {
    "path": "docs/docs/rules/cssBody.md",
    "content": "# cssBody\n将现有响应内容体t替换成指定内容（（仅对响应类型 `content-type` 包含 `css`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern cssBody://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path1 cssBody://(Hello) file://(-test-)\nwww.example.com/path2 cssBody://(Hello) file://(-test-) resType://js\nwww.example.com/path3 cssBody://(Hello) file://(-test-) resType://css\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `<style>Hello</style>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `Hello`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 cssBody://{body.txt} file://(-test-)\nwww.example.com/path2 cssBody://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 cssBody://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `<style>Hello world.</style>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `Hello world.`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 cssBody:///User/xxx/test.css\nwww.example.com/path2 cssBody://https://www.xxx.com/xxx/params.css\n# 通过编辑临时文件\nwww.example.com/path3 cssBody://temp/blank.css\n``\n\n## 关联协议\n1. 替换响应内容：[resBody](./resBody)\n2. 在 CSS 类型的响应内容前面注入内容：[cssPrepend](./cssPrepend)\n3. 在 CSS 类型的响应内容后面注入内容：[cssAppend](./cssAppend)\n"
  },
  {
    "path": "docs/docs/rules/cssPrepend.md",
    "content": "# cssPrepend\n在现有响应内容体前面插入指定内容（仅对响应类型 `content-type` 包含 `css`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern cssPrepend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path1 cssPrepend://(Hello) file://(-test-)\nwww.example.com/path2 cssPrepend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 cssPrepend://(Hello) file://(-test-) resType://css\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    <style>Hello</style>-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `Hello-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 cssPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 cssPrepend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 cssPrepend://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path1/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    <style>Hello world.</style>-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `Hello world.-test-`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 cssPrepend:///User/xxx/test.css\nwww.example.com/path2 cssPrepend://https://www.xxx.com/xxx/params.css\n# 通过编辑临时文件\nwww.example.com/path3 cssPrepend://temp/blank.css\n````\n\n## 关联协议\n1. 在响应内容前面注入内容：[reqPrepend](./reqPrepend)\n2. 替换 CSS 类型的响应内容：[cssBody](./jsBody)\n3. 在 CSS 类型的响应内容后面注入内容：[cssPrepend](./jsAppend)\n"
  },
  {
    "path": "docs/docs/rules/delete.md",
    "content": "# delete\n用于删除请求 URL、请求/响应头、请求/响应内容。\n\n## 规则语法\n``` txt\npattern delete://prop1|prop2|... [filters...]\n\n# 等效于：\npattern delete://prop1 delete://prop2 ... [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | `pathname`：删除请求路径（不包含请求参数）<br/>`pathname.index`：`index` 为路径分段序号 `...、-1、0、1、...` 或关键字 `last` 详见下面说明<br/>`urlParams`：删除所有请求参数<br/>`urlParams.xxx`：删除 URL 的 `xxx 参数`<br/>`reqHeaders.xxx`: 删除请求头的 `xxx` 字段<br/>`resHeaders.xxx`: 删除响应头的 `xxx` 字段<br/>`reqBody`: 删除所有请求内容<br/>`resBody`: 删除所有响应内容<br/>`reqBody.xxx.yyy`: 删除请求内容中类型为表单(form)或JSON的 `xxx.yyy` 字段<br/>`resBody.xxx.yyy`:  删除响应类型为 JSONP 或 JSON 的响应内容里的 `xxx.yyy` 字段<br/>`reqType`: 删除请求头 `content-type` 里的类型，不包含可能存在的 charset<br/>`resType`: 删除响应头 `content-type` 里的类型，不包含可能存在的 charset<br/>`reqCharset`: 删除请求头 `content-type` 里可能存在的 charset<br/>`resCharset`: 删除响应头 `content-type` 里可能存在的 charset<br/>`reqCookies.xxx`: 删除请求头的里面名为 `xxx` 的 cookie<br/>`resCookies.xxx`: 删除响应头的里面名为 `xxx` 的 cookie|    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nhttps://www.example.com/path delete://reqCookies.token|resCookies.token\n\nhttps://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json delete://resBody.name resType://json\n```\n上述 cookie 删除操作仅作用于请求/响应过程中的 cookie，不会修改浏览器本地存储的 cookie。如需修改浏览器持久化 cookie，可通过以下方式实现：\n- 通过 [jsPrepend](./jsPrepend) 等注入 JS 删除\n- 通过 [resCookies](./resCookies) 设置 cookie 过期\n\n## 删除路径\n> 支持版本：v2.9.102 及以上\n\n### 规则语法\n```\n# 基本格式\n目标域名 delete://pathname.索引号\n\n# 示例\nwww.example.com/api/path/to delete://pathname.0 delete://pathname.1 delete://pathname.-1\n```\n\n### 规则说明\n该规则用于删除请求 URL 中路径的特定分段。Whistle 会提取不包含查询参数的请求路径，并通过 `/` 分割成数组后进行删除操作。\n\n**示例解析：**\n- 请求URL：`https://www.example.com/api/path/to/xxx?query`\n- 提取路径：`api/path/to/xxx`\n- 分割数组：`['api', 'path', 'to', 'xxx']`\n- 应用规则：\n  - `delete://pathname.0` → 删除 'api'\n  - `delete://pathname.1` → 删除 'path' \n  - `delete://pathname.-1` → 删除 'xxx'\n- 最终路径：`/to`\n- 完整结果：`https://www.example.com/to?query`\n\n### 索引规则\n- **正数索引**：从 0 开始，表示从前往后的顺序\n- **负数索引**：从 -1 开始，表示从后往前的位置（-1 = 最后一段，-2 = 倒数第二段，以此类推）\n- **特殊值**：使用 `last` 可删除最后一段路径但保留末尾斜杠\n- **边界情况**：超出数组范围的索引将被忽略\n\n### 重要说明\n当路径以 `/` 结尾时，分割后的数组最后会包含一个空字符串项：\n- 路径：`/api/path/to/xxx/`\n- 分割结果：`['api', 'path', 'to', 'xxx', '']`\n\n### 使用建议\n- 使用 `pathname.-1` 删除最后一段且不保留末尾斜杠\n- 使用 `pathname.last` 删除最后一段但保留末尾斜杠\n\n**对比示例：**\n```\nwww.example.com/api/path/to delete://pathname.0 delete://pathname.1 delete://pathname.last\n```\n- 请求：`https://www.example.com/api/path/to/xxx?query`\n- 结果：`https://www.example.com/to/?query` (保留末尾斜杠)\n"
  },
  {
    "path": "docs/docs/rules/disable.md",
    "content": "# disable\n通过规则禁用 HTTPS、隐藏请求、终止请求等功能。\n\n## 规则语法\n``` txt\npattern disable://action1|action2|... [filters...]\n\n# 等效于：\npattern disable://action1 disable://action2 ... [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| action  | 具体动作，详见下面的说明 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n- `301`：禁止 `301` 跳转，`301` 强制转 `302`（也可以用 [replaceStatus](./replaceStatus)）\n- `abort`：禁用 abort 功能，详见：[enable](./enable)\n- `abortReq`：禁用 abortReq 功能，详见：[enable](./enable)\n- `abortRes`：禁用 abortRes 功能，详见：[enable](./enable)\n- `authCapture`：禁用 `authCapture` 功能，详见：[enable](./enable)\n- `auto2http`：禁用 auto2http 功能，详见：[enable](./enable)\n- `autoCors`：使用 [file](./file) 协议替换请求时，如果 Whistle 检测到该请求属于跨域请求，会自动添加必要的 CORS (跨域资源共享) 头信息，可以通过 `disable://autoCors` 禁用\n- `ajax`: 删除请求头 `x-requested-with`\n- `bigData`：禁用 bigData 功能，详见：[enable](./enable)\n- `capture` 或 `https`：禁用 `Enable HTTPS`，详见：[enable](./enable)\n- `captureIp`：禁用 captureIp 功能，详见：[enable](./enable)\n- `captureStream`：禁用 captureStream 功能，详见：[enable](./enable)\n- `clientCert`：禁用 clientCert 功能，详见：[enable](./enable)\n- `clientId`：删除请求头 `x-whistle-client-id`\n- `clientIp`：删除请求头 `x-forwarded-for`\n- `customParser`：禁用 customParser 功能，详见：[enable](./enable)\n- `cache`: 禁用缓存\n- `dnsCache`: 禁止 DNS 缓存\n- `csp`: 禁用 CSP\n- `cookies`: 禁用请求和响应的 cookie\n- `reqCookies`: 禁用请求的 cookie\n- `resCookies`: 禁用响应的 cookie\n- `flushHeaders`：禁用 flushHeaders 功能，详见：[enable](./enable)\n- `forHttp`：禁用 forHttp 功能，详见：[enable](./enable)\n- `forHttps`：禁用 forHttps 功能，详见：[enable](./enable)\n- `forceReqWrite`：禁用 forceReqWrite 功能，详见：[enable](./enable)\n- `forceResWrite`： 禁用 forceResWrite 功能，详见：[enable](./enable)\n- `gzip`：禁止压缩响应内容\n- `h2`：禁用 h2 功能，详见：[enable](./enable)\n  > 禁用：Whistle 代理 -xx-> 服务器启用 HTTP2\n- `http2`：禁用 http2 功能，详见：[enable](./enable)\n  > 禁用：浏览器 -xx-> Whistle 代理 -xx-> 服务器全部启用 HTTP2\n- `httpH2`：禁用 httpH2 功能，详见：[enable](./enable)\n  > 禁用：Whistle 代理 -xx-> 服务器的 HTTP 请求启用 HTTP2\n- `hide`：禁用 hide 功能，详见：[enable](./enable)\n- `hideComposer`：禁用 hideComposer 功能，详见：[enable](./enable)\n- `hideCaptureError`：禁用 hideCaptureError 功能，详见：[enable](./enable)\n- `interceptConsole`：禁用 interceptConsole 功能，详见：[enable](./enable)\n- `internalProxy`：禁用 internalProxy 功能，详见：[enable](./enable)\n- `proxyFirst`：禁用优先使用 [proxy](./proxy) 规则\n- `proxyHost`：禁用 proxyHost 功能，详见：[enable](./enable)\n- `proxyTunnel`：禁用 proxyTunnel 功能，详见：[enable](./enable)\n- `keepCSP`：禁用 keepCSP 功能，详见：[enable](./enable)\n- `keepAllCSP`：禁用 keepAllCSP 功能，详见：[enable](./enable)\n- `keepCache`：禁用 keepCache 功能，详见：[enable](./enable)\n- `keepAllCache`：禁用 keepAllCache 功能，详见：[enable](./enable)\n- `keepAlive`：禁止缓存请求连接\n- `keepClientId`：禁用 keepClientId 功能，详见：[enable](./enable)\n- `keepH2Session`：无需每个 TUNNEL 连接对应一个 HTTP2 连接（默认 `是`，建议使用默认即可）\n- `safeHtml`：禁用 safeHtml 功能，详见：[enable](./enable)\n- `strictHtml`：禁用 strictHtml 功能，详见：[enable](./enable)\n- `proxyConnection`：通过 proxy、socks 等代理协议转发请求时设置 `proxy-connection: close`\n- `ua`: 删除请求头 `user-agent`\n- `proxyUA`：通过 proxy、socks 等代理协议转发请求时删除请求头 `user-agent`\n- `referer`: 禁用 `referer`\n- `rejectUnauthorized`：`rejectUnauthorized` 设置为 `true`\n- `requestWithMatchedRules`：禁用 requestWithMatchedRules 功能，详见：[enable](./enable)\n- `responseWithMatchedRules`：禁用 responseWithMatchedRules 功能，详见：[enable](./enable)\n- `secureOptions`：禁用建立 HTTP2 连接的默认 `options`\n- `timeout`：禁用请求超时设置\n- `trailerHeader`：删除响应头 `trailer`\n- `trailers`：删除 trailers\n- `tunnelAuthHeader`：删除代理鉴权头 `proxy-authorization`\n- `tunnelHeadersFirst`：禁用 tunnelHeadersFirst 功能，详见：[enable](./enable)\n- `useLocalHost`：禁用 useLocalHost 功能，详见：[enable](./enable)\n- `useSafePort`：禁用 useSafePort 功能，详见：[enable](./enable)\n- `userLogin`：禁用设置 [statusCode://401](./statusCode) 时显示登录框\n- `weakRule`：禁用 weakRule 功能，详见：[enable](./enable)\n\n## 配置示例\n``` txt\n# 禁用请求的缓存，同时删除请求和响应头的缓存字段\n# 跟 cache 协议的区别是，cache 只是用来设置响应的缓存头\nwwww.example.com/path disable://cache\n\n# 禁用请求和响应的 cookies\nwwww.example.com/path disable://cookies\n\n# 只禁用请求的 cookies\nwwww.example.com/path disable://reqCookies\n\n# 只禁用响应的 cookies\nwwww.example.com/path disable://resCookies\n\n# 删除 user-agent\nwwww.example.com/path disable://ua\n\n# 删除 referer\nwwww.test.com/path disable://referer\n\n# 删除csp策略\nwwww.test.com/path disable://csp\n\n# 禁用 timeout，默认情况下 Whistle 对每个请求如果 36s 内没有发生数据传输，会认为请求超时\nwwww.test.com/path disable://timeout\n\n# 把 301 转成 302，防止 cache\nwwww.test.com/path disable://301\n\n# 禁用 HTTPS 拦截\nwwww.test.com/path disable://capture\n\n# 不缓存 DNS 结果\nwwww.test.com/path disable://dnsCache\n\n# 禁用代理服务器请求链接复用\nwwww.test.com/path disable://keepAlive\n\n# 删除请求头 `x-requested-with`\nwwww.test.com/path disable://ajax\n\n# 也可以同时禁用多个\nwww.example.com/path disable://cache|cookies|ua|referer|csp|timeout|301|intercept|dnsCache|keepAlive|autoCors\n```\n\n关联操作：[enable](./enable)\n"
  },
  {
    "path": "docs/docs/rules/enable.md",
    "content": "# enable\n通过规则启用 HTTPS、隐藏请求、终止请求等功能。\n\n## 规则语法\n``` txt\npattern enable://action1|action2|... [filters...]\n\n# 等效于：\npattern enable://action1 enable://action2 ... [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| action  | 具体动作，详见下面的说明 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n- `abort`：请求或响应阶段中断请求（根据匹配阶段）\n- `abortReq`：请求阶段中断请求\n- `abortRes`：响应阶段中断请求\n- `authCapture`：强制在转成 HTTPS 之前执行 [auth hook](../extensions/dev#auth)（默认是转成 HTTPS 请求后再执行插件的 auth hook）\n- `auto2http`：开启 HTTPS 请求报 TLS 错误自动转成 HTTP 请求，默认情况下如果 serverIP 是本地 IP 会自动启用\n- `bigData`：扩大抓包数据显示限制(2M→16M)\n- `br`：启用 BR 压缩响应内容\n- `gzip`：启用 GZIP 压缩响应内容\n- `deflate`：启用 Deflate 压缩响应内容\n- `capture` 或 `https`：Enable HTTPS（同 [HTTPS菜单功能](../gui/https.html)）\n- `captureIp` ：域名为 IP 的请求，默认不解密 HTTPS 请求，可以通过 `enable://captureIp` 启用解析 HTTPS 请求\n- `captureStream`：将抓取到的请求与响应内容以数据流的形式，实时输出到抓包界面并动态追加显示\n- `clientCert`：启用客户端与服务器之间的双向认证 (mTLS)\n- `clientId`：请求头带上 `x-whistle-client-id: Whistle本地生成的唯一ID`\n- `clientIp`：为匹配的非本地请求设置 `x-forwarded-for` 请求头，将客户端的真实IP地址透传给上游服务\n- `customParser`：自定义抓包界面显示内容，用法参考插件：https://github.com/whistle-plugins/whistle.custom-parser\n- `flushHeaders`：`response.writeHead(...)` 后调用 [response.flushHeaders](https://nodejs.org/docs/latest/api/http.html#responseflushheaders)（默认执行）\n- `forHttp`：让 `capture` 功能只对 HTTP 请求生效\n- `forHttps`：让 `capture` 功能只对 HTTPS 请求生效\n- `forceReqWrite`：使用 [reqWrite](./reqWrite)、[reqWriteRaw](./reqWriteRaw) 将请求数据写入本地文件时，如果对应的文件已存在，默认跳过写入操作以保护现有文件，可以通过 `enable://forceReqWrite` 强制覆盖\n- `forceResWrite`： 使用 [resWrite](./resWrite)、[reqWriteRaw](./reqWriteRaw) 将响应数据写入本地文件时，如果对应的文件已存在，默认跳过写入操作以保护现有文件，可以通过 `enable://forceResWrite` 强制覆盖\n- `h2`：Whistle 代理 -> 服务器启用 HTTP2\n- `http2`：浏览器 -> Whistle 代理 -> 服务器全部启用 HTTP2\n- `httpH2`：Whistle 代理 -> 服务器的 HTTP 请求启用 HTTP2\n- `hide`：在界面上隐藏抓包数据（不包括 `captureError` 和 Composer 发出的请求）\n- `hideComposer`：隐藏 Composer 发出的请求\n- `hideCaptureError`：隐藏 `captureError` 请求\n- `showHost`：将服务器 IP 设置到响应头 `x-host-ip`\n- `ignoreSend`：WebSocket 和 TUNNEL 请求时忽略发送数据帧（TUNNEL 请求要启用 `inspect`）\n- `ignoreReceive`：WebSocket 和 TUNNEL 请求时忽略接收数据帧（TUNNEL 请求要启用 `inspect`）\n- `pauseSend`：WebSocket 和 TUNNEL 请求时暂停发送数据帧（TUNNEL 请求要启用 `inspect`）\n- `pauseReceive`：WebSocket 和 TUNNEL 请求时暂停接收数据帧（TUNNEL 请求要启用 `inspect`）\n- `inspect`：使的在 Inspectors / Frames 看到 TUNNEL 请求的内容\n- `interceptConsole`：截获 `console.xxx` 的请求并显示在 Whistle 管理界面的 Log 面板（默认开启）\n- `internalProxy`：利用 `proxy`、`socks` 等代理协议将请求转发至其他代理服务器（如另一 Whistle 实例）。启用此功能后，已在第一层代理解密的 HTTPS 请求将以明文形式在代理链中传输，从而上游代理可以直接获取明文数据\n- `proxyFirst`：优先使用 [proxy](./proxy) 规则（默认情况下，同时匹配 `host` 和 `proxy`，只有 `host` 生效）\n- `proxyHost`：[proxy](./proxy) 和 [host](./host) 同时生效\n- `proxyTunnel`：跟 `proxyHost` 一同使用，让上游代理再次通过隧道代理到上上游的 HTTP 代理，详见下面的示例\n- `keepCSP`：通过 `htmlXxx`/`jsXxx`/`cssXxx` 注入内容时会自动删除响应头大 `csp` 字段，如果想保留这些字段可以用 `enable://keepCSP`\n- `keepAllCSP`：通过 `htmlXxx`/`jsXxx`/`cssXxx`/`weinre`/`log` 注入内容时会自动删除响应头的 `csp` 字段，如果想保留这些字段可以用 `enable://keepAllCSP`\n- `keepCache`：通过 `htmlXxx`/`jsXxx`/`cssXxx` 注入内容时会自动删除响应头的缓存字段，如果想保留原有的缓存头可以用 `enable://keepCache`\n- `keepAllCache`：通过 `htmlXxx`/`jsXxx`/`cssXxx`/`weinre`/`log` 注入内容时会自动删除响应头的缓存字段，如果想保留原有的缓存头可以用 `enable://keepAllCache`\n- `keepClientId`：保留请求原有的 `x-whistle-client-id` 请求头（默认会删除请求带过来的  `x-whistle-client-id`）\n- `safeHtml`：是一种安全防护机制，当使用 `htmlXxx`/`jsXxx`/`cssXxx` 向 HTML 页面注入内容时，会先检查响应内容的第一个非空白字符是否为 `{` 和 `[`（JSON 对象开头字符），如果不是才会执行注入操作。这可以有效防止对非标准 HTML 响应（如 JSON 接口）的误注入\n- `strictHtml`：是一种安全防护机制，当使用 `htmlXxx`/`jsXxx`/`cssXxx` 向 HTML 页面注入内容时，会先检查响应内容的第一个非空白字符是否为 `<`，如果不是才会执行注入操作。这可以有效防止对非标准 HTML 响应（如 JSON 接口）的误注入\n- `multiClient`：Whistle 作为公共代理且启用 `enable://clientId` 时会为所有请求添加一个固定的 `x-whistle-client-id` 请求头，这导致上游服务无法区分不同客户端。启用 `enable://multiClient` 后，将为每个客户端连接生成并维持一个唯一且不变的标识符，确保上游服务能准确识别请求来源\n- `requestWithMatchedRules`：在请求头带上当前匹配的规则\n- `responseWithMatchedRules`：在响应头带上当前匹配的规则\n- `tunnelHeadersFirst`：用于控制请求头合并的优先级。插件可通过 [tunnelKey](../extensions/dev) 将隧道（TUNNEL）请求头传递至后续阶段。默认的合并规则是：若隧道头与解析后的普通请求头存在同名键，则保留普通请求头的值。启用 `enable://tunnelHeadersFirst` 可改变这一行为，确保隧道请求头优先，从而强制覆盖任何冲突的普通头\n- `useLocalHost`：修改 `log` 和 `weinre` 请求 `URL` 的域名，使用内置域名\n- `useSafePort`：修改 `log` 和 `weinre` 请求 `URL` 的端口，使用内置端口\n- `userLogin`：设置 [statusCode://401](./statusCode) 是否显示登录框（默认显示）\n- `weakRule`：默认情况下，当配置了 [file](./file) 等协议时，[proxy](./proxy) 规则会自动失效。通过设置 `weakRule` 属性，可以提升 [proxy](./proxy) 规则的优先级，使其在上述场景中仍然生效\n- `socket`：在启用 HTTPS 解析（`Enable HTTPS` 或 `enable://https`）后，发往 `80/443` 端口的 TUNNEL 请求会被强制尝试解析为 HTTP/HTTPS 流量。默认情况下，若解析失败，该连接将被销毁；而其他端口的请求解析失败则会继续以 TUNNEL 方式传输。通过设置 `enable://socket`，可让发往 `80/443` 端口的请求在解析失败时同样降级为 TUNNEL 连接，避免连接被销毁\n- `websocket`：用于处理非标准 WebSocket 连接。某些请求虽使用 WebSocket 协议传输，但其 Upgrade 请求头并非标准值（如 `Upgrade: websocket`）。默认情况下，Whistle 会将其视为普通 TCP 连接而不解析数据。启用 `enable://websocket` 可强制 Whistle 识别此类连接为 WebSocket 协议并进行数据解析\n\n\n## 配置示例\n``` txt\n# Enable HTTPS\nwww.example.com enale://https\n\n# 延迟 3000毫秒终止请求\nwww.example.com/path reqDelay://3000 enable://abortReq\n\n# 延迟 5000毫米终止响应\nwww.example.com/path resDelay://5000 enable://abortRes\n\n# 本地替换的内容开启 GZIP\nwww.example.com/path file:///User/xxx/test enable://gzip\n\n# 给上游代理设置 hosts (10.10.10.20:8888)\nwww.example.com/path proxy://10.1.1.1:8080 10.10.10.20:8888 enable://proxyHost\n\n# 通过上游 HTTP 代理 (10.1.1.1:8080) 将请求通过隧道代理到指定的 HTTP 代理(10.10.10.20:8080)\nwww.example.com proxy://10.1.1.1:8080 10.10.10.20:8080 enable://proxyHost|proxyTunnel\n\n# 启用浏览器 -> Whistle 代理 -> 服务器整个链路的 HTTP2 功能\nwww.example.com enable://http2\n\n# 启用 Whistle 代理 -> 服务器的 HTTP2 功能\nwww.example.com enable://h2\n\n# 强制 Whistle 代理 -> 服务器的 HTTP 请求使用 HTTP2 传输\nwww.example.com enable://httpH2\n\n# 安全注入模式：当使用 htmlXxx/jsXxx/cssXxx 注入指令时，检测响应首字符不是 `{` 才注入\nwww.example.com/path enable://safeHtml\n\n# 严格HTML注入模式：当使用 htmlXxx/jsXxx/cssXxx 注入指令时，检测响应首字符不是 `<` 才注入\nwww.example.com/path enable://strictHtml\n\n# 自动添加 x-forwarded-for 请求头传递客户端真实 IP\nwww.example.com enable://clientIp\n\n# 扩大抓包数据显示限制(2M→16M)\nwww.example.com/path enable://bigData\n\n# 修改 log/weinre 请求 `URL` 的域名或端口\nwww.example.com/path enable://useLocalHost|useSafePort\n\n# 强制 reqWrite/reqWriteRaw/resWrite/resWriteRaw 覆盖已有的文件\nwww.example.com/path enable://forceReqWrite|forceResWrite\n\n# 强制 HTTPS 请求被解析前也走 `auth hook`（默认是转成 HTTPS 请求后再执行插件的 auth hook）\nwww.example.com enable://authCapture\n```\n\n关联操作：[disable](./disable)\n"
  },
  {
    "path": "docs/docs/rules/excludeFilter.md",
    "content": "# excludeFilter\n在匹配 [pattern](./pattern) 的基础上，进一步筛选符合指定条件的请求（满足任一条件即可保留）。\n\n## 语法规则\n``` txt\npattern operation excludeFilter://p1 excludeFilter://p2 ...\n```\n> 请求要匹配 `pattern` 且不能匹配 `p1`、`p2`、... 中的任一个条件才能生效\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| operation   | 操作指令 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 仅对 GET 或 POST 请求不生效\nwww.example.com/api/data proxy://127.0.0.1:8080 excludeFilter://m:GET excludeFilter://m:post\n```\n\n可以跟 [includeFilter](./includeFilter) 一起使用，详细用法参考：[过滤器文档](./filters)\n"
  },
  {
    "path": "docs/docs/rules/file.md",
    "content": "# file\n\n`file` 协议用于将请求映射到本地文件系统，用本地文件的内容作为响应返回给客户端，适用于以下场景：\n\n- **搭建本地开发环境**：快速搭建本地开发服务器\n- **调试本地前端页面**：直接调试本地 HTML、CSS、JavaScript 文件\n- **接口 Mock 场景**：使用本地 JSON 文件模拟 API 接口响应\n- **静态资源服务**：将本地目录作为静态资源服务器使用\n\n## 规则语法\n\n`file` 协议支持多种方式指定本地文件内容：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要返回的内容，适用于简短的文本内容。注意：`value` 不能包含空格和换行符。\n\n```txt\npattern file://(value) [lineProps...] [filters...]\n```\n\n**示例：**\n```txt\nwww.example.com/api/data file://({\"status\":\"ok\"})\n```\n\n**注意：内联值必须用括号 `()` 包裹，否则会被识别为本地文件路径，可能导致 404 错误：**\n``` txt\n# 正确写法：返回字符串 \"success\"\npattern file://(success)\n\n# 错误写法：会被当作路径处理，可能导致 404\npattern file://success\n```\n\n> **file 协议的内联值语法要点**：\n> - 必须使用 `()` 包裹内容\n> - 内容不能包含空格和换行符\n> - 可以是字符串、JSON、HTML片段等\n> - 如果不加 `()`，Whistle 会将其解释为文件路径或 `{key}` 并尝试加载对应文件或 key 值\n\n### 2. 内嵌值（使用代码块）\n当需要处理包含空格、换行符的复杂内容，或希望复用某段配置时，推荐使用此方式。\n\n````txt\npattern file://{custom-key.json} [lineProps...] [filters...]\n\n``` custom-key.json\n{\n  \"status\": \"success\",\n  \"data\": {\n    \"message\": \"Hello from local file\"\n  }\n}\n```\n````\n\n**最佳实践**：建议为 `key` 添加响应类型后缀（如 `.json`、`.html`、`.css`），Whistle 会自动根据后缀设置正确的 `Content-Type`。\n\n### 3. 引用 Values 中的值\n当内容较大时，可以将其存储在 `Values` 配置区中。\n\n```txt\npattern file://{key-of-values.html} [lineProps...] [filters...]\n```\n\n**前提**：在 `Values` 中存在名为 `key-of-values.html` 的键，其值为要返回的内容。\n\n**容量建议**：\n- 小于 2KB 的内容：建议使用内联或内嵌方式\n- 2KB 至 200KB 的内容：建议存储在 `Values` 中\n- 大于 200KB 的内容：建议使用本地文件\n\n### 4. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern file://temp.html\n```\n\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `file://temp.html`\n3. 在弹出的编辑对话框中输入响应内容\n4. 点击 `Save` 保存\n\n保存后规则会自动变为类似以下格式：\n```txt\nhttps://example.com/report file://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.html\n```\n\n需要再次编辑时，用同样的方式点击该临时文件链接即可。\n\n### 5. 从文件路径或远程 URL 加载\n从本地文件系统或远程 URL 加载响应内容。\n\n```txt\n# 从本地文件加载\npattern file:///User/xxx/test.html\n\n# 从远程 URL 加载\npattern file://https://example.com/template.html\n```\n\n\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 要返回的文件内容或路径，支持多种格式：<br>• 本地文件或目录路径<br>• 远程 URL<br>• 内联、内嵌、Values 引用内容 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>**示例**：<br>• `lineProps://important`：提升规则优先级<br>• `lineProps://weakRule`：当同时配置 [file](./file) 和 [proxy](./proxy) 规则时，默认 [proxy](./proxy) 会失效。通过此属性可提升 [proxy](./proxy) 规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n\n\n## 配置示例\n\n### 1. 基础文件路径映射\n```txt\n# 将域名映射到本地目录\nwww.example.com/path file:///Users/username/projects/my-site\n\n# Windows 系统路径\nwww.example.com/path file://D:\\projects\\my-site\n```\n\n**特性说明**：\n- **自动路径拼接**：访问 `https://www.example.com/path/x/y/z` 会映射到 `/Users/username/projects/my-site/x/y/z`\n- **禁用路径拼接**：使用 `< >` 包裹路径可以禁用自动拼接\n    ```txt\n    www.example.com/path file://</Users/username/projects/my-site/index.html>\n    ```\n    > 访问 `https://www.example.com/path/x/y/z` 只会返回 `/Users/username/projects/my-site/index.html` 文件\n\n### 2. JSONP 接口 Mock\n````txt\n# 内联方式\nwww.example.com/jsonp file://`(${query.callback}({\"status\":\"ok\"}))`\n\n# 内嵌方式\n``` jsonp-response\n${query.callback}({\n  \"status\": \"error\",\n  \"message\": \"Invalid parameter\"\n})\n```\nwww.example.com/jsonp file://`{jsonp-response}`\n````\n\n> **模板字符串限制**：模板字符串在以下场景无法直接生效：\n> - 引用本地文件路径时\n> - 引用远程 URL 地址时\n> \n> 遇到上述限制时，可以使用 [tpl](./tpl) 协议作为替代方案。\n\n### 3. 配合过滤器使用\n```txt\n# 排除特定接口\nwww.example.com/api/path file:///Users/username/mock-data excludeFilter://*/api/auth\n\n# 根据请求体内容匹配\nwww.example.com/api/search file:///Users/username/search-results.json includeFilter://b:/\"type\":\\s*\"advanced\"/i\n\n# 仅匹配 POST 请求\nwww.example.com/api/submit file:///Users/username/success.json includeFilter://m:POST\n```\n\n### 4. 多目录搜索\n```txt\n# 多个文件或目录用 `|` 分隔\nwww.example.com/static/path file:///path/to/project1|/path/to/project2|/path/to/project3\n```\n\n**查找逻辑**：\n1. 按顺序检查每个目录\n   - `/path/to/project1/static/file.js`\n   - `/path/to/project2/static/file.js`\n   - `/path/to/project3/static/file.js`\n2. 找到第一个存在的文件立即返回\n3. 全部未找到时返回 `404 Not Found`\n\n## 高级用法\n\n### 动态路径处理\n```txt\n# 使用正则表达式捕获组\n/^https?://www\\.example\\.com/user/(\\d+)/profile file:///Users/username/mock/profiles/user-$1.json\n\n# 如果不限制数字，可以用通配符\n^www.example.com/user/*/profile file:///Users/username/mock/profiles/user-$1.json\n```\n\n### 环境特定配置\n````txt\n``` dev-config\n{\n  \"apiBase\": \"http://localhost:3000\",\n  \"debugMode\": true\n}\n```\n\n``` prod-config\n{\n  \"apiBase\": \"https://api.example.com\",\n  \"debugMode\": false\n}\n```\n\n# 通过 cookie：env=dev 判断是否为开发环境\nwww.example.com/config file://{dev-config} includeFilter://reqH:cookie=/env=dev/\nwww.example.com/config file://{prod-config} # 默认正式环境\n````\n\n### 组合使用其他协议\n```txt\n# 先使用 file 提供静态文件，再设置响应头\nwww.example.com file:///Users/username/static-files cache://3600\n\n# 对于不存在的文件，使用 xfile 允许请求继续\nwww.example.com xfile:///Users/username/static-files\n```\n\n## 与 resBody 的区别\n\n`file` 协议与 [`resBody`](./resBody) 协议的主要区别在于请求处理流程和响应机制：\n\n**`file` 协议：***\n- **请求流程**：匹配 `file` 协议的请求**不会发送到后台服务器**\n- **响应机制**：直接返回指定文件内容，并响应 200 状态码\n- **网络开销**：零网络延迟，完全本地处理\n\n**示意图**：\n```\n客户端请求 → Whistle (匹配file规则) → 读取本地文件 → 返回响应 (200)\n                  ↓\n            (不发送到服务器)\n```\n\n**`resBody` 协议：**\n- **请求流程**：请求**会先发送到后台服务器**，获取原始响应\n- **响应机制**：收到服务器响应后，用指定内容**替换**原始响应体\n- **网络开销**：包含完整的请求-响应网络往返\n\n**示意图**：\n```\n客户端请求 → Whistle → 发送到服务器 → 收到原始响应 → 替换响应体 → 返回修改后的响应\n```\n\n## 关联协议\n\n1. **允许未匹配文件继续访问**：[xfile](./xfile)\n   - 当本地文件不存在时，允许请求继续访问原始服务器\n   - 适合静态资源服务器的开发场景\n\n2. **使用远程 URL 内容替换**：[https](./https) 或 [http](./http)\n   - 使用远程服务器的内容作为响应\n   - 注意：不建议使用 `file://https://xxx` 这种形式\n\n3. **域名解析重定向**：[host](./host)\n   - 修改域名解析，将请求导向指定 IP\n\n4. **模板渲染**：[tpl](./tpl)\n   - 支持更复杂的模板渲染和动态内容生成\n\n\n\n## 注意事项\n\n### 路径处理\n1. **相对路径**：除了插件内部的规则外，不建议使用相对路径。相对路径相对于 Whistle 配置文件所在目录。\n2. **绝对路径**：支持系统绝对路径\n3. **用户目录**：支持 `~` 表示用户主目录\n4. **Windows 路径**：支持混用 `/` 和 `\\` 作为路径分隔符\n\n### 文件编码\n1. **文本文件**：默认使用 UTF-8 编码\n2. **编码问题**：如遇乱码，请检查文件的实际编码格式\n\n\n## 故障排除\n\n### Q: 文件未找到（404）\n**A:** 检查：\n1. 文件路径是否正确\n2. 文件是否存在且有读取权限\n3. 是否使用了 `< >` 禁用了路径拼接\n\n### Q: 乱码问题\n**A:** 检查：\n1. 文件的实际编码格式\n2. 响应头的 `Content-Type` 是否正确\n3. 文件内容是否包含非法字符\n\n### Q: 规则未生效\n**A:** 检查：\n1. 规则 pattern 是否正确匹配\n2. 是否有其他规则覆盖\n3. 过滤器条件是否满足\n\n### Q: 模板字符串不生效\n**A:** 检查：\n1. 是否在文件路径或远程 URL 场景下使用模板字符串\n2. 如果是，请改用 [tpl](./tpl) 协议\n\n\n\n## 扩展阅读\n\n- [匹配模式文档](./pattern)：详细了解 URL 匹配规则\n- [操作指令文档](./operation)：了解内容加载的多种方式\n- [附加配置](./lineProps)：详细了解一些特殊功能\n- [过滤器](./filters)：了解更多过滤器功能\n"
  },
  {
    "path": "docs/docs/rules/filters.md",
    "content": "# 过滤器\n\n当需要基于请求属性或响应属性（而不仅仅是URL）进行匹配时，可以使用过滤器来实现更精细化的规则控制，语法结构：\n\n``` txt\npattern opertaion includeFilter://pattern1 ... excludeFilter://patternx ...\n```\n> 多个过滤器间为「或」匹配，只要匹配其中一个过滤条件就成立\n\n## 过滤器类型\n\n| 过滤器类型     | 语法格式                  | 用途                     |\n| -------------- | ------------------------- | ------------------------ |\n| **包含过滤器** | `includeFilter://pattern` | 只匹配符合指定条件的请求 |\n| **排除过滤器** | `excludeFilter://pattern` | 排除符合指定条件的请求   |\n\n## pattern 类型\n\n\n\n| 语法                | 用途                  | 示例                          |\n| ------------------- | --------------------- | ----------------------------- |\n| `b:pattern`         | 匹配请求体内容        | `includeFilter://b:keyword` `excludeFilter://b:/regexp/[i]` |\n| `m:pattern`         | 匹配 HTTP 方法        | `includeFilter://m:keyword` `excludeFilter://m:/regexp/[i]` |\n| `i:pattern`         | 匹配客户端或服务端 IP | `includeFilter://i:keyword` `excludeFilter://i:/regexp/[i]` |\n| `chance:probability`| `Math.random() < probability` | `includeFilter://chance:0.5` `excludeFilter://chance:0.3` |\n| `clientIp:pattern`  | 仅匹配客户端 IP       | `includeFilter://clientIp:/regexp/[i]`  `excludeFilter://clientIp:keyword` |\n| `serverIp:pattern`  | 仅匹配服务端 IP       | `includeFilter://serverIp:/regexp/[i]`  `excludeFilter://serverIp:keyword` |\n| `s:pattern`         | 匹配响应状态码        | `includeFilter://s:/^20/`  `excludeFilter://s:30` |\n| `h:name=pattern`    | 匹配请求/响应头       | `includeFilter://h:content-type=json`  `excludeFilter://h:content-type=/regexp/i` |\n| `reqH:name=pattern` | 仅匹配请求头          | `includeFilter://reqH:content-type=json`  `excludeFilter://reqH:content-type=/regexp/i` |\n| `resH:name=pattern` | 仅匹配响应头          | `includeFilter://resH:content-type=json`  `excludeFilter://resH:content-type=/regexp/i` |\n|  其它 `xxxxxx`     | 匹配请求 URL（同 [pattern](./pattern)） | `includeFilter://*/cgi-*` `excludeFilter://www.test.com` `includeFilter://https://www.test.com/path` |\n\n\n\n"
  },
  {
    "path": "docs/docs/rules/forwardedFor.md",
    "content": "# forwardedFor\n修改请求头 `x-forwarded-for` 字段，自定义客户端 IP。\n\n## 规则语法\n``` txt\npattern forwardedFor://ip [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| ip   | 自定义客户端 IP<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 设置固定 IP\nwww.example.com/path  forwardedFor://1.1.1.1\n\n# 使用真实客户端 IP（透传模式）\nwww.example.com  forwardedFor://`${clientIp}`\n```\n\n## 关联协议\n1. 直接修改请求头：[reqHeaders://x-forwarded-for=value](./reqHeaders)（这种方式的 `value` 可以不局限于 IP）\n2. 启用自动设置 `x-forwarded-for`：[enable://clientIp](./enable)\n"
  },
  {
    "path": "docs/docs/rules/frameScript.md",
    "content": "# frameScript\n通过 JavaScript 脚本操作 WebSocket 和普通 TCP 请求数据帧。\n\n## 规则语法\n``` txt\npattern frameScript://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 生成规则的 JS 脚本，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n``` test-frame.js\n// 发送预设数据\nctx.sendToServer('1 = 0x12300000');\nctx.sendToClient('1 = 0x1236666666');\n\n// 处理发往服务端的数据帧\nctx.handleSendToServerFrame = function(buf, opts) {\n  // 可以返回空、null、undefined 等\n  return (buf + '').replace(/1/g, '***');\n};\n\n// 处理发往客户端的数据帧  \nctx.handleSendToClientFrame = function(buf, opts) {\n  // 可以返回空、null、undefined 等\n  return (buf + '').replace(/1/g, '+++');\n};\n```\n\nwss://echo.websocket.org/ frameScript://{test-frame.js}\n````\n访问 `https://echo.websocket.org/.ws` 效果：\n\n <img width=\"1200\" alt=\"frame-script\" src=\"/img/frame-script.png\" />\n\n#### 可用全局变量\n\n| 变量/方法          | 描述                                                                 |\n|--------------------|---------------------------------------------------------------------|\n| `url`             | 完整请求URL                                                         |\n| `method`          | 请求方法(GET/POST等)                                                |\n| `ip`/`clientIp`   | 客户端IP地址                                                       |\n| `headers`         | 请求头对象                                                          |\n| `rules`           | 规则数组，通过push添加新规则                                        |\n| `values`          | 临时值存储对象                                                      |\n| `render(tpl,data)`| 微型模板渲染函数                                                    |\n| `getValue(key)`   | 获取Values中的值                                                    |\n| `parseUrl`        | 同Node.js的`url.parse`                                              |\n| `parseQuery`      | 同Node.js的`querystring.parse`                                      |\n| `sendToClient(frame, options)` | 发送数据（对象、字符串、Buffer）到客户端，用法见下面示例 |\n| `sendToServer(frame, options)` | 发送数据（对象、字符串、Buffer）到服务端，用法见下面示例 |\n| `handleSendToClientFrame(buffer, options)` | 处理（过滤或删除）发送到客户端的数据，用法见下面示例 |\n| `handleSendToServerFrame(buffer, options)` | 处理（过滤或删除）发送到服务端的数据，用法见下面示例 |\n\n"
  },
  {
    "path": "docs/docs/rules/headerReplace.md",
    "content": "# headerReplace\n通过关键字匹配或正则表达式替换指定请求头/响应头。\n\n## 规则语法\n``` txt\npattern headerReplace://value [filters...]\n```\n> `header-name` 不区分大小写\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 三种情况：<br/>• `req.header-name:p1=v1&p2=v2`<br/>• `res.header-name:p1=v1&p2=v2`<br/>• `trailer.trailer-name:p1=v1&p2=v2` | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 请求头 `accept` 字段值的第一个 `html` 关键字被改成 `abc`\nwww.example.com/path headerReplace://req.accept:html=abc\n\n# 请求头 `accept` 字段值的所有 `ml` 关键字被改成 `abc`\nwww.example.com/path2 headerReplace://req.accept:/ml/g=abc\n\n# 修改响应头\nwww.example.com/path3 headerReplace://res.Content-Type:html=plain\n```\n\n`headerReplace` 是用来替换请求/响应头的局部内容，如果要修改请求/响应字段内容，还可以用：\n- 设置请求头：[reqHeaders](./reqHeaders)\n- 设置响应头：[resHeaders](./resHeaders)\n"
  },
  {
    "path": "docs/docs/rules/host.md",
    "content": "# host\n修改请求的 DNS 解析结果，将指定请求解析到特定 IP 地址（域名）及端口，可以看作终极版的系统 hosts 配置功能。\n\n## 规则语法\n``` txt\npattern host://ipOrDomain[:port] [filters...]\n```\n> `port` 如果不填，则沿用请求 URL 的原始端口，如果是指向域名则相当于 `cname` 功能\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | IP + 可选端口 或域名 + 可选端口<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 如果只是 IP 或端口可以省略 `host://`\nwww.example.com/test0 127.0.0.1      # 不改端口，沿用请求 URL 的原始端口\nwww.example.com/test1 127.0.0.1:5173\n\n# CNAME 功能\nwww.example.com/test2 host://www.test.com\nwww.example.com/test3 host://www.test.com:8080\n\n# 高级配置，从请求参数获取目标地址，includeFilter 确保存在该参数\nwww.example.com/test4 host://`${query.target}$:8080` includeFilter:///[?&]target=[\\w-]+/i\n```\n## 与 proxy 的匹配优先级\n以下是优化后的文档，结构更清晰，语言更简洁准确：\n\n---\n\n## 与 host 的匹配优先级\n\n#### 默认行为\n\n当请求同时匹配 `host` 和 `proxy` 规则时：\n- 仅 `host` 规则生效\n- `proxy` 规则自动忽略\n\n#### 修改优先级\n| 配置方式 | 语法 | 效果 |\n|---------|------|------|\n| **优先 proxy** | [`enable://proxyFirst`](./enable) 或 [`lineProps://proxyFirst`](./lineProps) | 仅 `proxy` 生效（覆盖 host） |\n| **同时生效** | [`enable://proxyHost`](./enable) 或 [`lineProps://proxyHost`](./lineProps) | `proxy` 和 `host` 同时生效 |\n\n#### 使用建议\n- 大多数场景使用默认行为即可\n- 需要特殊代理逻辑时才使用 `proxyFirst`\n- 需要双重匹配时使用 `proxyHost`\n\n## 注意事项\n`host` 协议仅对经过规则替换后生成的 `Final URL`（可在 Overview 面板中查看）生效。若 `Final URL` 为空，则会作用于原始请求的 URL。\n\n例如以下规则：\n\n``` txt\nwww.example.com/api www.example.com 127.0.0.1:1234\n```\n\n当请求 `https://www.example.com/api/path` 时，Whistle 会先将其转换为 `https://www.example.com/path`（该结果即为 `Final URL`）。此时希望将该请求指向 `127.0.0.1:1234`，但由于 `host` 规则仅匹配替换前的原始域名 `www.example.com/api`，而转换后的 `Final URL` 已经是 `www.example.com/path`，因此无法命中这条 `host` 规则。\n\n若需要对替换后的请求也生效，可拆解为两条规则：\n\n``` txt\nwww.example.com/api www.example.com\nwww.example.com 127.0.0.1:1234\n```\n\n这样，原始请求先被第一条规则重写，生成新的 `Final URL`，然后再被第二条 `host` 规则匹配，最终指向 `127.0.0.1:1234`。\n\n\n## 常见问题\n1. 与 URL 转换的区别：\n    ``` txt\n    # 服务端收到的 URL 还是 www.example.com\n    www.example.com 127.0.0.1:5173\n\n    # 服务端收到的 URL 是 http://127.0.0.1:5173\n    www.example.com http://127.0.0.1:5173\n    ```\n2. 自动降级为 HTTP 请求：如果配置的目标 IP 为 `127.0.0.1`，且 HTTPS 请求报错会自动降级为 HTTP 请求，方便访问本地服务，用户可以通过以下规则禁用该功能：\n    ``` txt\n    # 禁用本地 HTTPS 自动降级\n    pattern disable://auto2http\n    ```\n"
  },
  {
    "path": "docs/docs/rules/htmlAppend.md",
    "content": "# htmlAppend\n在现有响应内容体后面插入指定内容（仅对响应类型 `content-type` 包含 `html`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern htmlAppend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path htmlAppend://(Hello) file://(-test-)\nwww.example.com/path2 htmlAppend://(Hello) file://(-test-) resType://js\n```\n- 请求 `https://www.example.com/path/to` 响应内容变成 `-test-Hello`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlAppend://{body.txt} file://(-test-)\nwww.example.com/path2 htmlAppend://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path/to` 响应内容变成 `-test-Hello world.`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 规避不规范的请求\n当接口响应类型（`Content-Type`）不规范地返回为 `text/html` 时，可能导致：\n- 前端误将接口数据当作 HTML 解析\n- 注入内容破坏原始数据结构\n- 引发前端解析错误\n\n使用 enable://strictHtml 或 enable://safeHtml 模式保护非 HTML 内容：\n``` txt\nwww.example.com/path1 htmlAppend://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlAppend://(test) file://([-test-])  enable://strictHtml\nwww.example.com/path3 htmlAppend://(test) file://([-test-])  enable://safeHtml\nwww.example.com/path4 htmlAppend://(test) file://(<div>Test</div>) enable://strictHtml\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path4/to` 响应内容变成 `<div>Test</div>test`\n\n`safeHtml`/`strictHtml` 功能参考：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 htmlAppend:///User/xxx/test.txt\nwww.example.com/path2 htmlAppend://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 htmlAppend://temp/blank.txt\n````\n\n## 关联协议\n1. 在响应内容前面注入内容：[reqAppend](./reqAppend)\n2. 在 HTML 类型的响应内容前面注入内容：[htmlPrepend](./htmlPrepend)\n3. 替换 HTML 类型的响应内容：[htmlBody](./htmlBody)\n4. 校验 HTML 内容格式：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n"
  },
  {
    "path": "docs/docs/rules/htmlBody.md",
    "content": "# htmlBody\n\n将现有响应内容体t替换成指定内容（仅对响应类型 `content-type` 包含 `html`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern htmlBody://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path htmlBody://(Hello) file://(-test-)\nwww.example.com/path2 htmlBody://(Hello) file://(-test-) resType://js\n```\n- 请求 `https://www.example.com/path/to` 响应内容变成 `Hello`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlBody://{body.txt} file://(-test-)\nwww.example.com/path2 htmlBody://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path/to` 响应内容变成 `Hello world.`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 规避不规范的请求\n当接口响应类型（`Content-Type`）不规范地返回为 `text/html` 时，可能导致：\n- 前端误将接口数据当作 HTML 解析\n- 注入内容破坏原始数据结构\n- 引发前端解析错误\n\n使用 enable://strictHtml 或 enable://safeHtml 模式保护非 HTML 内容：\n``` txt\nwww.example.com/path1 htmlBody://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlBody://(test) file://([-test-])  enable://strictHtml\nwww.example.com/path3 htmlBody://(test) file://([-test-])  enable://safeHtml\nwww.example.com/path4 htmlBody://(test) file://(<div>Test</div>) enable://strictHtml\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path4/to` 响应内容变成 `test`\n\n`safeHtml`/`strictHtml` 功能参考：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 htmlBody:///User/xxx/test.txt\nwww.example.com/path2 htmlBody://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 htmlBody://temp/blank.txt\n````\n\n## 关联协议\n1. 替换响应内容：[resBody](./resBody)\n2. 在 HTML 类型的响应内容前面注入内容：[htmlPrepend](./htmlPrepend)\n3. 在 HTML 类型的响应内容后面注入内容：[htmlBody](./htmlBody)\n4. 校验 HTML 内容格式：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n\n"
  },
  {
    "path": "docs/docs/rules/htmlPrepend.md",
    "content": "# htmlPrepend\n在现有响应内容体前面面插入指定内容（仅对响应类型 `content-type` 包含 `html`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern htmlPrepend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path htmlPrepend://(Hello) file://(-test-)\nwww.example.com/path2 htmlPrepend://(Hello) file://(-test-) resType://js\n```\n- 请求 `https://www.example.com/path/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    Hello-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 htmlPrepend://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    Hello world.-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-`\n\n#### 规避不规范的请求\n当接口响应类型（`Content-Type`）不规范地返回为 `text/html` 时，可能导致：\n- 前端误将接口数据当作 HTML 解析\n- 注入内容破坏原始数据结构\n- 引发前端解析错误\n\n使用 enable://strictHtml 或 enable://safeHtml 模式保护非 HTML 内容：\n``` txt\nwww.example.com/path1 htmlPrepend://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlPrepend://(test) file://([-test-])  enable://strictHtml\nwww.example.com/path3 htmlPrepend://(test) file://([-test-])  enable://safeHtml\nwww.example.com/path4 htmlPrepend://(test) file://(<div>Test</div>) enable://strictHtml\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `-test-`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `[-test-]`\n- 请求 `https://www.example.com/path4/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    test<div>Test</div>\n    ```\n\n`safeHtml`/`strictHtml` 功能参考：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 htmlPrepend:///User/xxx/test.txt\nwww.example.com/path2 htmlPrepend://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 htmlPrepend://temp/blank.txt\n````\n\n\n## 关联协议\n1. 在响应内容前面注入内容：[reqPrepend](./reqPrepend)\n2. 替换 HTML 类型的响应内容：[htmlBody](./htmlBody)\n3. 在 HTML 类型的响应内容后面注入内容：[htmlPrepend](./htmlPrepend)\n4. 校验 HTML 内容格式：[enable://safeHtml](./enable)、[lineProps://strictHtml](./lineProps)\n"
  },
  {
    "path": "docs/docs/rules/http.md",
    "content": "# http\n将以下三种请求转换为 HTTP 请求（服务端将收到转换后的 HTTP URL）：\n1. **隧道代理：** `tunnel://domain:port`\n    > 示例：`tunnel://www.test.com:443`\n2. **WebSocket：** `ws[s]://domain[:port]/[path/to[?query]]`\n    > 示例：`wss://www.test.com/path?a=1&b=2`\n3. **普通 HTTP/HTTPS：** `http[s]://domain[:port]/[path/to[?query]]`\n    > 示例：`https://www.test.com/path?a=1&b=2`\n\n## 规则语法\n``` txt\npattern http://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]/[path][?query]`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## HTTP/HTTPS 转换\n``` txt\nhttp://www.example.com/path1 http://www.test.com/path/xxx\nhttps://www.example.com/path2 http://www.abc.com/path3/yyy\n```\n1. 自动路径拼接：\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `http://www.example.com/path1`              | `http://www.test.com/path/xxx`             |\n    | `http://www.example.com/path1/a/b/c?query`  | `http://www.test.com/path/xxx/a/b/c?query` |\n    | `https://www.example.com/path2`            | `http://www.abc.com/path3/yyy`             |\n    | `https://www.example.com/path2/a/b/c?query` | `http://www.abc.com/path3/yyy/a/b/c?query` |\n2. 禁用路径拼接：使用 `< >` 或 `( )` 包裹路径\n    ``` txt\n    www.example.com/path1 http://<www.test.com/path/xxx>\n    # www.example.com/path1 http://(www.test.com/path/xxx)\n    ```\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `http://www.test.com/path/xxx` |\n\n## WebSocket 转换\n``` txt\nws://www.example.com/path1 http://www.test.com/path/xxx\nwss://www.example.com/path2 http://www.abc.com/path3/yyy\n```\nWebSocket 请求替换成指定的 ws 请求：\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1`              | `ws://www.test.com/path/xxx`             |\n| `wss://www.example.com/path2/a/b/c?query`  | `ws://www.abc.com/path3/yyy/a/b/c?query`|\n\n同样也支持**自动路径拼接**和**禁用路径拼接**。\n\n## TUNNEL 转换\n``` txt\ntunnel://www.example.com:443 http://www.test.com:123\ntunnel://www.example2.com:443 http://www.test2.com/path\n```\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443`              | `tunnel://www.test.com:123`             |\n| `tunnel://www.example2.com:443`  | `tunnel://www.test2.com:80`|\n\n自动忽略匹配 URL 的路径，HTTP 协议默认端口为 `80`，HTTPS 协议默认端口为 `443`\n\n\n"
  },
  {
    "path": "docs/docs/rules/https-proxy.md",
    "content": "# https-proxy\n`https-proxy` 指令用于将匹配的请求通过指定的 HTTPS 代理服务器转发。\n\n## 规则语法\n``` txt\npattern https-proxy://ipOrDomain[:port] [filters...]\n```\n> `port` 可选，不填则使用默认端口 `443`\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | IP + 可选端口 或域名 + 可选端口<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 将请求代理到 HTTPS 代理: `127.0.0.1:443`\nwww.example.com/path https-proxy://127.0.0.1 # 默认端口 443\n\n# 将当前域名的所有请求代理到 HTTPS 代理: `127.0.0.1:8080`\nwww.example.com https-proxy://127.0.0.1:8080\n\n# 也可以用域名\nwww.example.com/path https-proxy://test.proxy.com # 默认端口 443\nwww.example.com https-proxy://test.proxy.com:8080\n```\n\n## 高级用法\n默认情况下，上游代理会自行解析请求的域名。但某些场景下，你可能希望强制代理直接访问指定的目标 IP（跳过 DNS 解析），例如：\n- 绕过 DNS 污染\n- 直接访问特定后端 IP\n- 测试不同环境的服务\n``` txt\n# 通过查询参数\nwww.example.com https-proxy://127.0.0.1:8080?host=1.1.1.1\nwww.example.com https-proxy://127.0.0.1:8080?host=1.1.1.1:8080\n\n# 通过指令启用\nwww.example.com https-proxy://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com https-proxy://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n```\n> `1.1.1.1` 等价于 `host://1.1.1.1`\n\n## 注意事项\n`https-proxy` 协议仅对经过规则替换后生成的 `Final URL`（可在 Overview 面板中查看）生效。若 `Final URL` 为空，则会作用于原始请求的 URL。\n\n例如以下规则：\n\n``` txt\nwww.example.com/api www.example.com https-proxy://127.0.0.1:1234\n```\n\n当请求 `https://www.example.com/api/path` 时，Whistle 会先将其转换为 `https://www.example.com/path`（该结果即为 `Final URL`）。此时希望将该请求代理到 `127.0.0.1:1234`，但由于 `https-proxy` 规则仅匹配替换前的原始域名 `www.example.com/api`，而转换后的 `Final URL` 已经是 `www.example.com/path`，因此无法命中这条 `https-proxy` 规则。\n\n若需要对替换后的请求也生效，可拆解为两条规则：\n\n``` txt\nwww.example.com/api www.example.com\nwww.example.com https-proxy://127.0.0.1:1234\n```\n\n这样，原始请求先被第一条规则重写，生成新的 `Final URL`，然后再被第二条 `https-proxy` 规则匹配，最终代理到 `127.0.0.1:1234`。\n"
  },
  {
    "path": "docs/docs/rules/https.md",
    "content": "# https\n将以下三种请求转换为 HTTPS 请求（服务端将收到转换后的 HTTPS URL）：\n1. **隧道代理：** `tunnel://domain:port`\n    > 示例：`tunnel://www.test.com:443`\n2. **WebSocket：** `ws[s]://domain[:port]/[path/to[?query]]`\n    > 示例：`wss://www.test.com/path?a=1&b=2`\n3. **普通 HTTP/HTTPS：** `http[s]://domain[:port]/[path/to[?query]]`\n    > 示例：`https://www.test.com/path?a=1&b=2`\n\n## 规则语法\n``` txt\npattern https://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]/[path][?query]`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## HTTP/HTTPS 转换\n``` txt\nhttp://www.example.com/path1 https://www.test.com/path/xxx\nhttps://www.example.com/path2 https://www.abc.com/path3/yyy\n```\n1. 自动路径拼接：\n     | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n     | ----------------------------------------- | ----------------------------------------- |\n     | `http://www.example.com/path1`              | `https://www.test.com/path/xxx`             |\n     | `http://www.example.com/path1/a/b/c?query`  | `https://www.test.com/path/xxx/a/b/c?query` |\n     | `https://www.example.com/path2`            | `https://www.abc.com/path3/yyy`             |\n     | `https://www.example.com/path2/a/b/c?query` | `https://www.abc.com/path3/yyy/a/b/c?query` |\n2. 禁用路径拼接：使用 `< >` 或 `( )` 包裹路径\n    ``` txt\n    www.example.com/path1 https://<www.test.com/path/xxx>\n    # www.example.com/path1 https://(www.test.com/path/xxx)\n    ```\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `https://www.test.com/path/xxx` |\n\n## WebSocket 转换\n``` txt\nws://www.example.com/path1 https://www.test.com/path/xxx\nwss://www.example.com/path2 https://www.abc.com/path3/yyy\n```\nWebSocket 请求替换成指定的 ws 请求：\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1`              | `wss://www.test.com/path/xxx`             |\n| `wss://www.example.com/path2/a/b/c?query`  | `wss://www.abc.com/path3/yyy/a/b/c?query`|\n\n同样也支持**自动路径拼接**和**禁用路径拼接**。\n\n## TUNNEL 转换\n``` txt\ntunnel://www.example.com:443 https://www.test.com:123\ntunnel://www.example2.com:443 https://www.test2.com/path\n```\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443`              | `tunnel://www.test.com:123`             |\n| `tunnel://www.example2.com:443`  | `tunnel://www.test2.com:443`|\n\n⚠️ 自动忽略匹配 URL 的路径，HTTPS 协议默认端口为 `443`\n\n"
  },
  {
    "path": "docs/docs/rules/ignore.md",
    "content": "# ignore\n用于忽略指定协议的匹配规则，也可以忽略当前配置的匹配规则。\n\n## 规则语法\n``` txt\npattern ignore://p1|p2|... [filters...]\n# 等效于：\npattern ignore://p1 ignore://p2 ... [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作协议（`protocol`）名称<br/>`*` 表示所以协议<br/>`-p` 表示排除 `p`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作协议列表](./protocols)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 忽略所有规则，只保留 `file` 协议的规则\nwww.example.com/path ignore://*|-file\n\n# 忽略指定协议：file、host\nwww.example.com/path2 ignore://file|host\n```\n\n## 常见问题\n`ignore` 是根据指定协议忽略已匹配的规则，用这种方式忽略的规则不会继续匹配同类型的其他规则，如果想让请求忽略某个规则后再继续尝试匹配剩余的同类型规则可以采用 [skip](./skip)。\n\n"
  },
  {
    "path": "docs/docs/rules/includeFilter.md",
    "content": "# includeFilter\n在匹配 [pattern](./pattern) 的基础上，进一步筛选符合指定条件的请求（满足任一条件即可保留）。\n\n## 语法规则\n``` txt\npattern operation includeFilter://p1 includeFilter://p2 ...\n```\n> 请求要匹配 `pattern` 及 `p1`、`p2`、... 中的一个条件才能生效\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| operation   | 操作指令 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 仅对 GET 或 POST 请求生效  \nwww.example.com/api/data proxy://127.0.0.1:8080 includeFilter://m:GET includeFilter://m:POST\n```\n\n可以跟 [excludeFilter](./excludeFilter) 一起使用，详细用法参考：[过滤器文档](./filters)\n"
  },
  {
    "path": "docs/docs/rules/inherit.md",
    "content": "# 协议继承\n[http](./http)/[https](./https)/[ws](./ws)/[wss](./wss)/[tunnel](./tunnel) 都限制了目标 URL 的协议，Whistle 也支持根据请求 URL 的协议自动补全。\n## 规则语法\n``` txt\npattern //value [filters...]\n# 或\npattern value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]/[path][?query]`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## HTTP/HTTPS 转换\n``` txt\nhttp://www.example.com/path1 www.test.com/path/xxx\nhttps://www.example.com/path2 www.abc.com/path3/yyy\n```\n1. 自动路径拼接：\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `http://www.example.com/path1`              | `http://www.test.com/path/xxx`             |\n    | `http://www.example.com/path1/a/b/c?query`  | `http://www.test.com/path/xxx/a/b/c?query` |\n    | `https://www.example.com/path2`            | `https://www.abc.com/path3/yyy`             |\n    | `https://www.example.com/path2/a/b/c?query` | `https://www.abc.com/path3/yyy/a/b/c?query` |\n2. 禁用路径拼接：使用 `< >` 或 `( )` 包裹路径\n    ``` txt\n    www.example.com/path1 //<www.test.com/path/xxx>\n    # www.example.com/path1 //(www.test.com/path/xxx)\n    ```\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `[http/https/wss/ws]://www.test.com/path/xxx` |\n\n## WebSocket 转换\n``` txt\nws://www.example.com/path1 www.test.com/path/xxx\nwss://www.example.com/path2 www.abc.com/path3/yyy\n```\nWebSocket 请求替换成指定的 ws 请求：\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1`              | `ws://www.test.com/path/xxx`             |\n| `wss://www.example.com/path2/a/b/c?query`  | `wss://www.abc.com/path3/yyy/a/b/c?query`|\n\n同样也支持**自动路径拼接**和**禁用路径拼接**。\n\n## TUNNEL 转换\n``` txt\ntunnel://www.example.com:443 //www.test.com:123\ntunnel://www.example2.com:443 www.test2.com/path\n```\n| 原始请求                                  | 转换结果（服务端收到的 URL）              |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443`              | `tunnel://www.test.com:123`             |\n| `tunnel://www.example2.com:443`  | `tunnel://www.test2.com:443`|\n\n"
  },
  {
    "path": "docs/docs/rules/jsAppend.md",
    "content": "# jsAppend\n在现有响应内容体后面插入指定内容（仅对响应类型 `content-type` 包含 `javascript`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern jsAppend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path1 jsAppend://(Hello) file://(-test-)\nwww.example.com/path2 jsAppend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsAppend://(Hello) file://(-test-) resType://css\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `-test-<script>Hello</script>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-Hello`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsAppend://{body.txt} file://(-test-)\nwww.example.com/path2 jsAppend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsAppend://{body.txt}) file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `-test-<script>Hello world.</script>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `-test-Hello world.`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 jsAppend:///User/xxx/test.js\nwww.example.com/path2 jsAppend://https://www.xxx.com/xxx/params.js\n# 通过编辑临时文件\nwww.example.com/path3 jsAppend://temp/blank.js\n````\n\n## 为注入的脚本设置 `<script>` 标签属性\n\n通过 `jsAppend` 注入到 HTML 页面的脚本，Whistle 会自动为其包裹 `<script>` 标签。若需要为该标签设置额外的属性，如 `nomodule`、`module`、`defer`、`async`、`crossorigin` 等，可以使用 `lineProps` 参数进行配置。\n\n```txt\nwww.example.com/path1 jsAppend://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsAppend://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsAppend://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsAppend://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsAppend://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### 属性说明与示例\n| 属性 | 用途说明 | 配置示例 |\n|------|----------|----------|\n| `nomodule` | 传统浏览器中执行，不支持 ES 模块的浏览器会运行此脚本 | `lineProps://nomodule` |\n| `module` | 声明为 ES 模块，支持模块化导入 | `lineProps://module` |\n| `defer` | 异步加载，在文档解析完成后执行 | `lineProps://defer` |\n| `async` | 异步加载，下载完成后立即执行 | `lineProps://async` |\n| `crossorigin`| 启用跨域资源共享（CORS）模式 | `lineProps://crossorigin` |\n\n\n## 关联协议\n1. 在响应内容前面注入内容：[reqAppend](./reqAppend)\n2. 在 JavaScript 类型的响应内容前面注入内容：[jsAppend](./jsAppend)\n3. 替换 JavaScript 类型的响应内容：[jsBody](./jsBody)\n"
  },
  {
    "path": "docs/docs/rules/jsBody.md",
    "content": "# jsBody\n将现有响应内容体t替换成指定内容（（仅对响应类型 `content-type` 包含 `javascript`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern jsBody://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path1 jsBody://(Hello) file://(-test-)\nwww.example.com/path2 jsBody://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsBody://(Hello) file://(-test-) resType://css\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `<script>Hello</script>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `Hello`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsBody://{body.txt} file://(-test-)\nwww.example.com/path2 jsBody://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsBody://{body.txt} file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path1/to` 响应内容变成 `<script>Hello world.</script>`\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `Hello world.`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 jsBody:///User/xxx/test.js\nwww.example.com/path2 jsBody://https://www.xxx.com/xxx/params.js\n# 通过编辑临时文件\nwww.example.com/path3 jsBody://temp/blank.js\n````\n\n## 为注入的脚本设置 `<script>` 标签属性\n\n通过 `jsBody` 注入到 HTML 页面的脚本，Whistle 会自动为其包裹 `<script>` 标签。若需要为该标签设置额外的属性，如 `nomodule`、`module`、`defer`、`async`、`crossorigin` 等，可以使用 `lineProps` 参数进行配置。\n\n```txt\nwww.example.com/path1 jsBody://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsBody://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsBody://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsBody://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsBody://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### 属性说明与示例\n| 属性 | 用途说明 | 配置示例 |\n|------|----------|----------|\n| `nomodule` | 传统浏览器中执行，不支持 ES 模块的浏览器会运行此脚本 | `lineProps://nomodule` |\n| `module` | 声明为 ES 模块，支持模块化导入 | `lineProps://module` |\n| `defer` | 异步加载，在文档解析完成后执行 | `lineProps://defer` |\n| `async` | 异步加载，下载完成后立即执行 | `lineProps://async` |\n| `crossorigin`| 启用跨域资源共享（CORS）模式 | `lineProps://crossorigin` |\n\n\n## 关联协议\n1. 替换响应内容：[resBody](./resBody)\n2. 在 JavaScript 类型的响应内容前面注入内容：[jsBody](./jsBody)\n3. 在 JavaScript 类型的响应内容后面注入内容：[jsBody](./jsBody)\n"
  },
  {
    "path": "docs/docs/rules/jsPrepend.md",
    "content": "# jsPrepend\n在现有响应内容体前面插入指定内容（仅对响应类型 `content-type` 包含 `javascript`，且包含响应内容体的状态码（如 `200`/`500` 等）有才效）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern jsPrepend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path1 jsPrepend://(Hello) file://(-test-)\nwww.example.com/path2 jsPrepend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsPrepend://(Hello) file://(-test-) resType://css\n```\n- 请求 `https://www.example.com/path1/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    <script>Hello</script>-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `Hello-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 jsPrepend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsPrepend://{body.txt}) file://(-test-) resType://css\n````\n- 请求 `https://www.example.com/path1/to` 响应内容变成\n    ``` html\n    <!DOCTYPE html>\n    <script>Hello world.</script>-test-\n    ```\n- 请求 `https://www.example.com/path2/to` 响应内容变成 `Hello world.-test-`\n- 请求 `https://www.example.com/path3/to` 响应内容变成 `-test-`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 jsPrepend:///User/xxx/test.js\nwww.example.com/path2 jsPrepend://https://www.xxx.com/xxx/params.js\n# 通过编辑临时文件\nwww.example.com/path3 jsPrepend://temp/blank.js\n````\n\n## 为注入的脚本设置 `<script>` 标签属性\n\n通过 `jsPrepend` 注入到 HTML 页面的脚本，Whistle 会自动为其包裹 `<script>` 标签。若需要为该标签设置额外的属性，如 `nomodule`、`module`、`defer`、`async`、`crossorigin` 等，可以使用 `lineProps` 参数进行配置。\n\n```txt\nwww.example.com/path1 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### 属性说明与示例\n| 属性 | 用途说明 | 配置示例 |\n|------|----------|----------|\n| `nomodule` | 传统浏览器中执行，不支持 ES 模块的浏览器会运行此脚本 | `lineProps://nomodule` |\n| `module` | 声明为 ES 模块，支持模块化导入 | `lineProps://module` |\n| `defer` | 异步加载，在文档解析完成后执行 | `lineProps://defer` |\n| `async` | 异步加载，下载完成后立即执行 | `lineProps://async` |\n| `crossorigin`| 启用跨域资源共享（CORS）模式 | `lineProps://crossorigin` |\n\n\n## 关联协议\n1. 在响应内容前面注入内容：[reqPrepend](./reqPrepend)\n2. 替换 JavaScript 类型的响应内容：[jsBody](./jsBody)\n3. 在 JavaScript 类型的响应内容后面注入内容：[jsPrepend](./jsPrepend)\n\n"
  },
  {
    "path": "docs/docs/rules/lineProps.md",
    "content": "# lineProps\n通过规则启用 proxyHost、proxyTunnel、safeHtml 等功能。\n> 📌 与 [enable](./enable) 的区别：\n>\n> `enable` 是全局生效的配置\n>\n> `lineProps` 只对配置所在行的规则生效\n\n## 规则语法\n``` txt\npattern operation lineProps://action1|action2|... [filters...]\n\n# 等效于：\npattern operation lineProps://action1 lineProps://action2 ... [filters...]\n```\n> `lineProps` 不能单独作为 `operation` 使用，且只对同一行的 `operation` 生效\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| operation   | 操作指令                          | [操作指令文档](./operation)   |\n| action  | 具体动作，详见下面的说明 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n- `important`：类似于 CSS 的 !important，用于提升规则优先级，且仅对同类协议的操作生效\n- `safeHtml`：是一种安全防护机制，当使用 `htmlXxx`/`jsXxx`/`cssXxx` 向 HTML 页面注入内容时，会先检查响应内容的第一个非空白字符是否为 `{` 和 `[`（JSON 对象开头字符），如果不是才会执行注入操作。这可以有效防止对非标准 HTML 响应（如 JSON 接口）的误注入\n- `strictHtml`：是一种安全防护机制，当使用 `htmlXxx`/`jsXxx`/`cssXxx` 向 HTML 页面注入内容时，会先检查响应内容的第一个非空白字符是否为 `<`，如果不是才会执行注入操作。这可以有效防止对非标准 HTML 响应（如 JSON 接口）的误注入\n- `disableAutoCors`：禁用 [file](./file) 协议替换请求时自动添加必要的 CORS (跨域资源共享) 头信息\n- `disableUserLogin`：禁用设置 [statusCode://401](./statusCode) 时显示登录框\n- `enableUserLogin`：设置 [statusCode://401](./statusCode) 是否显示登录框（默认显示，用于关闭 `disable.userLogin`）\n- `internal`：将 `proxy`、`socks`、`host` 协议掉规则同时作用于 Whistle 内部请求\n- `internalOnly`：将 `proxy`、`socks`、`host` 协议掉规则只作用于 Whistle 内部请求\n- `internalProxy`：利用 `proxy`、`socks` 等代理协议将请求转发至其他代理服务器（如另一 Whistle 实例）。启用此功能后，已在第一层代理解密的 HTTPS 请求将以明文形式在代理链中传输，从而上游代理可以直接获取明文数据\n- `proxyFirst`：优先使用 [proxy](./proxy) 规则（默认情况下，同时匹配 `host` 和 `proxy`，只有 `host` 生效）\n- `proxyHost`：[proxy](./proxy) 和 [host](./host) 同时生效\n- `proxyHostOnly`：功能同 `proxyHost`，但如果没有匹配 [host](./host) 则 [proxy](./proxy) 自动失效\n- `proxyTunnel`：跟 `proxyHost` 一同使用，让上游代理再次通过隧道代理到上上游的 HTTP 代理，详见下面的示例\n- `weakRule`：默认情况下，当配置了 [file](./file) 等协议时，[proxy](./proxy) 规则会自动失效。通过设置 `weakRule` 属性，可以提升 [proxy](./proxy) 规则的优先级，使其在上述场景中仍然生效\n- `enableBigData`：支持 [reqMerge](./reqMerge) 或 [resMerge](./resMerge) 匹配大的请求/响应内容\n\n## 配置示例\n#### 未使用 `lineProps://important`\n``` txt\nwww.example.com/path file:///User/xxx/important1.html\nwww.example.com/path file:///User/xxx/important2.html\n```\n访问 `https://www.example.com/path ` 将匹配 `file:///User/xxx/important1.html`\n\n#### 使用 `lineProps://important`\n``` txt\nwww.example.com/path file:///User/xxx/important1.html\nwww.example.com/path file:///User/xxx/important2.html lineProps://important\n```\n访问 `https://www.example.com/path ` 将匹配 `file:///User/xxx/important2.html`\n\n#### 注入文本\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1)) \nwww.example.com/path jsPrepend://(alert(1)) \nwww.example.com/path cssPrepend://(alert(1)) \n```\n访问 `https://www.example.com/path ` 返回响应内容：\n``` html\n<!DOCTYPE html>\n<style>alert(1)</style>\nalert(1)\n<script>alert(1)</script>test\n```\n\n#### 使用 `enable://strictHtml`\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1)) \nwww.example.com/path jsPrepend://(alert(1)) enable://strictHtml\nwww.example.com/path cssPrepend://(alert(1)) \n```\n访问 `https://www.example.com/path` 返回响应内容：\n``` html\ntest\n```\n> `enable://strictHtml` 对所有规则都生效\n\n### 使用 `lineProps://strictHtml`\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1)) \nwww.example.com/path jsPrepend://(alert(1)) lineProps://strictHtml\nwww.example.com/path cssPrepend://(alert(1)) \n```\n访问 `https://www.example.com/path` 返回响应内容：\n``` html\n<!DOCTYPE html>\n<style>alert(1)</style>\nalert(1)test\n```\n> `lineProps://strictHtml` 只对所在行的规则生效\n"
  },
  {
    "path": "docs/docs/rules/locationHref.md",
    "content": "# locationHref\n针对无法通过服务端重定向（`302`/`301`）的场景，通过在 HTML 页面中返回 JavaScript 代码 `window.location.href = targetUrl` 实现客户端跳转。特别适用于：\n- 本地HTML文件加载的APP页面\n- 单页应用(SPA)\n- 特殊框架开发的混合应用(Hybrid App)\n\n## 规则语法\n``` txt\npattern locationHref://targetUrl [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| targetUrl   | 重定向后的 URL，可以是相对路径 |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 基础配置\n``` txt\nwww.example.com/path locationHref://https://www.qq.com\n\nwww.example.com/path2 locationHref://../abc/123\n```\n- 访问 `https://www.example.com/path/to` 重定向到 `https://www.qq.com`（不自动拼接路径）\n- 访问 `https://www.example.com/path2/to` 重定向到 `https://www.example.com/abc/123`\n\n#### 实现路径拼接\n``` txt\n# 通配符\n^www.example.com/path/*** locationHref://`https://www.example.com/$1`\n```\n- 访问 `https://www.example.com/path/to?query` 重定向到 `https://www.example.com/to?query`\n\n#### location.replace\n若需通过 `location.replace(targetUrl)` 实现无历史记录的页面跳转，可按以下格式配置：\n``` txt\nwww.example.com/path locationHref://replace:https://www.qq.com\n```\n> 跳转至目标 URL，且当前页面不会存入浏览器历史记录，用户无法通过“返回”按钮回到原页面\n\n## 关联协议\n1. `302` 跳转：[redirect](./redirect)\n\n"
  },
  {
    "path": "docs/docs/rules/log.md",
    "content": "# log 规则\n\n自动在页面中注入 JavaScript 代码，捕获 JavaScript 异常及 `console.xxx` 日志，并在 Whistle 管理界面中实时显示。\n\n## 功能概述\n- **异常监控**：自动捕获页面中的 JavaScript 执行错误和未处理的 Promise 异常。\n- **日志收集**：拦截并收集所有 `console.log()`、`console.warn()`、`console.error()` 等控制台输出。\n- **可视化查看**：在 Whistle 管理界面中按分组实时查看、筛选和搜索日志。\n- **日志预处理**：支持通过自定义脚本对日志内容进行过滤或格式化处理。\n\n## 规则语法\n```\npattern log://id [filters...]\n```\n\n### 参数说明\n| 参数     | 是否必填 | 描述                                                                 | 详细说明                                                                 |\n|----------|----------|----------------------------------------------------------------------|--------------------------------------------------------------------------|\n| pattern  | 是       | 匹配请求 URL 的表达式，用于指定需要监控的页面                         | [匹配模式语法](./pattern)                                                |\n| id       | 是       | 日志分组标识（普通字符串），用于在 Whistle 界面中区分和筛选不同来源的日志 | 同一分组下的日志会集中显示，支持按 ID 快速切换视图                       |\n| filters  | 否       | 过滤器条件，用于进一步匹配请求或响应的特定特征                         | [过滤器语法](./filters) <br> 支持按 URL、方法、请求头、响应状态码等过滤 |\n\n## 使用方法\n\n### 基础用法\n捕获指定域名下所有页面的日志：\n```\nwww.example.com log://myapp\n```\n\n### 多域名分组监控\n为不同域名或路径设置不同的日志分组，便于分类查看：\n```\nke.qq.com log://ke\nnews.qq.com log://news\napi.example.com log://api\n```\n\n## 高级配置：日志预处理\n\n通过 `jsPrepend` 规则注入自定义脚本，可在日志发送到 Whistle 前进行预处理，例如敏感信息脱敏、格式转换或条件过滤。\n\n### 配置示例\n```\nwww.example.com log:// jsPrepend://{handleLog.js}\n```\n\n### 预处理脚本示例\n```javascript\n// handleLog.js\n// 在日志发送到 Whistle 前执行的自定义处理函数\nwindow.onBeforeWhistleLogSend = function(result, level) {\n  // result: 待输出的原始信息数组\n  // level: 日志级别 ('log', 'warn', 'error', 'info', 'debug')\n  \n  result.forEach(function(msg, i) {\n    // 示例1：隐藏敏感关键词\n    if (typeof msg === 'string' && msg.includes('password')) {\n      result[i] = '[SENSITIVE DATA HIDDEN]';\n    }\n    \n    // 示例2：特定内容转换为结构化对象\n    if (msg === 'abc') {\n      result[i] = {\n        name: 'avenwu',\n        level: level,\n        timestamp: new Date().toISOString()\n      };\n    }\n    \n    // 示例3：过滤掉某些类型的日志\n    if (msg === 'ignore-this-message') {\n      // 返回空数组将阻止此条日志发送\n      result.splice(i, 1);\n    }\n  });\n};\n```\n\n## 界面操作指南\n\n### 查看日志\n1. 打开 Whistle 管理界面（默认为 `http://127.0.0.1:8899`）\n2. 切换到 **Network / Tools / Console** 标签页\n3. 在左上角选择对应的日志分组 ID\n4. 实时查看页面输出的日志和错误信息\n\n### 日志筛选\n- 使用顶部的筛选栏可按日志级别（Log/Warn/Error）快速过滤\n- 支持关键词搜索，快速定位特定日志\n"
  },
  {
    "path": "docs/docs/rules/method.md",
    "content": "# method\n修改请求方法。\n\n## 规则语法\n``` txt\npattern method://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | `get`/`post`/`head` 等请求方法名称（不区分大小写） |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 访问 `https://www.example.com/path/to` 在 Whistle 抓包界面上可以看到请求方法为 `POST`\nwww.example.com/path method://post\n\n# 带过滤条件的方法修改\nwww.example.com/path2 method://patch includeFilter://reqH.content-type=multipart/form-data\n```\n"
  },
  {
    "path": "docs/docs/rules/operation.md",
    "content": "# operation\n\n在 Whistle 中，每条规则由 匹配模式（`pattern`） 和 操作（`operation`） 两部分组成，其中 `operation` 的通用语法为：\n\n``` txt\nprotocol://[value]\n```\n- **protocol**：指定操作类型（如 `file`、`proxy`、`resReplace` 等）\n- **value**：操作内容（支持多种格式，见下文）\n\n## 内联值\n``` txt\npattern reqHeaders://x-proxy=Whistle   # 设置请求头\npattern statusCode://404               # 修改状态码\npattern file://({\"ec\":0})              # 用内联内容（小括号里面的值：`{\"ec\":0}`）响应请求\n```\n\n当操作内容（Value）包含空格、换行符或特殊字符时，无法直接使用内联方式（Inline），需改用以下方法：\n\n## 内嵌值\n\n```` txt\n``` ua.txt\nMozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\n```\npattern ua://{ua.txt}\n````\n\n等价于\n\n```` txt\n``` headers.json\nuser-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\n```\npattern reqHeaders://{headers.json}\n````\n\n## Values 引用\n\n当操作值（Value）需要被多个规则共享时，直接内嵌在规则中会导致无法复用。此时可以将这些值存储在 Whistle 界面的 Values 模块中，并通过键名引用：\n1. 在 Values 里面创建一个名为 `result.json` 的 key 后，填入操作内容\n2. 在规则里面即可通过 `{result.json}` 的方式引用，如：`www.test.com/cgi-bin/test file://{result.json}`\n\n<img src=\"/img//values-demo1.png\" width=\"420\" />\n\n## 文件/远程资源\n``` txt\npattern reqHeaders:///User/xxx/filepath             # 从本地文件加载操作内容\npattern resHeadrs://https://example.com/config.json # 从远处加载 JSON 对象\npattern resHeaders://temp/blank.json                # 通过边境临时文件\n```\n> ⚠️ 注意：http/https/ws/wss/tunnel/host/enable/cache... 等协议禁止通过文件路径或远程 URL 获取内容，详见各协议文档。\n\n## 临时文件\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern protocol://temp.json\n```\n\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `protocol://temp.json`\n3. 在弹出的编辑对话框中输入响应内容\n4. 点击 `Save` 保存\n\n## 小括号用途\n在 Whistle 规则中，protocol://value 的 value 部分可能有三种间接引用类型：\n1. `{key}` - 引用内嵌值\n2. `remote-url` - 远程资源地址\n3. `localfilepath` - 本地文件路径\n\n当需要直接引用上述内容本身（而非它们间接引用内容）作为操作内容时，可以用小括号包裹：\n``` txt\nprotocol://(value)\n```\n\n示例：\n1. `reqHeaders:///User/xxx/yyy.txt` - 从本地文件 `/User/xxx/yyy.txt` 加载操作内容\n2. `reqHeaders://(/User/xxx/yyy.txt)` - 将 `/User/xxx/yyy.txt` 直接作为操作内容\n\n## 模板字符串\nWhistle 提供了类似 ES6 的模板字符串功能，允许您动态引用请求信息并应用到规则配置中。支持以下几种模板字符串：\n\n##### 一般内敛值\n``` txt\npattern protocol://`...${version}...`\n```\n\n##### 内嵌值或 Values 引用\n```` txt\n\n``` test.key\n...${reqId}...\n...${version}...\n```\npattern protocol://`{test.key}`\n````\n\n##### 小括号内容\n```` txt\npattern protocol://`(...${now}...)`\n````\n\n##### 字符串变量\n\n| 变量名称              | 取值                                                         |\n| --------------------- | ------------------------------------------------------------ |\n| `${now}`                 | Date.now()                                                   |\n| `${random}`              | Math.random()                                                |\n| `${randomUUID}`          | crypto.randomUUID()                      |\n| `${randomInt(n)}` 或 `${randomInt(n1-n2)}` | 从 [0, n] 或 [n1, n2] 取一个随机正整数 (Added in: v2.9.104)|\n| `${reqId}`               | Whistle 给每个请求分配的 ID                                  |\n| `${url.protocol}`        | url.parse(fullUrl).protocol                                  |\n| `${url.hostname}`        | url.parse(fullUrl).hostname                                  |\n| `${url.host}`            | url.parse(fullUrl).host                                      |\n| `${url.port}`            | url.parse(fullUrl).port                                      |\n| `${url.path}`            | url.parse(fullUrl).path                                      |\n| `${url.pathname}`        | url.parse(fullUrl).pathname                                  |\n| `${url.search}`          | url.parse(fullUrl).search                                    |\n| `${query.xxx}`           | 请求参数 `xxx` 的值                                          |\n| `${url}`                 | 请求完整 URL                                                 |\n| `${querystring}`         | url.parse(fullUrl).search \\|\\| '?'（不为空）                 |\n| `${searchstring}`        | url.parse(fullUrl).search \\|\\| '?'（不为空）                 |\n| `${method}`              | 请求方法                                                     |\n| `${reqHeaders.xxx}`      | 请求头字段 `xxx` 的值                                        |\n| `${resHeaders.xxx}`      | 响应头字段 `xxx` 的值                                        |\n| `${version}`             | Whistle 版本号                                               |\n| `${port}`                | Whitle 端口号                                                |\n| `${host}`                | Whistle 启动时监听的网卡 IP（默认为空）                      |\n| `${realPort}`            | Whistle 界面 Online 对话框显示的 port（一般为 Whistle 端口号） |\n| `${realHost}`            | Whistle 界面 Online 对话框显示的 host（一般为 Whistle 监听的网卡 IP） |\n| `${clientIp}`            | 客户端 IP                                                    |\n| `${clientPort}`          | 客户端端口                                                   |\n| `${serverIp}`            | 服务端 IP                                                    |\n| `${serverPort}`          | 服务端端口                                                   |\n| `${reqCookies.xxx}`      | 请求 cookie `xxx` 的值                                       |\n| `${resCookies.xxx}`      | 响应 cookie  `xxx` 的值                                      |\n| `${statusCode}`          | 响应状态码                                                   |\n| `${env.xxx}`             | process.env.xxx                                              |\n| `${whistle.plugin-name}` | `whistle.plugin-name://value`  或 `plugin-name://value` 的 `value` |\n\n> `${whistle.plugin-name}` 只在插件的内部规则才可能有值\n\n##### 示例\n\n````txt\n``` test.txt\nnow: ${now}\nrandom: ${random}\nrandomUUID: ${randomUUID}\nreqId: ${reqId}\nurl.protocol: ${url.protocol}\nurl.hostname: ${url.hostname}\nurl.host: ${url.host}\nurl.port: ${url.port}\nurl.path: ${url.path}\nurl.pathname: ${url.pathname}\nurl.search; ${url.search}\nquery: ${query.name}\nurl: ${url}\nquerystring: ${querystring}\nsearchstring: ${searchstring}\nmethod: ${method}\nreqHeaders.accept: ${reqHeaders.accept}\nresHeaders.content-type: ${resHeaders.content-type}\nversion: ${version}\nport: ${port}\nhost: ${host}\nrealPort: ${realPort}\nrealHost: ${realHost}\nclientIp: ${clientIp}\nclientPort: ${clientPort}\nserverIp: ${serverIp}\nserverPort: ${serverPort}\nreqCookies.test: ${reqCookies.test}\nresCookies.test: ${resCookies.test}\nstatusCode: ${statusCode}\nenv.USER: ${env.USER}\n```\n\nwww.test.com/index.html file://`{test.txt}`\n````\n\n访问 `https://www.test.com/index.html?name=avenwu` 则返回响应内容：\n\n``` txt\nnow: 1752301623295\nrandom: 0.6819241513880432\nrandomUUID: e917b9fc-e2ef-4255-9209-11eb417235c5\nreqId: 1752301623294-339\nurl.protocol: https:\nurl.hostname: www.test.com\nurl.host: www.test.com\nurl.port: \nurl.path: /index.html?name=avenwu\nurl.pathname: /index.html\nurl.search; ?name=avenwu\nquery: avenwu\nurl: https://www.test.com/index.html?name=avenwu\nquerystring: ?name=avenwu\nsearchstring: ?name=avenwu\nmethod: GET\nreqHeaders.accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\nresHeaders.content-type: \nversion: 2.9.100\nport: 8899\nhost: \nrealPort: 8899\nrealHost: \nclientIp: 127.0.0.1\nclientPort: 60582\nserverIp: \nserverPort: \nreqCookies.test: \nresCookies.test: \nstatusCode: \nenv.USER: av\n```\n\n## 数据对象\n操作内容除了文本或二进制内容，还有可能是 JSON 对象，Whistle 支持以下 3 种数据对象格式：\n\n#### JSON 格式\n``` js\n{\n  \"key1\": value1,\n  \"key2\": value2,\n  \"keyN\": valueN\n}\n```\n\n#### 行格式\n``` txt\nkey1: value1\nkey2:value2\nkeyN: valueN\n```\n> 以 `冒号+空格` 分隔，如果没有 `冒号+空格` ，则以第一个冒号分隔，如果没有冒号，则 `value` 为空字符串\n\n**多级嵌套：**\n``` txt\na.b.c: 123\nc\\.d\\.e: abc\n```\n等价于：\n``` json\n{\n  \"a\": {\n    \"b\": {\n      \"c\": 123\n    }\n  },\n  \"c.d.e\": \"abc\"\n}\n```\n\n#### 内联格式（请求参数格式）\n\n``` txt\nkey1=value1&key2=value2&keyN=valueN\n```\n> `key` 和 `value` 最好都 `encodeURIComponent`\n\n\n## 常见操作指令速查手册 {operation-manual}\n- [修改请求内容](#request)\n  - [修改请求方法](#method)\n  - [修改请求 URL](#url)\n  - [修改 HTTP 版本](#http-version)\n  - [修改请求头](#req-headers)\n  - [修改请求体](#req-body)\n- [修改响应内容](#response)\n  - [修改状态码](#status-code)\n  - [修改响应头](#res-headers)\n  - [修改响应体](#res-body)\n  - [修改尾部响应头](#trailers)\n- [修改连接过程](#connect)\n  - [修改 DNS](#dns)\n  - [设置代理](#proxy)\n  - [代理和 DNS 同时生效](#proxy-host)\n- [页面调试工具](#tools)\n  - [查看页面 DOM 结构](#weinre)\n  - [查看页面日志及错误信息](#log)\n\n---\n\n## 一、修改请求内容 {#request}\n\n### 1.1 修改请求方法 {#method}\n```txt\n# 基础语法\npattern method://新方法\n\n# 示例\nwww.example.com/path method://post\n```\n\n**数据来源支持：**\n- 直接指定：`method://get`\n- 内嵌值：`method://{keyOfEmbedded}`\n- Values配置：`method://{keyOfValues}`\n\n**注意事项：**\n- 方法名不区分大小写\n- 不支持从本地文件或远程URL获取\n\n**实用示例：**\n```txt\n# 示例1：将所有请求方法改为POST\nwww.example.com/path method://post\n\n# 示例2：仅将PUT请求改为POST\nwww.example.com/path method://post includeFilter://m:put\n\n# 示例3：根据请求体内容修改方法\nwww.example.com/api method://put includeFilter://b:cmdname=test\n```\n\n### 1.2 修改请求 URL {#url}\n#### URL映射\n```txt\nwww.example.com/path/to www.test.com/test\n```\n**映射效果：**\n- `https://www.example.com/path/to?query=abc` → `https://www.test.com/test?query=abc`\n- `https://www.example.com/path/to/subpage` → `https://www.test.com/test/subpage`\n- `wss://www.example.com/path/to/api` → `wss://www.test.com/test/api`\n\n#### 修改请求参数\n```txt\n# 新增/替换参数\npattern urlParams://({\"key\":\"value\"})\n\n# 删除参数\npattern delete://urlParams.paramName\n\n# 示例：修改参数并删除指定参数\nwww.example.com/api urlParams://({\"cmdname\":\"Test\"}) delete://urlParams.oldParam\n```\n\n#### 修改Path路径\n```txt\n# 正则替换\npattern pathReplace://({\"/old/ig\":\"new\"})\n\n# 关键字替换\npattern pathReplace://({\"old\":\"new\"})\n```\n\n**数据来源支持：**\n- 内联JSON\n- 内嵌值\n- Values配置\n- 本地文件\n- 远程URL\n\n### 1.3 修改 HTTP 版本 {#http-version}\n```txt\n# 强制使用普通HTTPS（禁用HTTP/2）\npattern disable://h2\n```\n> 默认会尝试使用HTTP/2建立连接，不支持时自动降级为HTTPS\n\n### 1.4 修改请求头 {#req-headers}\n```txt\n# 新增/替换请求头\npattern reqHeaders://({\"Header-Name\":\"value\"})\n\n# 删除请求头\npattern delete://reqHeaders.headerName\n\n# 示例\nwww.example.com reqHeaders://({\"X-Custom-Header\":\"test\"})\n```\n\n### 1.5 修改请求体 {#req-body}\n#### 合并修改（JSON/表单）\n```txt\npattern reqMerge://({\"newField\":\"value\"})\n```\n\n#### 文本替换\n```txt\n# 正则替换\npattern reqReplace://({\"/search/ig\":\"replace\"})\n\n# 关键字替换\npattern reqReplace://({\"search\":\"replace\"})\n```\n\n#### 完全替换\n```txt\npattern reqBody://(新内容)\n```\n\n#### 删除操作\n```txt\n# 删除特定字段\npattern delete://reqBody.fieldName\n\n# 删除整个请求体\npattern delete://reqBody\n```\n\n**数据来源支持：**\n- 内联值\n- 内嵌值\n- Values配置\n- 本地文件\n- 远程URL\n\n---\n\n## 二、修改响应内容 {#response}\n\n### 2.1 修改状态码 {#status-code}\n```txt\n# 替换现有响应状态码（请求会到达服务器）\npattern replaceStatus://500\n\n# 直接响应状态码（请求不发送到服务器）\npattern statusCode://500\n```\n\n### 2.2 修改响应头 {#res-headers}\n```txt\n# 新增/替换响应头\npattern resHeaders://({\"Header-Name\":\"value\"})\n\n# 删除响应头\npattern delete://resHeaders.headerName\n```\n\n### 2.3 修改响应体 {#res-body}\n#### 合并修改（JSON/JSONP）\n```txt\npattern resMerge://({\"newData\":\"value\"})\n```\n\n#### 文本替换\n```txt\npattern resReplace://({\"/old/ig\":\"new\"})\n```\n\n#### 替换响应体\n```txt\n# 替换服务器返回的内容\npattern resBody://(新内容)\n```\n\n#### 直接响应内容\n```txt\n# 请求不发送到服务器\npattern file://(直接返回的内容) resType://html\n```\n\n#### 删除操作\n```txt\n# 删除响应体字段\npattern delete://resBody.fieldName\n\n# 清空响应体\npattern delete://resBody\n```\n\n### 2.4 修改尾部响应头 {#trailers}\n> Trailers是在分块传输响应后发送的额外头部字段\n\n```txt\n# 新增/替换Trailers\npattern trailers://({\"Trailer-Name\":\"value\"})\n\n# 删除Trailers\npattern delete://trailers.trailerName\n```\n\n---\n\n## 三、修改连接过程 {#connect}\n\n### 3.1 修改 DNS {#dns}\n```txt\n# 设置IP地址\npattern 127.0.0.1\n\n# 设置IP和端口\npattern 127.0.0.1:8080\n\n# CNAME效果（指向其他主机）\npattern host://www.target.com:8080\n```\n\n### 3.2 设置代理 {#proxy}\n```txt\n# HTTP代理\npattern proxy://127.0.0.1:8080\n\n# SOCKS5代理\npattern socks://127.0.0.1:1080\n\n# 支持域名\npattern proxy://proxy.example.com:8080\n```\n\n### 3.3 代理和 DNS 同时生效 {#proxy-host}\n**优先级说明：**\n- 默认：host配置优先于proxy\n- 可调整：通过`lineProps://proxyHost`让两者同时生效\n\n```txt\n# 示例1：只生效host配置\npattern 127.0.0.1:8080 socks://10.1.1.1:1080\n\n# 示例2：只生效proxy配置\npattern 127.0.0.1:8080 socks://10.1.1.1:1080 ignore://host\n\n# 示例3：host和proxy同时生效\npattern 127.0.0.1:8080 socks://10.1.1.1:1080 lineProps://proxyHost\n```\n\n---\n\n## 四、页面调试工具 {#tools}\n\n### 4.1 查看页面 DOM 结构 {#weinre}\n```txt\npattern weinre://调试会话名称\n```\n> 详细用法参考：[weinre文档](./weinre)\n\n### 4.2 查看页面日志及错误信息 {#log}\n```txt\npattern log://日志会话名称\n```\n> 详细用法参考：[log文档](./log)\n\n---\n\n## 数据来源速查表\n\n| 操作类型 | 直接内联 | 内嵌值 | Values | 本地文件 | 远程URL |\n|---------|---------|--------|--------|----------|---------|\n| 请求方法 | ✓ | ✓ | ✓ | ✗ | ✗ |\n| URL参数 | ✓ | ✓ | ✓ | ✓ | ✓ |\n| 请求头 | ✓ | ✓ | ✓ | ✓ | ✓ |\n| 请求体 | ✓ | ✓ | ✓ | ✓ | ✓ |\n| 响应头 | ✓ | ✓ | ✓ | ✓ | ✓ |\n| 响应体 | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Trailers | ✓ | ✓ | ✓ | ✓ | ✓ |\n\n**语法示例：**\n```txt\n# 直接内联\nprotocol://({\"key\":\"value\"})\n\n# 内嵌值\nprotocol://{embeddedKey}\n\n# Values配置\nprotocol://{valuesKey}\n\n# 本地文件\nprotocol:///path/to/file.json\n\n# 远程URL\nprotocol://https://example.com/data.json\n```\n\n---\n\n## 常用过滤条件\n\n| 过滤器 | 说明 | 示例 |\n|--------|------|------|\n| `includeFilter://m:方法` | 按请求方法过滤 | `includeFilter://m:put` |\n| `includeFilter://b:内容` | 按请求体内容过滤 | `includeFilter://b:cmdname=test` |\n| 正则表达式 | 区分大小写匹配 | `/Test/` |\n\n> **提示：** 更多协议和高级用法请参考[完整协议列表](./protocols)\n"
  },
  {
    "path": "docs/docs/rules/pac.md",
    "content": "# pac\n\nPAC（Proxy Auto-Config）是一种通过 JavaScript 脚本自动决定请求代理规则的机制，允许您基于 URL、域名、IP 等条件动态选择代理或直连。\n\n## 规则语法\n``` txt\npattern pac://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n# 内嵌 PAC 脚本\n``` test.pac\nfunction FindProxyForURL(url, host) {\n  // ...\n}\n```\nwww.example.com/path pac://{test.pac}\n\n# Values\nwww.example.com/path1 pac://{test2.pac}\n\n# 本地文件\nwww.example.com/path3 pac:///User/xxx/test.pac\n\n# 远程 PAC 脚本\n* pac://https://raw.githubusercontent.com/imweb/node-pac/master/test/scripts/normal.pac\n````\n\n## 高级用法\n将请求代理到上游代理后，默认情况下上游代理会根据请求的域名通过 DNS 获取服务器 IP 再继续请求，如果想让上游代理根据指定 IP及端口继续请求，可以这么处理：\n``` txt\nwww.example.com pac://https://xxx/path/normal.pac 1.1.1.1 enable://proxyHost\nwww.example.com pac:///User/xxx/test.pac 1.1.1.1:8080 enable://proxyHost\n```\n> `1.1.1.1` 等价于 `host://1.1.1.1`\n\n\n\n## 注意事项\n`pac` 协议仅对经过规则替换后生成的 `Final URL`（可在 Overview 面板中查看）生效。若 `Final URL` 为空，则会作用于原始请求的 URL。\n\n例如以下规则：\n\n``` txt\nwww.example.com/api www.example.com pac://https://xxx/path/normal.pac\n```\n\n当请求 `https://www.example.com/api/path` 时，Whistle 会先将其转换为 `https://www.example.com/path`（该结果即为 `Final URL`）。此时希望 PAC 脚本 `https://xxx/path/normal.pac` 作用于 `https://www.example.com/path`，但由于 `pac` 规则仅匹配替换前的原始域名 `www.example.com/api`，而转换后的 `Final URL` 已经是 `www.example.com/path`，因此无法命中这条 `pac` 规则。\n\n若需要对替换后的请求也生效，可拆解为两条规则：\n\n``` txt\nwww.example.com/api www.example.com\nwww.example.com pac://https://xxx/path/normal.pac\n```\n\n这样，原始请求先被第一条规则重写，生成新的 `Final URL`，然后再被第二条 `pac` 规则匹配。\n"
  },
  {
    "path": "docs/docs/rules/pathReplace.md",
    "content": "# pathReplace\n提供类似 JavaScript 的 String.replace() 方法的能力，通过正则表达式或字符串匹配来动态修改 URL 的路径(`path`) 部分。\n\n> URL 结构：\n> ``` txt\n>   https://www.example.com:8080/path/to/resource?query=string\n>   \\___/   \\_____________/\\____/\\____________________________/\n>     |           |         |                 |             \n>   协议(scheme)  主机(host) 端口(path)       路径(path) \n> ```\n>\n> **路径部分**指 `path/to/resource?query=string` 即不包含开头的  `/`\n\n## 规则语法\n``` txt\npattern pathReplace://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.example.com/path pathReplace://123=abc\n```\n访问 `https://www.example.com/path/123?test=123&value=123` 服务器收到的 URL：`https://www.example.com/path/abc?test=abc&value=abc`\n\n#### 替换多个\n\n```` txt\n``` test.json\ntest: name\n123: abc\n```\nwww.example.com/path2 pathReplace://{test.json}\n````\n访问 `https://www.example.com/path2/123?test=123&value=123` 服务器收到的 URL：`https://www.example.com/path2/abc?name=abc&value=abc`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 pathReplace:///User/xxx/test.json\nwww.example.com/path2 pathReplace://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 pathReplace://temp/blank.json\n````\n\n## 注意事项\n以下配置旨在删除指定路径片段：\n```txt\nwww.example.com/api/ pathReplace://(/api/=/)\n```\n\n**期望效果：**  \n将 `https://www.example.com/api/xxx` 中的 `/api/` 替换为 `/`，得到 `https://www.example.com/xxx`\n\n**实际问题：**  \nWhistle 会将 `/api/` 识别为正则表达式而非普通字符串，导致替换后产生多余斜杠：  \n`https://www.example.com///xxx`\n\n> 即使将 `/api/` 视为字符串，它也无法匹配 `api/xxx/...`，pathReplace 匹配的路径不包含开头的  `/`\n\n**解决方案：**  \n\n``` txt\nwww.example.com pathReplace://(/^api//=)\n```\n> Whistle 规则里面的正则不需要对 `/` 进行转义\n>\n> 新版 Whistle 也可以用 `delete://pathname.0` 删除上面 URL 的 `api/` 路径片段，详见 [delete://pathname.xxx](./delete)\n\n## 关联协议\n1. 修改请求参数：[urlParams](./urlParams)\n2. 删除路径：[delete://pathname.xxx](./delete)\n3. 删除请求参数：[delete://urlParams.xxx](./delete)\n\n"
  },
  {
    "path": "docs/docs/rules/pattern.md",
    "content": "`pattern` 是 Whistle 规则中的第一部分，用于匹配请求 URL，支持域名、路径、通配符、正则表达式等多种匹配方式。\n\n通过 `pattern`，你可以：\n- 精确匹配特定域名或路径\n- 使用通配符匹配一组相关请求\n- 使用正则表达式实现复杂匹配逻辑\n- 支持三种不同类型的 URL 匹配\n\n## 请求 URL 类型\n\nWhistle 支持三种请求 URL 类型：\n\n| 类型 | 格式 | 示例 |\n| :--- | :--- | :--- |\n| **隧道代理** | `tunnel://domain[:port]` | `tunnel://www.test.com:443` |\n| **WebSocket** | `ws[s]://domain[:port]/[path/to[?query]]` | `wss://www.test.com/path?a=1&b=2`<br>`ws://www.example.com:8080/path` |\n| **普通 HTTP/HTTPS** | `http[s]://domain[:port]/[path/to][?query]` | `https://www.test.com/path`<br>`http://www.example.com/path?a=1&b=2` |\n\n## 域名匹配\n\n### 域名结构\n```txt\n[[schema]://]domain[:port]\n```\n\n**参数说明**：\n- `domain`：域名或 IP 地址，支持通配符\n- `port`：端口号（可选），支持通配符\n- `schema`：协议类型（可选，如 `http`、`https`、`ws`、`wss`、`tunnel`），支持通配符\n- `//`：表示使用当前请求的协议（自动适配 HTTP/HTTPS）\n\n### 匹配格式\n\n| 类型 | 格式 | 示例 |\n| :--- | :--- | :--- |\n| **正常域名**（支持通配符） | `domain`<br>`IP`<br>`//domain`<br>`//IP` | `www.example.com`<br>`1.2.3.4`<br>`*.example.com`<br>`//www.example.com`<br>`//1.2.3.4` |\n| **带端口域名**（端口支持通配符） | `domain:port`<br>`//domain:port` | `www.example.com:8080`<br>`//www.ex*le.com:8*` |\n| **带协议域名**（协议支持通配符） | `schema://domain[:port]` | `tunnel://www.*amp*.com`<br>`ws*://**.example.com:443`<br>`http*://www.example.com:8*8` |\n\n### 域名的通配符说明\n\n#### 域名通配符\n- `*`：相当于正则 `/[^/?.]*/`（即域名里面的 0 或任意多个非 `.` 字符）\n- `**`：相当于正则 `/[^/?]*/`（即域名里面的 0 或任意多个字符）\n- `***`（及以上）：不推荐使用\n\n**示例**：\n- `www.example*.com`：可以匹配 `www.example.com`、`www.examplexxx.com:8080` 等，但不能匹配 `www.example.x.com`\n- `*.example.com`：可以匹配 `www.example.com`、`www.example.com:8080`，但不能匹配 `x.www.example.com`\n- `**.example.com`：可以匹配 `x.y.z.www.example.com`、`x.y.www.example.com:8080` 等，但不能匹配 `example.com`\n\n#### 端口通配符\n- `*`（及以上）：相当于正则 `/\\d*/`（即 0 或任意多个数字）\n\n**示例**：\n- `http://www.example.com:8*8`：匹配 `http://www.example.com:88`、`http://www.example.com:8888` 等，但不能匹配 `http://www.example.com:8080`\n\n#### 协议通配符\n- `*`（及以上）：相当于正则 `/[a-z]*/`（即协议里面的 0 或任意多个字符）\n\n**示例**：\n- `http*://www.example.com`：匹配 `http://www.example.com` 和 `https://www.example.com:8080`\n\n\n## 路径匹配\n\nURL 路径结构：\n```txt\n[[schema:]//]domain[:port]/path?query\n```\n\n**示例**：`https://www.example.com/data/test/result?q=123`\n\n### 匹配格式\n\n#### 1. 无协议路径（可以匹配任何协议）\n- `www.example.com[:port]/[path/to[?query]]`\n- `//www.example.com[:port]/[path/to[?query]]`\n\n#### 2. 带协议路径（匹配指定协议的请求）\n- `ws[s]://www.example.com[:port]/[path/to[?query]]`\n- `http[s]://www.example.com[:port]/[path/to[?query]]`\n\n> **注意**：TUNNEL 请求没有路径。\n\n#### 3. 带通配符\n- `ws*://*.example.com/path/to`\n- `http*[s]*://www.example*.com:8*/path/to`\n\n## 匹配机制详解\n\n#### 基础路径匹配\n匹配指定主机和路径及其所有子路径，支持多种协议：\n\n**协议支持**：\n- `http://` / `https://`（HTTP/HTTPS）\n- `tunnel://`（隧道代理）\n- `ws://` / `wss://`（WebSocket连接）\n\n**路径匹配规则**：\n匹配 `www.example.com/path` 及其所有子路径：\n- ✅ `www.example.com/path`\n- ✅ `www.example.com/path/`\n- ✅ `www.example.com/path/subfolder`\n- ✅ `www.example.com/path/file.html`\n- ✅ `www.example.com/path/subfolder/file?query=1`\n- ❌ `www.example.com/path-other`（不以 `/path` 开头）\n- ❌ `www.example.com/path123`（不是 `/path` 的精确前缀）\n\n#### 通配符匹配示例\n规则：\n```txt\nwww.example*.com/path/to www.test.com/test\n```\n\n**匹配场景**：\n- `https://www.example123.com/path/to?query=abc`\n  → 映射为 `https://www.test.com/test?query=abc`\n- `https://www.example123.com/path/to/subpage`\n  → 映射为 `https://www.test.com/test/subpage`\n- `wss://www.example456.com/path/to/api`\n  → 映射为 `wss://www.test.com/test/api`\n\n**不匹配场景**：\n- `https://www.example123.com/path/to123`（路径不以 `/to` 结尾）\n- `https://example123.com/path/to`（缺少 www 前缀）\n- `https://www.example123.com/path`（路径不完整）\n\n#### **带查询参数的精确匹配**\n\n规则：\n```txt\nwww.demo*.com/path/to?name= www.test.com/test\n```\n\n**规则说明**：\n- 路径必须精确匹配 `/path/to`\n- 必须包含 `name=` 查询参数（区分大小写）\n- 匹配后移除 `name=` 参数，保留其他参数\n\n**匹配场景**：\n- `https://www.demo.com/path/to?name=john&age=20`\n  → 映射为 `https://www.test.com/test?age=20`\n- `https://www.demo.com/path/to?name=&sort=asc`\n  → 映射为 `https://www.test.com/test?sort=asc`\n\n**不匹配场景**：\n- `https://www.demo.com/path/to/extra?name=john`（路径不精确）\n- `https://www.demo.com/path/to?Name=john`（参数名大小写不匹配）\n- `https://www.demo.com/path/to?user=john`（缺少 name 参数）\n\n## 路径通配符匹配\n\n由于 `*` 是合法的 URL 路径字符，当需要将其作为通配符使用时，在表达式前面加 `^` 显式声明：\n\n```txt\n^[[schema:]//]domain[:port]/pa**th?qu*ery\n```\n\n**示例**：`^http*://**.example.com/data/*/result?q=*23`\n\nWhistle 内部会将通配符路径转换成对应的正则表达式，其转换规则如下：\n\n### 1. 协议、域名、端口部分通配符规则\n同上面的域名匹配。\n\n### 2. 路径部分通配符规则\n\n| 通配符 | 正则等价 | 匹配范围 | 示例 |\n| :--- | :--- | :--- | :--- |\n| `*` | `/[^?/]*/` | 单级路径（不含 `/` 和 `?`） | `^.../*/*.js` → `.../a/b.js` |\n| `**` | `/[^?]*/` | 多级路径（不含 `?`） | `^.../**file` → `.../a/b/c/test-file` |\n| `***` | `/.*/` | 任意字符（含 `/` 和 `?`） | `^.../data/***file` → `.../a/b/c?test=file` |\n\n### 3. 查询参数通配符规则\n\n| 通配符 | 正则等价 | 匹配范围 | 示例 |\n| :--- | :--- | :--- | :--- |\n| `*` | `/[^&]*/` | 单参数值（不含 `&`） | `^...?q=*123` → `...?q=abc123` |\n| `**` | `/.*/` | 任意字符（含 `&`） | `^...?q=**123` → `...?q=abc&test=123` |\n\n> **记忆要点**：主要记住 `*`、`**`、`***` 三种通配符的匹配范围。\n\n\n## 正则匹配\n\n除简单匹配规则外，Whistle 提供完整的正则表达式支持，语法与 JavaScript 正则完全兼容：\n\n```txt\n/pattern/[flags]\n```\n\n**参数说明**：\n- `pattern`：正则表达式主体\n- `flags`：匹配模式修饰符（可选），支持：\n  - `i`：忽略大小写，如 `/abc/i` 匹配 \"AbC\"\n  - `u`：启用 Unicode 支持，如 `/\\p{Emoji}/u` 匹配 \"😀\"\n\n**示例**：\n```txt\n/\\.test\\./          # 匹配 \".test.\"\n/key=value/i        # 忽略大小写匹配 \"key=value\"\n/\\/statics\\//ui     # Unicode 模式匹配 \"/statics/\"\n```\n\n## 子匹配传值\n\n在 Whistle 的规则配置中，可以通过 `$0`、`$1` 至 `$9` 引用通配符或正则表达式匹配的子匹配内容，并将其传递到操作值中：\n\n```txt\npattern protocol://$0_$1_$2_..._$1\n```\n\n**参数说明**：\n- **$0**：完整匹配结果\n- **$1 - $9**：对应捕获组的内容\n\n### 通配符匹配传值\n```txt\n^http://*.example.com/v0/users/** file:///User/xxx/$1/$2\n```\n\n**匹配示例**：\n- 请求 URL：`http://www.example.com/v2/users/alice/test.html?q=1`\n- 传值结果：\n  - `$1` = `www`\n  - `$2` = `users/alice`\n- 最终替换：本地文件 `/User/xxx/www/alice/test.html` 的内容\n\n### 正则匹配传值\n```txt\n/regexp\\/(user|admin)\\/(\\d+)/ reqHeaders://X-Type=$1&X-ID=$2\n```\n\n**匹配示例**：\n- 请求 URL：`.../regexp/admin/123`\n- 传值结果：\n  - `$1` = `admin`\n  - `$2` = `123`\n- 最终效果：添加请求头 `X-Type: admin` 和 `X-ID: 123`\n\n## 特殊说明\n\n域名匹配和路径匹配在映射本地文件/目录路径或远程新 URL 时会自动拼接路径，即：\n\n```txt\nhttps://*.example.com/path/to https://www.test.com/test\n\nwww.example.com file:///Usr/test\n```\n\n**示例**：\n- 访问 `https://abc.example.com/path/to/x/y/z?query` 会自动替换成新 URL：`https://www.test.com/test/x/y/z?query`\n- 访问 `https://wwww.example.com/path/to/index.html?query` 会自动替换成本地文件：`https://www.test.com/path/to/index.html`（自动去掉 `query`）\n\n## 配置示例\n\n### 基础匹配\n```txt\n# 精确域名匹配\napi.example.com proxy://127.0.0.1:3000\n\n# 端口特定匹配\nwww.example.com:8080 file:///local/dev\n\n# 路径匹配\nwww.example.com/api/users file://{user-data}\n```\n\n### 通配符匹配\n```txt\n# 匹配所有子域名\n**.example.com proxy://127.0.0.1:8080\n\n# 匹配特定前缀的子域名\ndev-**.example.com file:///(dev-mock)\n\n# 匹配所有 HTTP/HTTPS 请求\nhttp*://www.example.com  cache://3600\n```\n\n### 正则匹配\n```txt\n# 匹配数字 ID 的用户页面\n/^https?://www\\.example\\.com/user/(\\d+)/ file://(user-$1)\n\n# 忽略大小写匹配特定路径\n/\\/api\\/v1\\/data/i resBody://({\"version\":\"v1\"})\n\n# 匹配静态资源文件\n/\\.(jpg|png|gif|css|js)$/i cache://86400\n```\n\n### 复杂匹配\n```txt\n# 组合通配符和路径\n^https://**.example.com/api/*/v*/users reqHeaders://x-api-version=$3\n\n# 多条件匹配\nwww.example.com/api file://({\"status\":\"ok\"}) includeFilter://m:GET\nwww.example.com/api file://({\"status\":\"created\"}) includeFilter://m:POST\n```\n\n## 故障排除\n\n### Q: 规则没有匹配到请求\n**A:** 检查：\n1. URL 格式是否正确\n2. 是否包含正确的协议和端口\n3. 通配符或正则表达式是否正确\n4. 是否有更高优先级的规则覆盖\n\n### Q: 正则表达式不生效\n**A:** 检查：\n1. 正则表达式语法是否正确\n2. 是否需要对特殊字符进行转义\n3. 匹配模式标志是否正确设置\n\n### Q: 子匹配传值错误\n**A:** 检查：\n1. 捕获组编号是否正确\n2. 通配符匹配是否正确捕获了预期内容\n3. 正则表达式捕获组是否按预期工作\n\n\n## 扩展阅读\n\n- [规则语法文档](./rule)：了解完整的规则语法结构\n- [操作指令文档](./operation)：学习如何配置操作指令\n- [过滤器文档](./filters)：了解如何精确控制规则生效条件\n"
  },
  {
    "path": "docs/docs/rules/pipe.md",
    "content": "# pipe\n将 HTTP/HTTPS/WebSocket/TUNNEL 请求/响应数据流交给插件处理。\n\n## 规则语法\n``` txt\npattern pipe://plugin-name(pipeValue) [filters...]\n```\n> `(pipeValue)` 可选，在插件 Hook 可以通过 `req.originalReq.pipeValue` 获取\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| plugin-name(pipeValue)   | 插件名称 + 可选参数 |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\ntunnel://wwww.example.com pipe://test\ntunnel://test-tunnel.example.com pipe://test-pipe-tunnel(abc)\n\nwss://test-ws.example.com/path pipe://test-pipe-ws\n\nhttps://www.example.com/path pipe://test-pipe-http(123)\n\n```\n\n具体用法参考：[插件开发文档](../extensions/dev.md#pipe)\n"
  },
  {
    "path": "docs/docs/rules/plugin-vars.md",
    "content": "# `%` 符号用法\n\n通过插件管理界面可直观地配置各项参数，例如可以让 [whistle.autosave](https://github.com/whistle-plugins/whistle.autosave) 插件支持以下配置：\n- 启用存储抓包数据\n- 配置存储抓包数据目录\n\n除管理界面外，您还可以直接在规则文件中使用 `%` 符号快速配置。\n\n## 全局配置（对所有请求生效）\n``` txt\n%autosave=123\n%autosave.enableAutoSave=true\n%autosave.storageDir=/User/xxx/test/sessions\n```\n\n## 精细化配置（针对特定请求）\n``` txt\nwww.test.com/api %autosave=abc\nwww.test.com/api %autosave.enableAutoSave=false [filters...]\nwww.test.com/api %autosave.storageDir= [filters...]\n```\n\n## 获取插件变量值\n在插件的 Hooks 里面可以通过以下代码获取到配置的变量列表：\n\n``` js\nreq.originalReq.globalPluginVars; // 全局变量，如 ['123', 'enableAutoSave=true', 'storageDir=/User/xxx/test/sessions']\nreq.originalReq.pluginVars; // 精细化配置的变量，如 ['abc', 'enableAutoSave=false', 'storageDir=']\n```\n\n## 开启规则自动提示\n如果通过插件变量的配置项是固定的，可以通过插件的 `package.json` 里面的 `whistleConfig` 字段配置可选项，这样在规则中可以自动提醒：\n\n#### 简单提示\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": true,\n  ...\n}\n```\n完成上述配置后，在 Whistle 的 Rules 编辑器中输入 % 字符时，将自动提示以下格式的插件变量：\n``` txt\n%test-plugin-vars=\n```\n\n#### 匿名 Key 值提示\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintList\": [\n        \"test1\",\n        \"test2\",\n        \"test3\"\n      ]\n    }\n  },\n  ...\n}\n```\n\n<img src=\"/img/plugin-vars-hint-list.png\" width=\"260\" />\n\n值和显示内容分离：\n\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintList\": [\n        {\n          \"text\": \"test1\",\n          \"displayText\": \"displayText1\"\n        },\n       {\n          \"text\": \"test2\",\n          \"displayText\": \"displayText2\"\n        },\n        {\n          \"text\": \"test3\",\n          \"displayText\": \"displayText3\",\n          \"help\": \"https://www.example.com/path\"\n        }\n      ]\n    }\n  }\n  ...\n}\n```\n> `displayText` 和 `help` 都是可选，配置 `help: 帮助链接` 时，当选中该提醒时，键盘按 `F1` 键自动打开该帮助链接\n\n<img src=\"/img/plugin-vars-display-list.png\" width=\"220\" />\n\n#### 设置 Key 名称\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintList\": [\n        {\n          \"text\": \"test1\",\n          \"displayText\": \"displayText1\"\n        },\n       {\n          \"text\": \"test2\",\n          \"displayText\": \"displayText2\"\n        },\n        {\n          \"text\": \"test3\",\n          \"displayText\": \"displayText3\",\n          \"help\": \"https://www.example.com/path\"\n        }\n      ],\n      \"hintSuffix\": [\n        \"=\",\n        \".key1=123\",\n        \".key2\"\n      ]\n    }\n  }\n  ...\n}\n```\n<img src=\"/img/plugin-vars-key-hint.png\" width=\"300\" />\n\n选中 `%test-plugin-vars=` 后会自动显示 `hintList` 的内容：\n\n<img src=\"/img/plugin-vars-display-list.png\" width=\"220\" />\n\n#### 借助后台接口\n\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintSuffix\": [\n        \"=\",\n        \".key1=123\",\n        \".key2\"\n      ],\n      \"hintUrl\": \"/cgi-bin/plugin-vars\"\n    }\n  }\n  ...\n}\n```\n> `hintList` 和 `hintUrl` 是互斥的，同时只能使用其中一个，`hintSuffix` 可选\n\n<img src=\"/img/plugin-vars-hint-url1.png\" width=\"360\" />\n\n<img src=\"/img/plugin-vars-hint-url2.png\" width=\"360\" />\n\n`/cgi-bin/plugin-vars` 的实现参考：[插件开发文档](../extensions/dev#rules-hint)\n"
  },
  {
    "path": "docs/docs/rules/protocols.md",
    "content": "# 操作协议列表\n\n## 特殊规则\n1. [@（引入远程规则或设置客户端证书）](./@)\n2. [%（设置插件的变量值）](./plugin-vars)\n\n## Map Local（用本地文件替换响应内容）\n1. [file](./file)\n2. [xfile](./xfile)\n3. [tpl](./tpl)\n4. [xtpl](./xtpl)\n5. [rawfile](./rawfile)\n6. [xrawfile](./xrawfile)\n\n## Map Remote（返回其它 URL 的数据）\n1. [https](./https)\n2. [http](./http)\n3. [wss](./wss)\n4. [ws](./ws)\n5. [tunnel](./tunnel)\n\n## DNS Spoofing（修改请求 IP 地址）\n1. [host](./host)\n2. [xhost](./xhost)\n3. [proxy (http-proxy)](./proxy)\n4. [xproxy (xhttp-proxy)](./xproxy)\n5. [https-proxy](./https-proxy)\n6. [xhttps-proxy](./xhttps-proxy)\n7. [socks](./socks)\n8.  [xsocks](./xsocks)\n9.  [pac](./pac)\n\n## Rewrite Request（修改请求数据）\n1. [urlParams](./urlParams)\n2. [pathReplace](./pathReplace)\n3. [sniCallback](./sniCallback)\n4. [method](./method)\n5. [tlsOptions](./cipher)\n6. [reqHeaders](./reqHeaders)\n7. [forwardedFor](./forwardedFor)\n8. [ua](./ua)\n9. [auth](./auth)\n10. [cache](./cache)\n11. [referer](./referer)\n12. [attachment](./attachment)\n13. [reqCharset](./reqCharset)\n14. [reqCookies](./reqCookies)\n15. [reqCors](./reqCors)\n16. [reqType](./reqType)\n17. [reqBody](./reqBody)\n18. [reqMerge](./reqMerge)\n19. [reqPrepend](./reqPrepend)\n20. [reqAppend](./reqAppend)\n21. [reqReplace](./reqReplace)\n22. [reqWrite](./reqWrite)\n23. [reqWriteRaw](./reqWriteRaw)\n24. [reqRules](./reqRules)\n25. [reqScript](./reqScript)\n\n## Rewrite Reponse（修改响应数据）\n1. [statusCode](./statusCode)\n2. [replaceStatus](./replaceStatus)\n3. [redirect](./redirect)\n4. [locationHref](./locationHref)\n5. [resHeaders](./resHeaders)\n6. [responseFor](./responseFor)\n7. [resCharset](./resCharset)\n8. [resCookies](./resCookies)\n9. [resCors](./resCors)\n10. [resType](./resType)\n11. [resBody](./resBody)\n12. [resMerge](./resMerge)\n13. [resPrepend](./resPrepend)\n14. [resAppend](./resAppend)\n15. [resReplace](./resReplace)\n16. [htmlPrepend](./htmlPrepend)\n17. [htmlBody](./htmlBody)\n18. [htmlAppend](./htmlAppend)\n19. [cssPrepend](./cssPrepend)\n20. [cssBody](./cssBody)\n21. [cssAppend](./cssAppend)\n22. [jsPrepend](./jsPrepend)\n23. [jsBody](./jsBody)\n24. [jsAppend](./jsAppend)\n25. [trailers](./trailers)\n26. [resWrite](./resWrite)\n27. [resWriteRaw](./resWriteRaw)\n28. [resRules](./resRules)\n29. [resScript](./resScript)\n\n## General（请求/响应通用协议）\n1. [pipe](./pipe)\n2. [delete](./delete)\n3. [headerReplace](./headerReplace)\n\n## Throttle（限制流量速度）\n1. [reqDelay](./reqDelay)\n2. [resDelay](./resDelay)\n3. [reqSpeed](./reqSpeed)\n4. [resSpeed](./resSpeed)\n\n## Tools（调试工具）\n1. [weinre](./weinre)\n2. [log](./log)\n\n## Settings（特殊设置）\n1. [style](./style)\n2. [lineProps](./lineProps)\n3. [enable](./enable)\n4. [disable](./disable)\n\n## Filters（规则过滤器）\n1. [ignore](./ignore)\n2. [skip](./skip)\n3. [excludeFilter](./excludeFilter)\n4. [includeFilter](./includeFilter)\n"
  },
  {
    "path": "docs/docs/rules/proxy.md",
    "content": "# proxy (http-proxy)\n`proxy`（或 `http-proxy`）指令用于将匹配的请求通过指定的 HTTP 代理服务器转发。两个指令名称功能完全一致，可互换使用。\n\n## 规则语法\n``` txt\npattern proxy://ipOrDomain[:port] [filters...]\n# 等效写法：\npattern http-proxy://ipOrDomain[:port] [filters...]\n```\n> `port` 可选，不填则使用默认端口 `80`\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | IP + 可选端口 或域名 + 可选端口<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 将请求代理到 HTTP PROXY: `127.0.0.1:80`\nwww.example.com/path proxy://127.0.0.1 # 默认端口 80\n\n# 将当前域名的所有请求代理到 HTTP PROXY: `127.0.0.1:8080`\nwww.example.com proxy://127.0.0.1:8080\n\n# 也可以用域名\nwww.example.com/path proxy://test.proxy.com # 默认端口 80\nwww.example.com proxy://test.proxy.com:8080\n```\n\n## 高级用法\n将请求代理到上游代理后，默认情况下上游代理会根据请求的域名通过 DNS 获取服务器 IP 再继续请求，如果想让上游代理根据指定 IP及端口继续请求，可以这么处理：\n``` txt\n# 通过查询参数\nwww.example.com proxy://127.0.0.1:8080?host=1.1.1.1\nwww.example.com proxy://127.0.0.1:8080?host=1.1.1.1:8080\n\n# 通过指令启用\nwww.example.com proxy://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com proxy://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n````\n> `1.1.1.1` 等价于 `host://1.1.1.1`\n\n## 与 host 的匹配优先级\n\n#### 默认行为\n\n当请求同时匹配 `host` 和 `proxy` 规则时：\n- 仅 `host` 规则生效\n- `proxy` 规则自动忽略\n\n#### 修改优先级\n| 配置方式 | 语法 | 效果 |\n|---------|------|------|\n| **优先 proxy** | [`enable://proxyFirst`](./enable) 或 [`lineProps://proxyFirst`](./lineProps) | 仅 `proxy` 生效（覆盖 host） |\n| **同时生效** | [`enable://proxyHost`](./enable) 或 [`lineProps://proxyHost`](./lineProps) | `proxy` 和 `host` 同时生效 |\n\n#### 使用建议\n- 大多数场景使用默认行为即可\n- 需要特殊代理逻辑时才使用 `proxyFirst`\n- 需要双重匹配时使用 `proxyHost`\n\n## 注意事项\n`proxy` 协议仅对经过规则替换后生成的 `Final URL`（可在 Overview 面板中查看）生效。若 `Final URL` 为空，则会作用于原始请求的 URL。\n\n例如以下规则：\n\n``` txt\nwww.example.com/api www.example.com proxy://127.0.0.1:1234\n```\n\n当请求 `https://www.example.com/api/path` 时，Whistle 会先将其转换为 `https://www.example.com/path`（该结果即为 `Final URL`）。此时希望将该请求代理到 `127.0.0.1:1234`，但由于 `proxy` 规则仅匹配替换前的原始域名 `www.example.com/api`，而转换后的 `Final URL` 已经是 `www.example.com/path`，因此无法命中这条 `proxy` 规则。\n\n若需要对替换后的请求也生效，可拆解为两条规则：\n\n``` txt\nwww.example.com/api www.example.com\nwww.example.com proxy://127.0.0.1:1234\n```\n\n这样，原始请求先被第一条规则重写，生成新的 `Final URL`，然后再被第二条 `proxy` 规则匹配，最终代理到 `127.0.0.1:1234`。\n"
  },
  {
    "path": "docs/docs/rules/rawfile.md",
    "content": "# rawfile\nrawfile 是 [file](./file) 的增强版本，除了支持 [file](./file) 的所有功能外，还允许在文件中定义完整的 HTTP 响应，包括：\n- 响应状态码\n- 响应头部\n- 响应内容\n\n## 规则语法\n``` txt\npattern rawfile://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## Mock 接口\n1. 本地文件 `/User/xxx/raw.txt`\n    ``` js\n    HTTP/1.1 500 OK\n    Content-Type: application/json\n    X-Custom-Header: value\n\n    {\n      \"status\": \"success\",\n      \"data\": \"your content here\"\n    }\n    ```\n2. 配置规则\n    ``` txt\n    https://www.example.com/test/rawfile tpl:///User/xxx/raw.txt\n\n    # 等价于\n    #  https://www.example.com/test/rawfile file:///User/xxx/test.json replaceStatus://500 resType://json resHeaders://x-custom-header=value\n    \n    # 支持远程 URL\n    # pattern rawfile://https://example.com/raw.json\n    ```\n3. 请求 `https://www.example.com/test/rawfile` 返回结果：\n    ``` txt\n    // 状态码\n    500\n\n    // 响应头\n    content-type: application/json\n    x-custom-header: value\n    content-length: 56\n\n    // 响应内容\n    {\n      \"status\": \"success\",\n      \"data\": \"your content here\"\n    }\n    ```\n\n其它功能参考：[file](./file)\n"
  },
  {
    "path": "docs/docs/rules/redirect.md",
    "content": "# redirect\n将匹配的请求立即重定向（302 Found）到指定URL，不请求到后台服务器。\n\n## 规则语法\n``` txt\npattern redirect://targetUrl [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| targetUrl   | 重定向后的 URL，可以是相对路径 |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 基础配置\n``` txt\nwww.example.com/path redirect://https://www.qq.com\n\nwww.example.com/path2 redirect://../abc/123\n```\n- 访问 `https://www.example.com/path/to` 重定向到 `https://www.qq.com`（不自动拼接路径）\n- 访问 `https://www.example.com/path2/to` 重定向到 `https://www.example.com/abc/123`\n\n#### 实现路径拼接\n``` txt\n# 通配符\n^www.example.com/path/*** redirect://`https://www.example.com/$1`\n```\n- 访问 `https://www.example.com/path/to?query` 重定向到 `https://www.example.com/to?query`\n\n#### `301` 跳转\n``` txt\nwww.example.com/path/test redirect://https://www.qq.com replaceStatus://301\n```\n\n## 关联协议\n1. 无法通过 `302` 修改地址的页面可用：[locationHref](./locationHref)\n"
  },
  {
    "path": "docs/docs/rules/referer.md",
    "content": "# referer\n修改请求头的 `referer` 字段，有些服务器会校验请求头的 `referer` 字段，这个协议可以用来绕过这个检测或者测试后台的功能。\n\n## 规则语法\n``` txt\npattern referer://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 完整 URL 链接<br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.example.com/path referer://https://www.test.com/x/y/z?xxx\n```\n\n## 关联协议\n1. 等价于：[reqHeaders://referer=https://xxx](./reqHeaders)\n"
  },
  {
    "path": "docs/docs/rules/replaceStatus.md",
    "content": "# replaceStatus\n\n`replaceStatus` 协议用于替换 HTTP 状态码，在请求响应完成后将原始状态码替换成指定的 HTTP 状态码。通过 `replaceStatus` 协议，你可以：\n- **修改服务器响应的状态码**：基于真实服务器响应进行状态码替换\n- **测试不同状态码的处理逻辑**：在不修改后端代码的情况下测试各种状态码场景\n- **错误场景模拟**：将正常响应转换为错误状态码，测试客户端容错能力\n- **重定向测试**：修改重定向状态码或目标\n\n## 规则语法\n\n`replaceStatus` 协议支持多种方式配置：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要替换的状态码。\n\n```txt\npattern replaceStatus://code [lineProps...] [filters...]\n```\n**示例：**\n```txt\nwww.example.com/api/old replaceStatus://301\n```\n\n### 2. 内嵌值（使用代码块）\n当需要根据条件返回不同状态码，或希望复用配置时可使用此方式。\n\n````txt\npattern replaceStatus://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n404\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values`（中央配置区）中预先定义好的状态码。\n\n```txt\npattern replaceStatus://{key-of-values} [lineProps...] [filters...]\n```\n**前提**：在 `Values` 中存在名为 `key-of-values` 的键，其值为状态码。\n\n### 4. 文件/远程 URL 加载\n**当前不支持** 从本地文件或远程 URL 动态加载内容。\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **code** | 是 | 要替换的 HTTP 响应状态码，如：<br>• `200` OK<br>• `301` 永久重定向<br>• `302` 临时重定向<br>• `404` 未找到<br>• `500` 服务器错误<br>• 支持所有标准 HTTP 状态码 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n## 配置示例\n\n### 基础示例\n```txt\n# 将任何响应替换为 404 状态码\nwww.example.com replaceStatus://404\n\n# 将服务器错误替换为正常状态码\nwww.example.com/api replaceStatus://200 includeFilter://s:500\n\n# 修改重定向状态码\nwww.example.com/redirect replaceStatus://307\n```\n\n### 模拟认证失败\n```txt\n# 将正常响应替换为 401 状态码，触发浏览器认证弹窗\nwww.example.com/secure-page replaceStatus://401\n\n# 禁止弹出登录框，直接返回 401\nwww.example.com/secure-page replaceStatus://401 disable://userLogin\n\n# 或使用 lineProps 达到同样效果\nwww.example.com/secure-page replaceStatus://401 lineProps://disableUserLogin\n```\n\n### 条件替换\n```txt\n# 仅当原始状态码为 200 时替换为 500\nwww.example.com/api replaceStatus://500 includeFilter://s:200\n\n# 仅替换特定路径的响应状态码\nwww.example.com/api/admin replaceStatus://403\n\n```\n\n### 配合其他协议使用\n````txt\n# 替换状态码并修改响应内容\nwww.example.com/api/error replaceStatus://500 resBody://{errMsg}\n\n``` errMsg\n{\"message\":\"Custom error message\"}\n```\n\n# 替换为 301 重定向并设置新的 Location 头\nwww.example.com/old-url replaceStatus://301 resHeaders://location=https://www.example.com/new-url\n````\n\n### 使用内嵌值\n````txt\n``` maintenance-status\n503\n```\n\n# 将网站所有响应替换为维护状态\nwww.example.com replaceStatus://{maintenance-status}\n````\n\n### 环境特定配置\n```txt\n# 仅在测试环境模拟错误\ntest.example.com/api replaceStatus://500\n\n# 开发环境保持原样\n# dev.example.com/api (不设置 replaceStatus)\n```\n\n## 常见使用场景\n\n### 1. 错误处理测试\n```txt\n# 测试客户端对服务器错误的处理\nwww.example.com/api replaceStatus://500 includeFilter://chance:0.1\n```\n\n### 2. 重定向行为测试\n```txt\n# 测试永久重定向缓存\nwww.example.com/old-page replaceStatus://301\n\n# 测试临时重定向\nwww.example.com/temp-redirect replaceStatus://302\n```\n\n### 3. 认证授权测试\n```txt\n# 测试未认证访问\nwww.example.com/protected replaceStatus://401\n\n# 测试权限不足\nwww.example.com/admin replaceStatus://403\n```\n\n### 4. 限流测试\n```txt\n# 测试请求过多场景\nwww.example.com/api/rate-limited replaceStatus://429 resHeaders://retry-after=60\n```\n\n### 5. 维护模式测试\n```txt\n# 测试维护页面\nwww.example.com replaceStatus://503 resBody://(<h1>系统维护中...</h1>)\n```\n\n## 与 statusCode 的区别\n\n`replaceStatus` 协议与 [`statusCode`](./statusCode) 协议的主要区别在于处理时机：\n- **`replaceStatus`**：**先请求后端服务器**，收到响应后替换状态码\n- **`statusCode`**：**不请求后端服务器**，立即返回指定的状态码\n\n\n## 注意事项\n\n### 1. 与 statusCode 的区别\n- **处理时机**：`replaceStatus` 在响应阶段生效，`statusCode` 在请求阶段生效\n- **服务器访问**：`replaceStatus` 会访问服务器，`statusCode` 不会\n- **使用场景**：需要真实服务器数据时用 `replaceStatus`，完全模拟时用 `statusCode`\n\n### 2. 响应内容保留\n- 默认情况下，替换状态码时响应内容保持不变\n- 可通过配合 [`resBody`](./resBody) 协议修改响应内容\n\n### 3. 认证弹窗控制\n- 替换为 `401` 状态码时，默认会触发浏览器认证弹窗\n- 可通过以下方式禁用弹窗：\n  - 添加 `disable://userLogin`\n  - 添加 `lineProps://disableUserLogin`\n  - 使用 [`enable`](./enable) 协议的反向配置\n\n### 4. 重定向处理\n- 替换为重定向状态码（301、302 等）时，通常需要配合修改 `Location` 响应头\n- 否则客户端可能无法正确处理重定向\n\n### 5. 状态码兼容性\n- 确保替换的状态码与响应内容兼容\n- 例如：不应将 HTML 页面替换为 204（无内容）状态码\n\n## 高级用法\n\n### 状态码转换映射\n```txt\n# 将所有重定向统一为 302\nwww.example.com replaceStatus://302 includeFilter://s:301\n```\n\n### 概率性替换\n```txt\n# 10% 的概率将响应替换为错误\nwww.example.com/api replaceStatus://500 inclideFilter://chance:0.1\n```\n\n### 组合多个规则\n```txt\n# 针对不同原始状态码进行不同替换\nwww.example.com/api replaceStatus://200 includeFilter://s:404\nwww.example.com/api replaceStatus://400 includeFilter://s:403\n```\n\n## 故障排除\n\n### Q: 状态码替换没有生效\n**A:** 检查：\n1. 规则 pattern 是否正确匹配请求 URL\n2. 是否有其他更高优先级的规则覆盖\n3. 过滤器条件是否满足（特别是对原始状态码的过滤）\n\n### Q: 响应内容与状态码不匹配\n**A:** 检查：\n1. 是否同时修改了响应内容\n2. 响应头是否与新状态码兼容\n3. 客户端是否能正确处理该状态码\n\n### Q: 浏览器弹出认证窗口\n**A:** 对于 401 状态码：\n1. 检查是否添加了 `disable://userLogin` 或 `lineProps://disableUserLogin`\n2. 确认规则顺序是否正确\n\n### Q: 重定向循环\n**A:** 检查：\n1. 是否设置了正确的 `Location` 头\n2. 重定向目标是否正确\n3. 是否有多个重定向规则冲突\n\n## 关联协议\n\n1. **直接返回状态码**：[statusCode](./statusCode)\n   - 不请求服务器，直接返回指定的状态码\n\n2. **禁止认证弹窗**：[disable](./disable) 或 [lineProps](./lineProps)\n   - 禁用 401 状态码触发的浏览器登录弹窗\n\n3. **设置响应内容**：[resBody](./resBody)\n   - 为状态码响应添加自定义内容\n\n4. **设置响应头**：[resHeaders](./resHeaders)\n   - 为重定向等场景设置必要的响应头\n\n## 扩展阅读\n\n- [HTTP 状态码 MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status)：了解所有 HTTP 状态码的含义\n- [匹配模式文档](./pattern)：详细了解 URL 匹配规则\n- [过滤器文档](./filters)：了解更多过滤器功能\n"
  },
  {
    "path": "docs/docs/rules/reqAppend.md",
    "content": "# reqAppend\n在现有请求内容体后面插入指定内容（仅对包含内容体的请求有效，如 `POST`、`PUT` 等）\n> ⚠️ 注意：GET、HEAD 等无内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern reqAppend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path reqAppend://(Hello) reqBody://(-test-) method://post\n```\n请求 `https://www.example.com/path/to` 请求内容变成 `-test-Hello`。 \n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqAppend://{body.txt} reqBody://(-test-) method://post\n````\n请求 `https://www.example.com/path/to` 请求内容变成 `-test-Hello world.`。 \n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqAppend:///User/xxx/test.txt\nwww.example.com/path2 reqAppend://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 reqAppend://temp/blank.txt\n````\n\n## 关联协议\n1. 在请求内容后面追加内容：[reqPrepend](./reqPrepend)\n2. 在请求内容前面注入内容：[reqBody](./reqBody)\n3. 在响应内容后面追加内容：[resPrepend](./resPrepend)\n\n"
  },
  {
    "path": "docs/docs/rules/reqBody.md",
    "content": "# reqBody\n替换指定请求的内容体（仅对包含内容体的请求有效，如 `POST`、`PUT` 等）\n> ⚠️ 注意：GET、HEAD 等无内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern reqBody://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path reqBody://(Hello) method://post\n```\n请求 `https://www.example.com/path/to` 请求内容变成 `Hello`。 \n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqBody://{body.txt} method://post\n````\n请求 `https://www.example.com/path/to` 请求内容变成 `Hello world.`。 \n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqBody:///User/xxx/test.txt\n# 在 Windows 中，路径分隔符通常为 `\\`，但同时它也接受 `/`\nwww.example.com/path1 reqBody://D:\\test.txt\nwww.example.com/path2 reqBody://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 reqBody://temp/blank.txt\n````\n\n## 关联协议\n1. 在请求内容前面注入内容：[reqPrepend](./reqPrepend)\n2. 在请求内容后面追加内容：[reqAppend](./reqAppend)\n3. 替换响应内容：[reqBody](./reqBody)\n"
  },
  {
    "path": "docs/docs/rules/reqCharset.md",
    "content": "# reqCharset\n修改请求头 `content-type` 的 `charset` 部分，设置请求内容的编码。\n> `content-type` 结构：\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## 规则语法\n``` txt\npattern reqCharset://encoding [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| encoding | 编码名称，如 `utf8` <br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 设置请求编码，请求头 `content-type` 空字段被改成 `; charset=utf8`\nwww.example.com/path reqCharset://utf8\n\n# 设置请求编码，请求头 `content-type` 空字段被改成 `text/html; charset=utf8`\nwww.example.com/path resType://json reqCharset://utf8\n```\n\n## 关联协议\n1. 直接修改请求头：[reqHeaders://content-type=xxx](./reqHeaders)\n2. 修改请求内容编码：[reqType://media-type](./reqCharset)\n"
  },
  {
    "path": "docs/docs/rules/reqCookies.md",
    "content": "# reqCookies\n修改请求 `Cookie` 头。\n\n## 规则语法\n``` txt\npattern reqCookies://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | Cookie 对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\nCookie 对象结构：`key-value`\n\n## 配置示例\n#### 内联方式\n```` txt\nwww.example.com/path reqCookies://k1=v1&k2=v2\n````\n请求头新增两个请求 cookie：`k1: v1`/`k2: v2`\n\n### 内嵌模式\n```` txt\n``` cookies.json\nkey1: value1\nkey2: value2\n```\n# 或\n``` cookies.json\n{\n  key1: 'value1',\n  key2: 'value2'\n}\n```\nwww.example.com/path reqCookies://{cookies.json}\n````\n请求头新增两个请求 cookie：`key1: value1`/`key2: value2`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqCookies:///User/xxx/test.json\nwww.example.com/path2 reqCookies://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 reqCookies://temp/blank.json\n````\n\n## 关联协议\n1. 删除请求 cookie：[delete://reqCookies.xxx](./delete)\n2. 删除所有请求 cookie：[delete://reqHeaders.cookie](./delete)\n\n"
  },
  {
    "path": "docs/docs/rules/reqCors.md",
    "content": "# reqCors\n设置请求的跨域资源共享（[CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS))）头部信息，解决跨域请求问题。\n\n## 规则语法\n``` txt\npattern resCors://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | CORS 对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 快捷模式\n``` txt\nwww.example.com/path reqCors://*\n```\n设置请求头 `origin: *`\n\n#### 详细配置模式\n```` txt\n``` cors.json\norigin: *\nmethod: POST\nheaders: x-test\n```\nwww.example.com/path reqCors://{cors.json}\n````\n设置请求头：\n``` txt\norigin: *\naccess-control-request-method: POST\naccess-control-request-headers: x-test\n```\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqCors:///User/xxx/test.json\nwww.example.com/path2 reqCors://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 reqCors://temp/blank.json\n````\n\n## 关联协议\n1. 删除请求头字段：[delete://reqHeaders.orogin](./delete)\n2. 设置响应 CROS：[resCors](./resCors)\n\n"
  },
  {
    "path": "docs/docs/rules/reqDelay.md",
    "content": "# reqDelay\n\n设置延迟请求的时间(单位：毫秒)。\n\n## 规则语法\n``` txt\npattern reqDelay://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 时间数值（单位：毫秒）<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 延迟 3000 毫秒（即 3 秒）请求\nwww.example.com/path reqDelay://3000\n\n# 延迟 5000 秒（即 5 秒）后 abort 请求\nwww.example.com/path2 reqDelay://5000 enable://abort\n```\n\n## 随机延迟\n可以利用 [reqScript](./reqScript) 实现随机延迟请求：\n```` txt\n# 随机设置 reqDelay://1000 ～ reqDelay://6000 毫秒\n``` delay.js\nrules.push(`* reqDelay://${1000 + Math.ceil(5000 * Math.random())}`);\n\n```\n\nwww.example.com/path reqScript://{delay.js}\n````\n"
  },
  {
    "path": "docs/docs/rules/reqHeaders.md",
    "content": "# reqHeaders\n动态修改请求头部信息，支持多种数据源和批量操作方式。\n\n## 规则语法\n``` txt\npattern reqHeaders://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n``` txt\nwww.example.com/path reqHeaders://x-proxy=Whistle\n```\n访问 `https://www.example.com/path/to` 在 Whistle Network 或后台服务器可以看到新增请求头：\n``` txt\nx-proxy: Whistle\n```\n\n#### 设置多个请求头\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 reqHeaders://{test.json}\n\n# 等价于：www.example.com/path2 reqHeaders://x-test1=1&x-test2=&x-test3=abc\n````\n访问 `https://www.example.com/path2/to` 在 Whistle Network 或后台服务器可以看到新增请求头：\n``` txt\nx-test1: 1\nx-test2: \nx-test3: abc\n```\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqHeaders:///User/xxx/test.json\nwww.example.com/path2 reqHeaders://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 reqHeaders://temp/blank.json\n````\n\n## 关联协议\n1. 更灵活的修改请求头的方式：[headerReplace](./headerReplace)\n2. 删除请求头字段：[delete://reqHeaders.xxx](./delete)\n\n"
  },
  {
    "path": "docs/docs/rules/reqMerge.md",
    "content": "# reqMerge\n将指定数据对象智能合并到请求体中，适合修改部分参数而不影响其余内容，支持多种请求格式：\n- 常规表单 (`application/x-www-form-urlencoded`)\n- 文件上传表单 (`multipart/form-data`)\n- JSON 请求 (`application/json`)\n\n## 规则语法\n``` txt\npattern reqMerge://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 内联方式\n``` txt\nwww.example.com/path reqMerge://test=123 reqBody://(name=avenwu) reqType://form method://post\n```\n访问 `https://www.example.com/path/to` 服务器收到的请求内容：\n``` txt\nname=avenwu&test=123\n```\n\n#### 内嵌方式\n```` txt\n``` reqMerge.json\na.b.c: 123\nc\\.d\\.e: abc\n```\nwww.example.com/path reqMerge://{reqMerge.json} reqBody://({\"name\":\"avenwu\"}) reqType://json method://post\n````\n访问 `https://www.example.com/path/to` 抓包请求内容：\n``` js\n{\"name\":\"avenwu\",\"a\":{\"b\":{\"c\":123}},\"c.d.e\":\"abc\"}\n```\n\n#### 本地/远程资源\n```` txt\nwww.example.com/path1 reqMerge:///User/xxx/test.json\nwww.example.com/path2 reqMerge://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 reqMerge://temp/blank.json\n````\n\n## 注意事项：请求内容大小限制\n为了保障处理性能，`reqMerge` 默认对请求内容大小有限制。\n- **限制说明**：自动合并处理仅适用于请求体小于 2MB 的请求。超过此大小的响应将被跳过。\n- **如何覆盖**：如果您确认需要处理更大的请求内容，可以通过在匹配的规则中添加以下指令来显式启用：\n\n``` txt\npattern enable://reqMergeBigData\n\n# 或\nwww.example.com/path1 reqMerge:///User/xxx/test.json lineProps://enableBigData\n```\n启用后，reqMerge 将尝试处理更大体积的请求，请注意这可能增加内存消耗和处理时间。\n\n## 关联协议\n1. 通过关键字或正则替换：[reqReplace](./reqReplace)\n2. 修改响应内容对象：[resMerge](./resMerge)\n\n\n"
  },
  {
    "path": "docs/docs/rules/reqPrepend.md",
    "content": "# reqPrepend\n在现有请求内容体开头插入指定内容（仅对包含内容体的请求有效，如 `POST`、`PUT` 等）\n> ⚠️ 注意：GET、HEAD 等无内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern reqPrepend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path reqPrepend://(Hello) reqBody://(-test-) method://post\n```\n请求 `https://www.example.com/path/to` 请求内容变成 `Hello-test-`。 \n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqPrepend://{body.txt} reqBody://(-test-) method://post\n````\n请求 `https://www.example.com/path/to` 请求内容变成 `Hello world.-test-`。 \n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqPrepend:///User/xxx/test.txt\nwww.example.com/path2 reqPrepend://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 reqPrepend://temp/blank.txt\n````\n\n## 关联协议\n1. 在请求内容前面注入内容：[reqBody](./reqBody)\n2. 在请求内容后面追加内容：[reqAppend](./reqAppend)\n3. 在响应内容前面注入内容：[resPrepend](./resPrepend)\n\n"
  },
  {
    "path": "docs/docs/rules/reqReplace.md",
    "content": "# reqReplace\n使用类似 JavaScript `String.replace()` 的方法替换请求体内容（仅对包含内容体的请求有效，如 `POST`、`PUT` 等），支持多种文本格式：\n- 表单数据 (`application/x-www-form-urlencoded`)\n- JSON (`application/json`)\n- XML (`application/xml`)\n- HTML (`text/html`)\n- 纯文本 (`text/xxx`)\n\n## 规则语法\n``` txt\npattern reqReplace://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 替换配置对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 内联方式\n```` txt\nwww.example.com/path reqBody://(00user-11test-22user-33test) reqReplace://user=abc&/\\d+/g=number reqType://txt method://post\n````\n请求 `https://www.example.com/path/to` 后台收到的请求内容为：\n``` txt\nnumberabc-numbertest-numberabc-numbertest\n```\n\n### 内嵌模式\n```` txt\n``` reqReplace.json\nuser: name\n/\\d+/g: num\n```\n# 或（注意转义符）\n``` reqReplace.json\n{\n  'user': 'name',\n  '/\\\\d+/g': 'num'\n}\n```\nwww.example.com/path reqBody://(00user-11test-22user-33test) reqReplace://{reqReplace.json} reqType://txt method://post\n````\n请求 `https://www.example.com/path/to` 后台收到的请求内容为：\n``` txt\nnumname-numtest-numname-numtest\n```\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 reqReplace:///User/xxx/test.json\nwww.example.com/path2 reqReplace://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 reqReplace://temp/blank.json\n````\n\n## 关联协议\n1. 对象合并：[reqMerge](./reqMerge)\n2. 完全替换：[reqBody](./reqBody)\n\n\n\n"
  },
  {
    "path": "docs/docs/rules/reqRules.md",
    "content": "# reqRules\n为匹配的请求批量设置多个规则，实现复杂场景下的请求处理需求。\n\n## 规则语法\n``` txt\npattern reqRules://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 规则内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n``` test.txt\n* file://(<div>hello<div>)\n* resType://text\n```\n\n``` test2.txt\n* resAppend://(test)\n```\n\nwww.example.com/path reqRules://{test.txt} reqRules://{test2.txt}\n````\n访问 `https://www.example.com/path/to` 返回内容：\n``` txt\n<div>hello<div>test\n```\n## 关联协议\n1. 请求阶段脚本规则：[reqScript](./reqScript)\n2. 响应阶段批量规则：[resRules](./resRules)\n3. 响应阶段脚本规则：[resScript](./resScript)\n4. 更复杂的定制需求：[插件开发](../extensions/dev)\n"
  },
  {
    "path": "docs/docs/rules/reqScript.md",
    "content": "# reqScript\n在请求阶段通过 JavaScript 脚本动态生成规则，实现复杂请求处理逻辑。脚本可以访问请求上下文信息，并动态生成匹配规则。\n\n## 规则语法\n``` txt\npattern reqScript://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 生成规则的 JS 脚本，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n``` test.js\nif (method === 'GET') {\n    rules.push('* resType://text');\n    rules.push('* file://(<div>GET-Request</div>)');\n} else {\n    rules.push('* statusCode://403');\n}\n```\nwww.example.com/path reqScript://{test.js}\n````\n访问 `https://www.example.com/path/to` 返回内容：\n\n#### 可用全局变量\n\n| 变量/方法          | 描述                                                                 |\n|--------------------|---------------------------------------------------------------------|\n| `url`             | 完整请求URL                                                         |\n| `method`          | 请求方法(GET/POST等)                                                |\n| `ip`/`clientIp`   | 客户端IP地址                                                       |\n| `headers`         | 请求头对象                                                          |\n| `body`            | 请求内容(最大16KB)                                                  |\n| `rules`           | 规则数组，通过push添加新规则                                        |\n| `values`          | 临时值存储对象                                                      |\n| `render(tpl,data)`| 微型模板渲染函数                                                    |\n| `getValue(key)`   | 获取Values中的值                                                    |\n| `parseUrl`        | 同Node.js的`url.parse`                                              |\n| `parseQuery`      | 同Node.js的`querystring.parse`                                      |\n\n\n## 关联协议\n1. 请求阶段脚本规则：[reqScript](./reqScript)\n2. 请求阶段批量规则：[reqRules](./reqScript)\n3. 响应阶段批量规则：[resRules](./resRules)\n4. 更复杂的定制需求：[插件开发](../extensions/dev)\n"
  },
  {
    "path": "docs/docs/rules/reqSpeed.md",
    "content": "# reqSpeed\n设置请求 Body 的传输速度(单位：kb/s，千比特/每秒).\n\n## 规则语法\n``` txt\npattern reqSpeed://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 传输速度（单位：kb/s，千比特/每秒）<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 限制请求内容的传输速度：3kb/s\nwww.example.com/path reqSpeed://3\n```\n该规则对 `CONNECT`（TUNNEL 请求）、`UPGRADE`（WebSocket 请求）及响应状态码为 `204` 等无响应 Body 的请求无效\n"
  },
  {
    "path": "docs/docs/rules/reqType.md",
    "content": "# reqType\n修改请求头 `content-type` 的 `media-type` 和 `charset` 部分。\n> `content-type` 结构：\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## 规则语法\n``` txt\npattern reqType://type[;charset] [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| type[;charset] | `type` 请求类型，`charset` 编码 <br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n`reqType` 主要用来修改请求类型的 `media-type` 部分 ，`charset` 部分可选，如果没有设置 `charset` 部分则会保留请求原始的 `charset` 部分（如果存在）。\n\n## 配置示例\n\n#### 快捷命令（不包含 `/` 的字符串）\n- `urlencoded`: `application/x-www-form-urlencoded`\n- `form`: `application/x-www-form-urlencoded`\n- `json`: `application/json`\n- `xml`: `application/xml`\n- `text`: `text/plain`\n- `upload`: `multipart/form-data`\n- `multipart`: `multipart/form-data`\n- 其它：[mime](https://github.com/broofa/mime).lookup(type)\n\n``` txt\n# 不带编码 请求头的 `content-type` 变成 `application/x-www-form-urlencoded`\nwww.example.com/path reqType://form\n\n# 带编码，请求头的 `content-type` 变成 `application/json;charset=utf8`\nwww.example.com/path reqType://json;charset=utf8\n```\n#### 完整类型\n``` txt\n# 请求头的 `content-type` 变成 `text/plain`\nwww.example.com/path reqType://text/plain\n\n# 请求头的 `content-type` 变成 `text/plain;charset=utf8`\nwww.example.com/path reqType://text/plain;charset=utf8\n```\n\n## 关联协议\n1. 直接修改请求头：[reqHeaders://content-type=xxx](./reqHeaders)\n2. 修改请求内容编码：[reqCharset://encoding](./reqCharset)\n"
  },
  {
    "path": "docs/docs/rules/reqWrite.md",
    "content": "# reqWrite\n将请求内容体保存到指定目录或文件中，适用于需要记录请求数据的场景：\n- 自动根据请求URL生成文件路径\n- 采用安全写入策略（可以采用 [enable://forceReqWrite](./enable) 强制覆盖）\n- 仅对包含内容体的请求有效（POST/PUT/PATCH等）\n- GET/HEAD 等无内容体请求会自动跳过\n- 保存失败时自动跳过\n\n## 规则语法\n``` txt\npattern reqWrite://fileOrDirPath [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| fileOrDirPath   | 存储数据的目录或文件路径 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n```txt\nwproxy.org/docs reqWrite:///User/xxx/test/\n```\n##### 路径解析规则：\n1. **当访问具体文件时**  \n   `https://wproxy.org/docs/test.html`  \n   → 保存到：`/User/xxx/test/test.html`\n\n2. **当访问目录路径时**  \n   `https://wproxy.org/docs/`  \n   → 保存到：`/User/xxx/test/index.html`（目标路径为 `/User/xxx/test/`，结尾为 `/` 或 `\\` 自动追加 `index.html`）\n\n#### 目录显式配置\n```txt\nwproxy.org/docs/ reqWrite:///User/xxx/test/\n```\n##### 路径解析差异：\n1. **访问子路径时**  \n   `https://wproxy.org/docs/test.html`  \n   → 仍保存到：`/User/xxx/test/test.html`\n\n2. **访问配置目录时**  \n   `https://wproxy.org/docs/`  \n   → 直接保存到：`/User/xxx/test/`（作为整体文件）\n\n> 💡 关键区别：  \n> - 规则路径是否以 `/` 或 `\\`结尾，决定了目录请求的保存方式  \n> - 非目录路径（无结尾 `/` 或 `\\`）会直接保存为指定文件\n> - 目录路径（有结尾 `/` 或 `\\`）会自动补全 `index.html`\n\n#### 指定文件\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ reqWrite:///User/xxx/test/index.html\n```\n> 通过正则匹配限定请求 URL\n\n## 关联协议\n1. 启用强制写入：[enable://forceReqWrite](./enable)\n2. 写入请求都所有内容：[reqWriteRaw](./reqWriteRaw)\n"
  },
  {
    "path": "docs/docs/rules/reqWriteRaw.md",
    "content": "# reqWriteRaw\n将请求的完整内容（包括请求方法、路径、协议、请求头、内容）保存到指定目录或文件中，适用于需要记录请求数据的场景：\n- 自动根据请求URL生成文件路径\n- 采用安全写入策略（可以采用 [enable://forceReqWrite](./enable) 强制覆盖）\n- 仅对包含内容体的请求有效（POST/PUT/PATCH等）\n- GET/HEAD 等无内容体请求会自动跳过\n- 保存失败时自动跳过\n\n## 规则语法\n``` txt\npattern reqWriteRaw://fileOrDirPath [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| fileOrDirPath   | 存储数据的目录或文件路径 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n```txt\nwproxy.org/docs reqWriteRaw:///User/xxx/test/\n```\n##### 路径解析规则：\n1. **当访问具体文件时**  \n   `https://wproxy.org/docs/test.html`  \n   → 保存到：`/User/xxx/test/test.html`\n\n2. **当访问目录路径时**  \n   `https://wproxy.org/docs/`  \n   → 保存到：`/User/xxx/test/index.html`（目标路径为 `/User/xxx/test/`，结尾为 `/` 或 `\\` 自动追加 `index.html`）\n\n#### 目录显式配置\n```txt\nwproxy.org/docs/ reqWriteRaw:///User/xxx/test\n```\n##### 路径解析差异：\n1. **访问子路径时**  \n   `https://wproxy.org/docs/test.html`  \n   → 仍保存到：`/User/xxx/test/test.html`\n\n2. **访问配置目录时**  \n   `https://wproxy.org/docs/`  \n   → 直接保存到：`/User/xxx/test/`（作为整体文件）\n\n> 💡 关键区别：  \n> - 规则路径是否以 `/` 或 `\\`结尾，决定了目录请求的保存方式  \n> - 非目录路径（无结尾 `/` 或 `\\`）会直接保存为指定文件\n> - 目录路径（有结尾 `/` 或 `\\`）会自动补全 `index.html`\n\n#### 指定文件\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ reqWriteRaw:///User/xxx/test/index.html\n```\n> 通过正则匹配限定请求 URL\n\n## 关联协议\n1. 启用强制写入：[enable://forceReqWrite](./enable)\n2. 只写入请求 Body：[reqWrite](./reqWrite)\n"
  },
  {
    "path": "docs/docs/rules/resAppend.md",
    "content": "# resAppend\n在现有响应内容体后面插入指定内容（仅对包含响应内容体的状态码有效，如 `200`/`500` 等）\n> ⚠️ 注意：204、304 等无响应内容体的请求不受影响\n\n## 规则语法\n``` txt\npattern resAppend://value [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 文本或二进制内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 内联方式\n``` txt\nwww.example.com/path resAppend://(Hello) file://(-test-)\n```\n请求 `https://www.example.com/path/to` 响应内容变成 `-test-Hello`。 \n\n#### 内嵌/Values方式\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path resAppend://{body.txt} file://(-test-)\n````\n请求 `https://www.example.com/path/to` 响应内容变成 `-test-Hello world.`。 \n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 resAppend:///User/xxx/test.txt\nwww.example.com/path2 resAppend://https://www.xxx.com/xxx/params.txt\n# 通过编辑临时文件\nwww.example.com/path3 resAppend://temp/blank.txt\n````\n\n## 关联协议\n1. 在响应内容后面追加内容：[resPrepend](./resPrepend)\n2. 在响应内容前面注入内容：[resBody](./resBody)\n3. 在请求内容后面追加内容：[reqPrepend](./reqPrepend)\n"
  },
  {
    "path": "docs/docs/rules/resBody.md",
    "content": "# resBody\n\n`resBody` 协议用于替换指定请求的响应内容体，仅对包含响应内容体的状态码（如 `200`、`500` 等）有效。\n\n> **生效条件**：仅对有响应内容体的状态码生效\n> ⚠️ 注意：`204`、`304` 等无响应内容体的请求不受影响\n\n## 规则语法\n\n`resBody` 支持多种方式指定要替换的响应内容：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要替换的内容，适用于简短的文本内容。\n\n```txt\npattern resBody://(value) [lineProps...] [filters...]\n```\n**示例：**\n```txt\nwww.example.com/api/data resBody://({\"status\":\"modified\"})\n```\n\n### 2. 内嵌值（使用代码块）\n当需要处理包含空格、换行符的复杂内容，或希望复用某段配置时，推荐使用此方式。\n\n````txt\npattern resBody://{custom-key.json} [lineProps...] [filters...]\n\n``` custom-key.json\n{\n  \"status\": \"success\",\n  \"data\": {\n    \"message\": \"This response was modified by resBody\"\n  }\n}\n```\n````\n\n### 3. 引用 Values 中的值\n当内容较大时，可以将其存储在 `Values` 配置区中。\n\n```txt\npattern resBody://{key-of-values.html} [lineProps...] [filters...]\n```\n**前提**：在 `Values` 中存在名为 `key-of-values.html` 的键，其值为要替换的内容。\n\n### 4. 从文件路径或远程 URL 加载\n从本地文件或远程 URL 加载要替换的响应内容。\n\n```txt\n# 从本地文件加载\npattern resBody:///User/xxx/test.txt\n\n# 从远程 URL 加载\npattern resBody://https://www.example.com/override-content.txt\n```\n\n### 5. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern resBody://temp/blank.txt\n```\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `resBody://temp/blank.txt`\n3. 在弹出的编辑对话框中输入替换内容\n4. 点击 `Save` 保存\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 要替换的响应内容，支持多种格式：<br>• 本地文件路径<br>• 远程 URL<br>• 内联、内嵌、Values 引用内容 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n## 配置示例\n\n### 基础示例\n```txt\n# 替换 API 响应内容\nwww.example.com/api/data resBody://({\"status\":\"custom\",\"data\":\"modifiedContent\"})\n```\n\n### 使用内嵌值\n````txt\n``` error-response\n{\n  \"error\": {\n    \"code\": 1001,\n    \"message\": \"Custom error message for testing\"\n  }\n}\n```\n\nwww.example.com/api resBody://{error-response} includeFilter://s:500\n````\n\n### 从文件加载\n```txt\n# 从本地文件加载替换内容\nwww.example.com/api/config resBody:///Users/username/mock/config.json\n\n# 从远程 URL 加载替换内容\nwww.example.com/api/data resBody://https://raw.githubusercontent.com/user/repo/main/mock-data.json\n```\n\n### 使用临时文件\n```txt\nwww.example.com/api/user resBody://temp/blank.json\n```\n> 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）并用鼠标点击 `resBody://temp/blank.json` 进行编辑\n\n### 配合过滤器使用\n```txt\n# 仅替换特定状态码的响应\nwww.example.com/api resBody://({\"error\":\"ServiceUnavailable\"}) includeFilter://s:503\n\n# 根据请求方法决定替换内容\nwww.example.com/api/users resBody://({\"method\":\"GEToverride\"}) includeFilter://m:GET\nwww.example.com/api/users resBody://({\"method\":\"POSToverride\"}) includeFilter://m:POST\n```\n\n## 高级用法\n\n### 动态内容替换\n使用正则表达式进行动态内容构造：\n```txt\n# 将原始响应中的时间戳替换为当前时间\nwww.example.com/api/time resBody://`({\"timestamp\":${now}})`\n```\n\n### 环境特定替换\n```txt\n# 开发环境：使用模拟数据\ndev-api.example.com resBody://{\"env\":\"development\",\"data\":\"mock\"}\n```\n\n### 错误场景测试\n```txt\n# 模拟各种错误响应\nwww.example.com/api resBody://{\"error\":\"RateLimitExceeded\"} includeFilter://s:429\nwww.example.com/api resBody://{\"error\":\"InternalServerError\"} includeFilter://s:500\nwww.example.com/api resBody://{\"error\":\"ServiceUnavailable\"} includeFilter://s:503\n```\n\n## 与 file 协议的区别\n\n`resBody` 协议与 [`file`](./file) 协议的主要区别在于请求处理流程：\n\n### 处理流程对比\n- **`resBody` 协议**：请求**先发送到后台服务器**获取原始响应，然后用指定内容**替换**原始响应体\n- **`file` 协议**：请求**不会发送到后台服务器**，直接返回指定的文件内容\n\n### 使用场景对比\n- **`resBody`**：适用于修改真实接口的返回内容，保持请求-响应的完整流程\n- **`file`**：适用于完全本地模拟，不依赖真实后端服务\n\n## 注意事项\n\n### 响应类型保持\n- `resBody` 替换的是响应体内容，不会自动修改 `Content-Type` 响应头\n- 如果需要修改响应类型，请配合使用 [`resType`](./resType) 协议\n\n## 常见问题\n\n### Q: resBody 规则没有生效\n**A:** 检查：\n1. 响应状态码是否有响应体（排除 204、304 等）\n2. 规则 pattern 是否正确匹配请求 URL\n3. 过滤器条件是否满足\n4. 是否有更高优先级的规则覆盖\n\n\n### Q: 能否仅替换特定类型的响应\n**A:** 可以，使用过滤器：\n```txt\n# 仅替换 JSON 响应\npattern resBody://content includeFilter://resH:content-type=json\n\n# 仅替换特定路径的响应\npattern resBody://content includeFilter://*/api/v1/\n```\n\n\n## 关联协议\n\n1. **在响应内容前面注入内容**：[resPrepend](./resPrepend)\n   - 在原始响应内容的前面插入内容\n\n2. **在响应内容后面追加内容**：[resAppend](./resAppend)\n   - 在原始响应内容的后面追加内容\n\n3. **替换请求内容**：[reqBody](./reqBody)\n   - 替换发送到服务器的请求体内容\n\n4. **直接返回本地文件**：[file](./file)\n   - 不请求服务器，直接返回本地文件内容\n\n5. **修改响应头**：[resHeaders](./resHeaders)\n   - 修改响应头部信息，如 `Content-Type`\n\n## 扩展阅读\n\n- [匹配模式文档](./pattern)：详细了解 URL 匹配规则\n- [操作指令文档](./operation)：了解内容加载的多种方式\n- [过滤器文档](./filters)：了解更多过滤器功能\n"
  },
  {
    "path": "docs/docs/rules/resCharset.md",
    "content": "# resCharset\n修改响应头 `content-type` 的 `charset` 部分，设置响应内容的编码。\n> `content-type` 结构：\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## 规则语法\n``` txt\npattern resCharset://encoding [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| encoding | 编码名称，如 `utf8` <br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 设置响应编码，响应头 `content-type` 空字段被改成 `; charset=utf8`\nwww.example.com/path resCharset://utf8\n\n# 设置响应编码，响应头 `content-type` 空字段被改成 `text/html; charset=utf8`\nwww.example.com/path resType://json resCharset://utf8\n```\n\n## 关联协议\n1. 直接修改响应头：[resHeaders://content-type=xxx](./reqHeaders)\n2. 修改响应内容编码：[resType://media-type](./resCharset)\n"
  },
  {
    "path": "docs/docs/rules/resCookies.md",
    "content": "# resCookies\n修改响应 `Cookie`。\n\n## 规则语法\n``` txt\npattern resCookies://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | Cookie 对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\nCookie 对象结构\n``` js\n{\n  \"key1\": \"value1\",\n  \"key2\": \"value2\",\n  \"keyN\": {\n          \"value\": \"value1\",\n          \"maxAge\": 60,\n          \"httpOnly\": true,\n          \"path\": \"/\",\n          \"secure\": true,\n          \"domain\": \".example.com\",\n          \"sameSite\": 'None',\n          \"Partitioned\": false\n      }\n}\n```\n\n## 配置示例\n#### 内联方式\n```` txt\nwww.example.com/path resCookies://k1=v1&k2=v2;path=/&k3=v3;path=/;secure;samesite=none\n````\n响应头新增两个响应 cookie：`k1=v1`/`k2=v2; path=/`/`k3=v3; path=/; secure; samesite=none`\n\n### 内嵌模式\n```` txt\n``` cookies.json\nkey1: value1\nkey2: value2\n```\n# 或\n``` cookies.json\n{\n  key1: 'value1',\n  key2: {\n    value: 'value2',\n    path: '/',\n    secure: true,\n    domain: 'example.com'\n  }\n}\n```\nwww.example.com/path resCookies://{cookies.json}\n````\n响应头新增两个响应 cookie：`key1=value1`/`key2=value2; path=/; secure; domain=example.com`\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 resCookies:///User/xxx/test.json\nwww.example.com/path2 resCookies://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 resCookies://temp/blank.json\n````\n\n## 全局替换\n如果想给所有（或部分）响应 cookie 添加 `SameSite=Nonoe; Secure`，可以用 [headerReplace](./headerReplace)\n> 假设每个响应 cookie 都有 `path=/;`\n```` txt\n``` test.json\nresH.set-cookie:path=/;: SameSite=None; Secure;\n```\nwww.example.com/path headerReplace://{test.json} resCookies://test=123;path=/;\n````\n\n## 关联协议\n1. 删除响应 cookie：[delete://resCookies.xxx](./delete)\n2. 删除所有响应头 cookie：[delete://resHeaders.set-cookie](./delete)\n3. 替换响应头 cookie：[headerReplace://resH.set-cookie:pattern=replacement](./headerReplace)\n"
  },
  {
    "path": "docs/docs/rules/resCors.md",
    "content": "# resCors\n设置响应阶段的跨域资源共享（[CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS))）头部信息，解决跨域请求问题。\n\n## 规则语法\n``` txt\npattern resCors://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | CORS 对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\nCORS 对象结构：\n``` text\norigin: *\nmethods: POST\nheaders: x-test\ncredentials: true\nmaxAge: 300000\n```\n\n对应响应头：\n\n``` txt\naccess-control-allow-origin: *\naccess-control-allow-methods: POST\naccess-control-allow-headers: x-test\naccess-control-allow-credentials: true\naccess-control-max-age: 300000\n```\n> 请求方法为 `OPTIONS` 时，`access-control-allow-headers` -> `access-control-expose-headers`\n\n## 配置示例\n#### 快捷模式\n1. 设置响应头 `access-control-allow-origin: *`（不支持带 cookie）\n    ``` txt\n    www.example.com/path resCors://*\n    ```\n2. 允许带 cookie\n    ``` txt\n    # `enable` 表示根据请求头 `origin` 设置 access-control-allow-origin: http://reqOrigin\n    # 及设置 access-control-allow-credentials: true\n    www.example.com/path2 resCors://enable\n    ```\n\n#### 详细配置模式\n```` txt\n``` cors.json\norigin: *\nmethods: POST\nheaders: x-test\ncredentials: true\nmaxAge: 300000\n```\nwww.example.com/path resCors://{cors.json}\n\n# 对 OPTIONS 请求不处理\nwww.example.com/path2 resCors://{cors.json} excludeFilter://options\n````\n设置响应头：\n``` txt\naccess-control-allow-origin: *\naccess-control-allow-methods: POST\naccess-control-allow-headers: x-test\naccess-control-allow-credentials: true\naccess-control-max-age: 300000\n```\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 resCors:///User/xxx/test.json\nwww.example.com/path2 resCors://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 resCors://temp/blank.json\n````\n\n\n## 关联协议\n1. 删除响应头字段：[delete://resHeaders.orogin](./delete)\n2. 设置请求 CROS：[resCors](./resCors)\n"
  },
  {
    "path": "docs/docs/rules/resDelay.md",
    "content": "# resDelay\n设置服务器处理完成后延迟响应的时间(单位：毫秒)。\n\n## 规则语法\n``` txt\npattern resDelay://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 时间数值（单位：毫秒）<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 后台返回后延迟 3000 毫秒（即 3 秒）响应到客户端\nwww.example.com/path resDelay://3000\n\n# 后台返回后延迟 5000 秒（即 5 秒）后 abort 请求\nwww.example.com/path2 resDelay://5000 enable://abortRes\n```\n"
  },
  {
    "path": "docs/docs/rules/resHeaders.md",
    "content": "# resHeaders\n动态修改响应头部信息，支持多种数据源和批量操作方式。\n\n## 规则语法\n``` txt\npattern resHeaders://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n``` txt\nwww.example.com/path resHeaders://x-proxy=Whistle\n```\n访问 `https://www.example.com/path/to` 新增响应头：\n``` txt\nx-proxy: Whistle\n```\n\n#### 设置多个响应头\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 resHeaders://{test.json}\n\n# 等价于：www.example.com/path2 resHeaders://x-test1=1&x-test2=&x-test3=abc\n````\n访问 `https://www.example.com/path2/to` 在 Whistle Network 或后台服务器可以看到新增响应头：\n``` txt\nx-test1: 1\nx-test2: \nx-test3: abc\n```\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 resHeaders:///User/xxx/test.json\nwww.example.com/path2 resHeaders://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 resHeaders://temp/blank.json\n````\n\n## 关联协议\n1. 更灵活的修改响应头的方式：[headerReplace](./headerReplace)\n2. 删除响应头字段：[delete://resHeaders.xxx](./delete)\n\n"
  },
  {
    "path": "docs/docs/rules/resMerge.md",
    "content": "# resMerge\n将指定数据对象智能合并到响应体中，适合修改部分参数而不影响其余内容，支持以下响应类型：\n- JSON（响应 `content-type` 包含 `json` 关键字）\n- JSONP（响应 `content-type`为空或包含 `html`/`javascript` 关键字）\n\n## 规则语法\n``` txt\npattern resMerge://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 内联方式\n``` txt\nwww.example.com/path resMerge://test=123 file://({\"name\":\"avenwu\"})\n```\n访问 `https://www.example.com/path/to` 浏览器收到的内容：\n``` js\n{\"name\":\"avenwu\",\"test\":\"123\"}\n```\n\n#### 内嵌方式\n```` txt\n``` resMerge.json\na.b.c: 123\nc\\.d\\.e: abc\n```\nwww.example.com/path resMerge://{resMerge.json} file://({\"name\":\"avenwu\"})\n````\n访问 `https://www.example.com/path/to` 浏览器收到的内容：\n``` js\n{\"name\":\"avenwu\",\"a\":{\"b\":{\"c\":123}},\"c.d.e\":\"abc\"}\n```\n\n#### 本地/远程资源\n```` txt\nwww.example.com/path1 resMerge:///User/xxx/test.json\nwww.example.com/path2 resMerge://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 resMerge://temp/blank.json\n````\n\n\n## 注意事项：响应大小限制\n为了保障处理性能，`resMerge` 默认对响应内容大小有限制。\n- **限制说明**：自动合并处理仅适用于响应体小于 2MB 的请求。超过此大小的响应将被跳过。\n- **如何覆盖**：如果您确认需要处理更大的响应（例如下载文件或处理大数据接口），可以通过在匹配的规则中添加以下指令来显式启用：\n\n``` txt\npattern enable://resMergeBigData\n\n# 或\nwww.example.com/path1 resMerge:///User/xxx/test.json lineProps://enableBigData\n```\n启用后，reqMerge 将尝试处理更大体积的响应，请注意这可能增加内存消耗和处理时间。\n\n## 关联协议\n1. 通过关键字或正则替换：[resReplace](./reqReplace)\n2. 修改请求内容对象：[reqMerge](./resMerge)\n\n"
  },
  {
    "path": "docs/docs/rules/resPrepend.md",
    "content": "# resPrepend\n\n`resPrepend` 协议用于在现有响应内容体的开头插入指定内容，仅对包含响应内容体的状态码（如 `200`、`500` 等）有效，通过 `resPrepend` 协议，你可以：\n- 在响应内容的开头添加自定义内容\n- 插入调试信息或时间戳\n- 添加脚本、样式表或其他资源的引用\n- 注入环境标记或版本信息\n- 保持原始响应内容完整，仅在开头添加额外内容\n\n> **生效条件**：仅对有响应内容体的状态码生效\n> \n> ⚠️ 注意：`204`、`304` 等无响应内容体的请求不受影响\n\n## 规则语法\n\n`resPrepend` 支持多种方式指定要插入的内容：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要插入的内容，适用于简短文本（不能包含空格和换行符）。\n\n```txt\npattern resPrepend://(value) [lineProps...] [filters...]\n```\n**示例：**\n```txt\nwww.example.com/page resPrepend://(<!--页面开始-->)\n```\n\n### 2. 内嵌值（使用代码块）\n当需要处理包含空格、换行符的复杂内容，或希望复用某段配置时，推荐使用此方式。\n\n````txt\npattern resPrepend://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n<!-- 调试信息 -->\n<script>\n  console.log('页面加载时间:', new Date().toISOString());\n</script>\n```\n````\n\n### 3. 引用 Values 中的值\n当内容较大时，可以将其存储在 `Values` 配置区中。\n\n```txt\npattern resPrepend://{key-of-values} [lineProps...] [filters...]\n```\n**前提**：在 `Values` 中存在名为 `key-of-values` 的键，其值为要插入的内容。\n\n### 4. 从文件路径或远程 URL 加载\n从本地文件或远程 URL 加载要插入的响应内容。\n\n```txt\n# 从本地文件加载\npattern resPrepend:///User/xxx/header.html\n\n# 从远程 URL 加载\npattern resPrepend://https://cdn.example.com/analytics-script.js\n```\n\n### 5. 从临时文件中加载\n当需要频繁编辑内容时，可以使用 Whistle 提供的临时文件功能。\n\n```txt\npattern resPrepend://temp/blank.txt\n```\n**操作步骤**：\n1. 在 Rules 编辑器中，按住 `Command`（Mac）/ `Ctrl`（Windows）\n2. 鼠标点击 `resPrepend://temp/blank.txt`\n3. 在弹出的编辑对话框中输入要插入的内容\n4. 点击 `Save` 保存\n\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **value** | 是 | 要插入的响应内容，支持多种格式：<br>• 本地文件路径<br>• 远程 URL<br>• 内联、内嵌、Values 引用内容 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n\n\n## 配置示例\n\n### 基础示例\n```txt\n# 在 HTML 页面开头添加注释\nwww.example.com/index.html resPrepend://(<!--页面开始时间:-->)\n\n# 在 JSON 响应前添加时间戳\napi.example.com/data.json resPrepend://({\"timestamp\":\"2024-01-01T00:00:00Z\"})\n```\n\n### 示例场景分析\n\n#### 场景 1：插入调试信息\n````txt\n``` debug-header\n<!-- \n  调试信息：\n  - URL: ${url}\n  - 时间: ${now}\n  - 用户代理: ${reqHeaders.user-agent}\n-->\n```\n\nwww.example.com resPrepend://`{debug-header}`\n````\n\n#### 场景 2：添加页面头部内容\n```` txt\n# 添加公共头部\n^www.example.com/*.html resPrepend://{custom-html}\n\n``` custom-html\n<div class=\"site-header\">网站头部</div>\n```\n````\n\n#### 场景 3：注入分析脚本\n```` txt\n# 添加 Google Analytics 脚本\nwww.example.com resPrepend://{google-analytics}\n``` google-analytics\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-XXXXX\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'UA-XXXXX');\n</script>\n```\n````\n\n### 配合其他协议使用\n```txt\n# 先添加头部内容，再添加尾部内容\nwww.example.com/page resPrepend://(<header>) resAppend://(</footer>)\n\n# 配合 file 协议使用\nwww.example.com/path resPrepend://(Hello) file://(-test-)\n```\n**响应结果：**\n```\nHello-test-\n```\n\n### 使用内嵌值\n````txt\n``` body.txt\nHello world.\n```\n\nwww.example.com/path resPrepend://{body.txt} file://(-test-)\n````\n**响应结果：**\n```\nHello world.-test-\n```\n\n### 从文件或远程 URL 加载\n```txt\n# 从本地文件加载\nwww.example.com/path1 resPrepend:///User/xxx/test.txt\n\n# 从远程 URL 加载\nwww.example.com/path2 resPrepend://https://www.xxx.com/xxx/params.txt\n\n# 使用临时文件\nwww.example.com/path3 resPrepend://temp/blank.txt\n```\n\n### 配合过滤器使用\n```` txt\n# 仅为开发环境添加调试信息\nwww.example.com resPrepend://(<!--开发环境-->) includeFilter://reqH:host=/dev\\./\n\n# 仅对 HTML 页面添加头部\nwww.example.com resPrepend://{header} resType://html\n\n``` header\n<div class=\"header\" />\n```\n\n# 根据响应状态码决定是否添加内容\nwww.example.com/api resPrepend://({\"debug\":true}) includeFilter://s:200\n````\n\n\n## 高级用法\n\n### 动态内容插入\n使用模板字符串实现动态内容：\n```` txt\n# 插入当前时间戳\nwww.example.com/api resPrepend://`({\"timestamp\":\"${now}\"})`\n\n# 插入请求信息\nwww.example.com/debug resPrepend://`{req-info}`\n\n``` req-info\n<!-- 请求方法: ${method}, 路径: ${path} -->\n```\n````\n\n### 组合多个插入内容\n```` txt\n# 插入多个内容片段\nwww.example.com/page resPrepend://{header1} resPrepend://{header2}\n\n``` header1\n<div id=\"header1\" />\n```\n``` header2\n<div id=\"header2\" />\n```\n# 最终结果: <div id=\"header2\" /><div id=\"header1\" />[原始内容]\n````\n\n### 条件插入\n`````txt\n# 仅在特定条件下插入内容\nwww.example.com/admin resPrepend://{log.js} includeFilter://reqH:cookie=/admin=true/\n\n```` log.js\n<script>console.log('管理员页面')</script>\n````\n`````\n\n\n## 常见问题\n\n### Q: resPrepend 规则没有生效\n**A:** 检查：\n1. 响应状态码是否有响应体（排除 204、304 等）\n2. 规则 pattern 是否正确匹配请求 URL\n3. 过滤器条件是否满足\n4. 是否有其他规则干扰\n\n### Q: 如何确保插入内容在特定位置\n**A:** \n- `resPrepend` 始终在响应内容的最前面插入\n- 如果需要在其他位置插入，请考虑使用 `resReplace` 配合正则表达式替换\n\n### Q: 能否删除原始响应的部分内容\n**A:** 不能。`resPrepend` 只负责添加内容，不删除或修改原始内容。如需删除内容，请使用 `resBody` 协议。\n\n## 与 resBody 的区别\n\n`resPrepend` 协议与 [`resBody`](./resBody) 协议的主要区别在于处理方式：\n- **`resPrepend`**：在原始响应内容**前面**插入指定内容，保留原始内容\n- **`resBody`**：**替换**整个响应内容，不保留原始内容\n\n## 关联协议\n\n1. **替换响应内容**：[resBody](./resBody)\n   - 完全替换响应内容，不保留原始内容\n\n2. **在响应内容后面追加内容**：[resAppend](./resAppend)\n   - 在响应内容的末尾添加内容\n\n3. **替换请求内容**：[reqBody](./reqBody)\n   - 替换发送到服务器的请求体内容\n\n4. **在请求内容前面插入内容**：[reqPrepend](./reqPrepend)\n   - 在请求体内容的前面插入内容\n\n5. **在请求内容后面追加内容**：[reqAppend](./reqAppend)\n   - 在请求体内容的末尾添加内容\n\n## 扩展阅读\n\n- [匹配模式文档](./pattern)：详细了解 URL 匹配规则\n- [操作指令文档](./operation)：了解内容加载的多种方式\n- [过滤器文档](./filters)：了解更多过滤器功能\n"
  },
  {
    "path": "docs/docs/rules/resReplace.md",
    "content": "# resReplace\n使用类似 JavaScript `String.replace()` 的方法替换响应体内容（仅对包含响应内容体的请求有效，如 `200`、`500` 等），支持多种文本格式：\n- JSON (`application/json`)\n- XML (`application/xml`)\n- HTML (`text/html`)\n- 纯文本 (`text/xxx`)\n\n## 规则语法\n``` txt\npattern resReplace://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 替换配置对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容  | [操作指令文档](./operation) |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 内联方式\n```` txt\nwww.example.com/path file://(00user-11test-22user-33test) resReplace://user=abc&/\\d+/g=number\n````\n请求 `https://www.example.com/path/to` 浏览器收到的内容：\n``` txt\nnumberabc-numbertest-numberabc-numbertest\n```\n\n### 内嵌模式\n```` txt\n``` resReplace.json\nuser: name\n/\\d+/g: num\n```\n# 或（注意转义符）\n``` resReplace.json\n{\n  'user': 'name',\n  '/\\\\d+/g': 'num'\n}\n```\nwww.example.com/path file://(00user-11test-22user-33test) resReplace://{resReplace.json}\n````\n请求 `https://www.example.com/path/to` 浏览器收到的内容：\n``` txt\nnumname-numtest-numname-numtest\n```\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 resReplace:///User/xxx/test.json\nwww.example.com/path2 resReplace://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 resReplace://temp/blank.json\n````\n\n## 关联协议\n1. 对象合并：[resMerge](./resMerge)\n2. 完全替换：[resBody](./resBody)\n"
  },
  {
    "path": "docs/docs/rules/resRules.md",
    "content": "# resRules\n为匹配的请求在响应阶段批量设置多个规则，实现复杂场景下的请求处理需求。\n\n## 规则语法\n``` txt\npattern resRules://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 规则内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n``` test.txt\n* file://(<div>hello<div>)\n* resType://text\n```\n\n``` test2.txt\n* resAppend://(test)\n```\n\nwww.example.com/path resRules://{test.txt} resRules://{test2.txt}\n````\n访问 `https://www.example.com/path/to` 不执行 `* file://(<div>hello<div>)`（因为是在响应阶段执行 resRules 里面的规则）。\n\n## 关联协议\n1. 请求阶段脚本规则：[reqScript](./reqScript)\n2. 响应阶段批量规则：[reqRules](./resRules)\n3. 响应阶段脚本规则：[resScript](./resScript)\n4. 更复杂的定制需求：[插件开发](../extensions/dev)\n"
  },
  {
    "path": "docs/docs/rules/resScript.md",
    "content": "# resScript\n在响应阶段通过 JavaScript 脚本动态生成规则，实现复杂请求处理逻辑。脚本可以访问请求上下文信息，并动态生成匹配规则。\n\n## 规则语法\n``` txt\npattern resScript://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 生成规则的 JS 脚本，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n``` test.js\nif (method === 'GET') {\n    rules.push('* resType://text');\n    rules.push('* file://(<div>GET-Request</div>)');\n} else {\n    rules.push('* statusCode://403');\n}\n```\nwww.example.com/path resScript://{test.js}\n````\n访问 `https://www.example.com/path/to` 不执行 `* file://(<div>hello<div>)`（因为是在响应阶段执行 resScript 生成的规则）。\n\n#### 可用全局变量\n\n| 变量/方法          | 描述                                                                 |\n|--------------------|---------------------------------------------------------------------|\n| `url`             | 完整请求URL                                                         |\n| `method`          | 请求方法(GET/POST等)                                                |\n| `ip`/`clientIp`   | 客户端IP地址                                                       |\n| `headers`         | 请求头对象                                                          |\n| `body`            | 请求内容(最大16KB)                                                  |\n| `rules`           | 规则数组，通过push添加新规则                                        |\n| `values`          | 临时值存储对象                                                      |\n| `render(tpl,data)`| 微型模板渲染函数                                                    |\n| `getValue(key)`   | 获取Values中的值                                                    |\n| `parseUrl`        | 同Node.js的`url.parse`                                              |\n| `parseQuery`      | 同Node.js的`querystring.parse`                                      |\n\n\n## 关联协议\n1. 请求阶段脚本规则：[reqScript](./reqScript)\n2. 请求阶段批量规则：[resRules](./resScript)\n3. 响应阶段批量规则：[resRules](./resRules)\n4. 更复杂的定制需求：[插件开发](../extensions/dev)\n"
  },
  {
    "path": "docs/docs/rules/resSpeed.md",
    "content": "# resSpeed\n设置响应 Body 的传输速度(单位：kb/s，千比特/每秒).\n\n## 规则语法\n``` txt\npattern resSpeed://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 传输速度（单位：kb/s，千比特/每秒）<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n## 配置示例\n``` txt\n# 限制响应内容的传输速度：3kb/s\nwww.example.com/path resSpeed://3\n```\n该规则对请求方法为 `GET`、`HEAD`、`CONNECT`（TUNNEL 请求）、`UPGRADE`（WebSocket 请求）等无请求 Body 的请求无效\n"
  },
  {
    "path": "docs/docs/rules/resType.md",
    "content": "# resType\n修改响应头 `content-type` 的 `media-type` 和 `charset` 部分。\n> `content-type` 结构：\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## 规则语法\n``` txt\npattern resType://type[;charset] [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| type[;charset] | `type` 响应类型，`charset` 编码 <br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n`resType` 主要用来修改响应类型的 `media-type` 部分 ，`charset` 部分可选，如果没有设置 `charset` 部分则会保留响应头原始的 `charset` 部分（如果存在）。\n\n## 配置示例\n\n#### 快捷命令（不包含 `/` 的字符串）\n> 根据 [mime](https://github.com/broofa/mime).lookup(type) 自动转换\n``` txt\n# 不带编码响应头的 `content-type` 变成 `text/html`\nwww.example.com/path resType://html\n\n# 带编码响应头的 `content-type` 变成 `application/json;charset=utf8`\nwww.example.com/path resType://json;charset=utf8\n```\n\n#### 完整类型\n``` txt\n# 响应头的 `content-type` 变成 `text/plain`\nwww.example.com/path resType://text/plain\n\n# 响应头的 `content-type` 变成 `text/plain;charset=utf8`\nwww.example.com/path resType://text/plain;charset=utf8\n```\n\n## 关联协议\n1. 直接修改响应头：[resHeaders://content-type=xxx](./reqHeaders)\n2. 修改响应内容编码：[resCharset://encoding](./reqCharset)\n"
  },
  {
    "path": "docs/docs/rules/resWrite.md",
    "content": "# resWrite\n将响应内容体保存到指定目录或文件中，适用于需要记录响应数据的场景：\n- 自动根据请求URL生成文件路径\n- 采用安全写入策略（可以采用 [enable://forceReqWrite](./enable) 强制覆盖）\n- 仅对包含响应内容体的请求有效（POST/PUT/PATCH等）\n- GET/HEAD 等无响应内容体请求会自动跳过\n- 保存失败时自动跳过\n\n## 规则语法\n``` txt\npattern resWrite://fileOrDirPath [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| fileOrDirPath   | 存储数据的目录或文件路径 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n```txt\nwproxy.org/docs resWrite:///User/xxx/test/\n```\n##### 路径解析规则：\n1. **当访问具体文件时**  \n   `https://wproxy.org/docs/test.html`  \n   → 保存到：`/User/xxx/test/test.html`\n\n2. **当访问目录路径时**  \n   `https://wproxy.org/docs/`  \n   → 保存到：`/User/xxx/test/index.html`（目标路径为 `/User/xxx/test/`，结尾为 `/` 或 `\\` 自动追加 `index.html`）\n\n#### 目录显式配置\n```txt\nwproxy.org/docs/ resWrite:///User/xxx/test/\n```\n##### 路径解析差异：\n1. **访问子路径时**  \n   `https://wproxy.org/docs/test.html`  \n   → 仍保存到：`/User/xxx/test/test.html`\n\n2. **访问配置目录时**  \n   `https://wproxy.org/docs/`  \n   → 直接保存到：`/User/xxx/test/`（作为整体文件）\n\n> 💡 关键区别：  \n> - 规则路径是否以 `/` 或 `\\`结尾，决定了目录请求的保存方式  \n> - 非目录路径（无结尾 `/` 或 `\\`）会直接保存为指定文件\n> - 目录路径（有结尾 `/` 或 `\\`）会自动补全 `index.html`\n\n#### 指定文件\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ resWrite:///User/xxx/test/index.html\n```\n> 通过正则匹配限定请求 URL\n\n## 关联协议\n1. 启用强制写入：[enable://forceReqWrite](./enable)\n2. 写入所有响应内容：[resWriteRaw](./resWriteRaw)\n"
  },
  {
    "path": "docs/docs/rules/resWriteRaw.md",
    "content": "# resWriteRaw\n将响应的完整内容（包括协议、状态码、状态信息、响应头、内容）保存到指定目录或文件中，适用于需要记录响应数据的场景：\n- 自动根据请求URL生成文件路径\n- 采用安全写入策略（可以采用 [enable://forceReqWrite](./enable) 强制覆盖）\n- 仅对包含响应内容体的请求有效（POST/PUT/PATCH等）\n- GET/HEAD 等无响应内容体请求会自动跳过\n- 保存失败时自动跳过\n\n## 规则语法\n``` txt\npattern resWriteRaw://fileOrDirPath [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| fileOrDirPath   | 存储数据的目录或文件路径 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n\n#### 基础配置\n```txt\nwproxy.org/docs resWriteRaw:///User/xxx/test/\n```\n##### 路径解析规则：\n1. **当访问具体文件时**  \n   `https://wproxy.org/docs/test.html`  \n   → 保存到：`/User/xxx/test/test.html`\n\n2. **当访问目录路径时**  \n   `https://wproxy.org/docs/`  \n   → 保存到：`/User/xxx/test/index.html`（目标路径为 `/User/xxx/test/`，结尾为 `/` 或 `\\` 自动追加 `index.html`）\n\n#### 目录显式配置\n```txt\nwproxy.org/docs/ resWriteRaw:///User/xxx/test/\n```\n##### 路径解析差异：\n1. **访问子路径时**  \n   `https://wproxy.org/docs/test.html`  \n   → 仍保存到：`/User/xxx/test/test.html`\n\n2. **访问配置目录时**  \n   `https://wproxy.org/docs/`  \n   → 直接保存到：`/User/xxx/test/`（作为整体文件）\n\n> 💡 关键区别：  \n> - 规则路径是否以 `/` 或 `\\`结尾，决定了目录请求的保存方式  \n> - 非目录路径（无结尾 `/` 或 `\\`）会直接保存为指定文件\n> - 目录路径（有结尾 `/` 或 `\\`）会自动补全 `index.html`\n\n#### 指定文件\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ resWriteRaw:///User/xxx/test/index.html\n```\n> 通过正则匹配限定请求 URL\n\n## 关联协议\n1. 启用强制写入：[enable://forceReqWrite](./enable)\n2. 只写入响应 Body：[resWrite](./resWrite)\n"
  },
  {
    "path": "docs/docs/rules/responseFor.md",
    "content": "# responseFor\n通过设置响应头 `x-whistle-response-for` 字段，自定义在 Whistle Network 面板上显示的 `ServerIP`。\n> 等价于：[resHeaders://x-whistle-response-for=xxx](./resHeaders)\n\n# 规则语法\n``` txt\npattern responseFor://ip [filters...]\n```\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| ip   | 自定义客户端 IP<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.example.com/path  responseFor://1.1.1.1,2.2.2.2\n```\n<img src=\"/img/response-for.png\" width=\"800\" />\n\n## 关联协议\n1. 自定义响应头：[resHeaders://x-whistle-response-for=xxx](./resHeaders)\n"
  },
  {
    "path": "docs/docs/rules/rule.md",
    "content": "# 规则语法\n\nWhistle 通过简洁的规则配置来修改请求和响应，每条规则都遵循相同的基础语法结构。\n\n## 语法结构\n\n```txt\npattern operation [lineProps...] [filters...]\n```\n\n每条规则由以下四部分组成：\n\n| 组成部分 | 是否必填 | 描述 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 匹配请求 URL 的表达式，详细文档：[pattern](./pattern) |\n| **operation** | 是 | 操作指令，格式为 `protocol://value`，详细文档：[operation](./operation) |\n| **lineProps** | 否 | 附加配置，仅对当前规则生效，详细文档：[lineProps](./lineProps) |\n| **filters** | 否 | 过滤条件，用于精确控制规则生效场景，详细文档：[filters](./filters) |\n\n## 规则高级配置\n\n### 1. 组合配置\n单条规则可以包含多个操作指令，实现复杂的功能组合。\n\n**语法**：\n```txt\npattern operation1 operation2 ... [includeFilter://pattern1 ... excludeFilter://patternN ...]\n```\n\n**示例**：\n```txt\nwww.example.com/* file:///static-files cache://3600 resCors://*\n```\n\n**说明**：\n- 多个操作指令按顺序执行\n- 支持任意数量的操作指令组合\n- 过滤器条件对整条规则生效\n\n### 2. 位置调换\n当 `operation` 和第一个 `pattern` 不同时为 URL 或域名格式时，可以调换位置。\n\n**语法**：\n```txt\noperation pattern1 pattern2 ... [includeFilter://pattern1 ... excludeFilter://patternN ...]\n```\n\n**示例**：\n```txt\n# 标准写法\nwww.example.com proxy://127.0.0.1:8080\n\n# 位置调换写法\nproxy://127.0.0.1:8080 www.example.com api.example.com\n```\n\n> **限制条件**：`operation` 和第一个 `pattern` 不能同时为以下格式：\n> - `https://test.com/path`\n> - `//test.com/path`\n> - `test.com/path`\n> - `test.com`\n\n**适用场景**：\n- 为多个域名应用相同操作时更简洁\n- 批量配置代理、重定向等规则\n\n### 3. 换行配置\n使用代码块实现多行配置，提高复杂规则的可读性。\n\n**语法**：\n````txt\nline`\noperation\npattern1\npattern2\n...\n[includeFilter://pattern1\n...\nexcludeFilter://patternN \n...]\n`\n````\n\n**示例**：\n````txt\nline`\nproxy://127.0.0.1:8080\nwww.example.com\napi.example.com\nstatic.example.com\nincludeFilter://m:GET\nexcludeFilter:///admin/\n`\n````\n\n**特性**：\n- Whistle 会自动将代码块内的换行符替换为空格\n- 保持代码格式整洁，便于阅读和维护\n- 适合包含多个匹配模式和复杂过滤条件的场景\n\n**等价转换**：\n上述换行配置等价于：\n```txt\nproxy://127.0.0.1:8080 www.example.com api.example.com static.example.com includeFilter://m:GET excludeFilter:///admin/\n```\n\n## 注意事项\n\n### 1. 规则优先级\n- 规则按从上到下的顺序执行\n- 后面规则可能覆盖前面规则的效果\n- 使用 `lineProps://important` 提升重要规则优先级\n\n### 2. 调试技巧\n1. **逐步验证**：从简单规则开始，逐步添加复杂条件\n2. **日志查看**：使用 Whistle Network 界面的 Overview 面板查看规则匹配情况\n3. **浏览器调试**：配合浏览器开发者工具检查实际效果\n4. **临时禁用**：使用 `#` 注释暂时禁用规则进行测试\n"
  },
  {
    "path": "docs/docs/rules/skip.md",
    "content": "# skip\n跳过指定 `pattern` 或 `operation` 的规则，继续匹配后面的规则。\n\n## 语法规则\n``` txt\npattern skip://pattern=patternString skip://operation=operationString [filters...]\n```\n> 可同时配置多个 `skip`\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 需要忽略的规则表达式：<br/>• pattern=patternString<br/>• operation=operationString<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.example.com/path file:///User/xxx/index1.html\nwww.example.com/path file:///User/xxx/index2.html\n\nwww.example.com/path2/test file:///User/xxx/test1.html\nwww.example.com/path2 file://</User/xxx/test2.html>\n\nwww.example.com/path skip://operation=file:///User/xxx/index1.html\nwww.example.com/path2/test skip://pattern=www.example.com/path2/test\n```\n\n- 访问 `https://www.example.com/path` 返回 `/User/xxx/index2.html` 的内容\n    > 没有 `www.example.com/path skip://operation=file:///User/xxx/index1.html` 规则将返回 `/User/xxx/index1.html` 的内容\n- 访问 `https://www.example.com/path2/test` 返回 `/User/xxx/test2.html` 的内容\n    > 没有 `www.example.com/path2/test skip://pattern=www.example.com/path2/test` 规则将返回 `/User/xxx/test1.html` 的内容\n\n类似协议：[ignore](./ignore)\n"
  },
  {
    "path": "docs/docs/rules/sniCallback.md",
    "content": "# sniCallback\n通过插件机制动态定制 HTTPS 请求的 TLS 证书。\n\n## 规则语法\n``` txt\npattern sniCallback://plugin-name(sniValue) [filters...]\n```\n> `(sniValue)` 可选，在插件 Hook 可以通过 `req.originalReq.sniValue` 获取\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| plugin-name(sniValue)   | 插件名称 + 可选参数 |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwwww.example.com sniCallback://test\nwwww.example.com sniCallback://test-sni(abc)\n```\n\n在插件中通过以下属性访问 SNI 信息：\n``` js\nexports.auth = (req) => {\n  const { sniValue, servername } = req.originalReq; // 获取配置参数及 servername\n\n  return {\n    cert: /* 证书内容 */,\n    key:  /* 私钥内容 */\n  }\n};\n```\n\n具体用法参考：[插件开发文档](../extensions/dev.md#snicallback)\n"
  },
  {
    "path": "docs/docs/rules/socks.md",
    "content": "# socks\n`socks` 指令用于将匹配的请求通过指定的 SOCKS5 代理服务器转发。\n\n## 规则语法\n``` txt\npattern socks://ipOrDomain[:port] [filters...]\n```\n> `port` 可选，不填则使用默认端口 `443`\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | IP + 可选端口 或域名 + 可选端口<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\n# 将请求代理到 SOCKS5 代理: `127.0.0.1:443`\nwww.example.com/path socks://127.0.0.1 # 默认端口 443\n\n# 将当前域名的所有请求代理到 SOCKS5 代理: `127.0.0.1:8080`\nwww.example.com socks://127.0.0.1:8080\n\n# 也可以用域名\nwww.example.com/path socks://test.proxy.com # 默认端口 443\nwww.example.com socks://test.proxy.com:8080\n```\n\n## 高级用法\n默认情况下，上游代理会自行解析请求的域名。但某些场景下，你可能希望强制代理直接访问指定的目标 IP（跳过 DNS 解析），例如：\n- 绕过 DNS 污染\n- 直接访问特定后端 IP\n- 测试不同环境的服务\n``` txt\n# 通过查询参数\nwww.example.com socks://127.0.0.1:8080?host=1.1.1.1\nwww.example.com socks://127.0.0.1:8080?host=1.1.1.1:8080\n\n# 通过指令启用\nwww.example.com socks://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com socks://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n```\n> `1.1.1.1` 等价于 `host://1.1.1.1`\n\n## 注意事项\n`socks` 协议仅对经过规则替换后生成的 `Final URL`（可在 Overview 面板中查看）生效。若 `Final URL` 为空，则会作用于原始请求的 URL。\n\n例如以下规则：\n\n``` txt\nwww.example.com/api www.example.com socks://127.0.0.1:1234\n```\n\n当请求 `https://www.example.com/api/path` 时，Whistle 会先将其转换为 `https://www.example.com/path`（该结果即为 `Final URL`）。此时希望将该请求代理到 `127.0.0.1:1234`，但由于 `socks` 规则仅匹配替换前的原始域名 `www.example.com/api`，而转换后的 `Final URL` 已经是 `www.example.com/path`，因此无法命中这条 `socks` 规则。\n\n若需要对替换后的请求也生效，可拆解为两条规则：\n\n``` txt\nwww.example.com/api www.example.com\nwww.example.com socks://127.0.0.1:1234\n```\n\n这样，原始请求先被第一条规则重写，生成新的 `Final URL`，然后再被第二条 `socks` 规则匹配，最终代理到 `127.0.0.1:1234`。\n"
  },
  {
    "path": "docs/docs/rules/statusCode.md",
    "content": "# statusCode\n\n`statusCode` 协议用于立即中断请求并返回指定的 HTTP 状态码，不会将请求转发到后端服务器。通过 `statusCode` 协议，你可以：\n- **快速模拟特定 HTTP 状态码**：无需真实服务器即可测试各种状态码响应\n- **拦截并中断请求**：在请求到达真实服务器前直接返回状态码\n- **测试错误场景**：模拟服务器错误、重定向、认证失败等场景\n- **控制请求流程**：根据条件决定是否允许请求继续\n\n## 规则语法\n\n`statusCode` 协议支持多种方式配置：\n\n### 1. 内联值（直接指定）\n直接在规则中写明要返回的状态码。\n\n```txt\npattern statusCode://code [lineProps...] [filters...]\n```\n**示例：**\n```txt\nwww.example.com/api/old-endpoint statusCode://410\n```\n\n### 2. 内嵌值（使用代码块）\n当需要根据条件返回不同状态码，或希望复用配置时可使用此方式。\n\n````txt\npattern statusCode://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n404\n```\n````\n\n### 3. 引用 Values 中的值\n引用在界面 `Values`（中央配置区）中预先定义好的状态码。\n\n```txt\npattern statusCode://{key-of-values} [lineProps...] [filters...]\n```\n**前提**：在 `Values` 中存在名为 `key-of-values` 的键，其值为状态码。\n\n### 4. 文件/远程 URL 加载\n**当前不支持** 从本地文件或远程 URL 动态加载内容。\n\n## 参数详解\n\n| 参数 | 是否必填 | 描述与示例 |\n| :--- | :--- | :--- |\n| **pattern** | 是 | 用于匹配请求 URL 的表达式。<br>• 支持域名、路径、通配符、正则表达式。<br>• 详见 [匹配模式文档](./pattern)。 |\n| **code** | 是 | HTTP 响应状态码，如：<br>• `200` OK<br>• `301` 永久重定向<br>• `302` 临时重定向<br>• `404` 未找到<br>• `500` 服务器错误<br>• 支持所有标准 HTTP 状态码 |\n| **lineProps** | 否 | 为规则设置附加属性。<br>• 例如：`lineProps://important` 可提升此规则的优先级。<br>• 详见 [lineProps 文档](./lineProps)。 |\n| **filters** | 否 | 可选的过滤条件，用于精确控制规则生效的场景。<br>• 可匹配请求的 URL、方法、头部、体内容。<br>• 可匹配响应的状态码、头部。<br>• 详见 [过滤器文档](./filters)。 |\n\n\n## 配置示例\n\n### 基础示例\n```txt\n# 返回 404 状态码（页面未找到）\nwww.example.com/deleted-page statusCode://404\n\n# 返回 500 状态码（服务器内部错误）\nwww.example.com/api/error statusCode://500\n\n# 返回 302 重定向\nwww.example.com/old-url statusCode://302\n```\n\n### 模拟认证失败\n```txt\n# 访问时需要认证，浏览器会弹出登录框\nwww.example.com/secure-area statusCode://401\n\n# 禁止弹出登录框，直接返回 401\nwww.example.com/secure-area statusCode://401 disable://userLogin\n\n# 或使用 lineProps 达到同样效果\nwww.example.com/secure-area statusCode://401 lineProps://disableUserLogin\n```\n\n### 配合自定义响应内容\n```txt\n# 返回 404 状态码并自定义响应内容\nwww.example.com/missing-page statusCode://404 resBody://(<h1>页面不存在</h1>)\n```\n\n### 配合过滤器使用\n```txt\n# 仅对特定请求方法返回 405（方法不允许）\nwww.example.com/api/resource statusCode://405 includeFilter://m:PUT\n\n# 根据请求路径匹配返回不同状态码\n/^https?://www\\.example\\.com/user/\\d+/profile/ statusCode://403\n\n# 基于请求头条件返回状态码\nwww.example.com/api statusCode://429 includeFilter://reqH:user-agent=/bot/i\n```\n\n### 使用内嵌值\n````txt\n``` maintenance-status\n503\n```\n\nwww.example.com statusCode://{maintenance-status}\n````\n\n### 环境特定配置\n````txt\n``` testing-403\n403\n```\n\n# 仅在测试环境返回 403\ntest.example.com/api/admin statusCode://{testing-403}\n````\n\n## 常见状态码场景\n\n| 状态码 | 场景描述 | 典型使用 |\n| :--- | :--- | :--- |\n| **200** | 成功响应 | 测试正常流程 |\n| **301/302** | 重定向 | 测试 URL 跳转逻辑 |\n| **400** | 错误请求 | 测试客户端错误处理 |\n| **401** | 未认证 | 测试认证流程 |\n| **403** | 禁止访问 | 测试权限控制 |\n| **404** | 未找到 | 测试资源不存在处理 |\n| **429** | 请求过多 | 测试限流逻辑 |\n| **500** | 服务器错误 | 测试服务端异常处理 |\n| **503** | 服务不可用 | 测试维护页面 |\n\n## 注意事项\n\n\n### 1. 响应内容\n- 默认情况下，`statusCode` 返回的响应内容为空\n- 可通过配合 [`resBody`](./resBody) 协议自定义响应内容\n\n### 2. 认证弹窗控制\n- 返回 `401` 状态码时，默认会触发浏览器认证弹窗\n- 可通过以下方式禁用弹窗：\n  - 添加 `disable://userLogin`\n  - 添加 `lineProps://disableUserLogin`\n  - 使用 [`enable`](./enable) 协议的反向配置\n\n### 3. 重定向处理\n- 返回 `301`、`302` 等重定向状态码时，需配合 [`location`](./resHeaders) 响应头指定重定向地址：\n  ```txt\n  www.example.com/old statusCode://302 resHeaders://location=\"https://www.example.com/new\"\n  ```\n\n## 高级用法\n\n### 动态状态码\n```txt\n# 基于时间返回不同状态码（维护窗口）\nwww.example.com/api statusCode://503 includeFilter://t:>=00:00&t:<=06:00\n```\n\n### 组合使用其他协议\n```txt\n# 返回 403 并设置自定义错误页面\nwww.example.com/blocked statusCode://403 resBody://<h1>访问被拒绝</h1> resHeaders://content-type=\"text/html; charset=utf-8\"\n\n# 返回 503 并设置重试时间\nwww.example.com/down statusCode://503 resHeaders://retry-after=\"3600\"\n```\n\n### 测试客户端容错\n```txt\n# 随机返回不同错误状态码，测试客户端容错能力\nwww.example.com/api/unstable statusCode://500 includeFilter://chance:0.5\nwww.example.com/api/unstable statusCode://429 includeFilter://chance:0.3\nwww.example.com/api/unstable statusCode://200 includeFilter://chance:0.2\n```\n\n## 与 replaceStatus 的区别\n- **`statusCode`**：在请求阶段生效，**不会转发到后端服务器**\n- **`replaceStatus`**：在响应阶段生效，**会先请求后端服务器**，然后替换返回的状态码\n\n## 故障排除\n\n### Q: 状态码规则没有生效\n**A:** 检查：\n1. 规则 pattern 是否正确匹配请求 URL\n2. 是否有其他更高优先级的规则覆盖\n3. 过滤器条件是否满足\n\n### Q: 浏览器弹出认证窗口\n**A:** 对于 401 状态码：\n1. 检查是否添加了 `disable://userLogin` 或 `lineProps://disableUserLogin`\n2. 确认规则顺序是否正确\n\n### Q: 客户端显示空白页面\n**A:** 检查：\n1. 是否设置了自定义响应内容\n2. 响应头的 `Content-Type` 是否正确\n3. 响应内容编码是否匹配\n\n### Q: 重定向未生效\n**A:** 检查：\n1. 是否设置了 `Location` 响应头\n2. 重定向地址格式是否正确\n3. 浏览器是否遵循重定向规则\n\n## 关联协议\n\n1. **替换响应状态码**：[replaceStatus](./replaceStatus)\n   - 请求先到达服务器，然后替换返回的状态码\n\n2. **禁止认证弹窗**：[disable](./disable) 或 [lineProps](./lineProps)\n   - 禁用 401 状态码触发的浏览器登录弹窗\n\n3. **设置响应内容**：[resBody](./resBody)\n   - 为状态码响应添加自定义内容\n\n4. **设置响应头**：[resHeaders](./resHeaders)\n   - 为重定向等场景设置必要的响应头\n\n## 扩展阅读\n\n- [HTTP 状态码 MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status)：了解所有 HTTP 状态码的含义\n- [匹配模式文档](./pattern)：详细了解 URL 匹配规则\n- [过滤器文档](./filters)：了解更多过滤器功能\n"
  },
  {
    "path": "docs/docs/rules/style.md",
    "content": "# style\n\n自定义 Network 列表中请求行的显示样式，包括字体颜色、样式、背景色等。\n\n## 规则语法\n``` txt\npattern style://color=@value&fontStyle=value&bgColor=@value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 支持的样式属性：<br/>• `color` - 字体颜色（`@hex` 或 CSS 颜色名，如 `red`）<br/>• `fontStyle` - 字体样式（如 `italic`、`bold`）详见 [font-style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style)<br/>• `bgColor`- 背景颜色（@hex 或 CSS 颜色名，如 `red`） |    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.test.com style://color=@fff&fontStyle=italic&bgColor=red\n```\n<img src=\"/img/style.png\" width=\"1000\" />\n"
  },
  {
    "path": "docs/docs/rules/tpl.md",
    "content": "# tpl\ntpl 是基于 [file](./file) 功能的增强版本，提供了简单的模板引擎功能。\n\n## 规则语法\n``` txt\npattern tpl://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作内容，支持以下类型：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 模板规则\n1. 取值方式：`{key}` 或  `{{key}}`\n2.  `key`：为请求参数里面的属性\n\n## Mock JSONP\n1. 本地文件 `/User/xxx/test.js`\n    ``` js\n    {callback}({\n      ec: 0\n    });\n    ```\n2. 配置规则\n    ``` txt\n    https://www.example.com/test/tpl tpl:///User/xxx/test.js\n    \n    # 支持远程 URL\n    # pattern tpl://https://example.com/template\n    ```\n3. 请求 `https://www.example.com/test/tpl?callback=test` 返回结果：\n    ``` txt\n    test({\n      ec: 0\n    });\n    ```\n\n其它功能参考：[file](./file)\n"
  },
  {
    "path": "docs/docs/rules/trailers.md",
    "content": "# trailers\n修改或新增使用 `Transfer-Encoding: chunked` 分块传输编码的响应尾部信息（`Trailers`）。Trailers 是在分块传输的响应主体之后发送的额外 HTTP 头部字段。\n> HTTP Tailers 功能：https://http.dev/trailer\n\n## 规则语法\n``` txt\npattern trailers://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n#### 基础配置\n``` txt\n# 设置请求头 `x-proxy: Whistle`\nwww.example.com/path trailers://x-proxy=Whistle\n```\n\n#### 设置多个请求头\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 trailers://{test.json}\n\n# 等价于：www.example.com/path2 trailers://x-test1=1&x-test2=&x-test3=abc\n````\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 trailers:///User/xxx/test.json\nwww.example.com/path2 trailers://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 trailers://temp/blank.json\n````\n\n## 关联协议\n1. 删除请求头字段：[delete://trailers.xxx](./delete)\n\n\n\n"
  },
  {
    "path": "docs/docs/rules/tunnel.md",
    "content": "# tunnel\n将隧道代理请求转发新的服务器。\n> 只支持隧道代理 `tunnel://domain:port`，不支持转换 WebSocket 请求和普通 HTTP/HTTPS\n\n## 规则语法\n``` txt\npattern tunnel://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配 WebSocket 请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]`<br/>`port` 默认为 `443`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## TUNNEL 转换\n``` txt\ntunnel://www.example.com tunnel://www.test.com:5678\ntunnel://www.example2.com:8080 tunnel://www.test.com\n```\n | 原始请求                                  | 转换结果                                    |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443`              | `tunnel://www.test.com:5678`             |\n| `tunnel://www.example2.com:8080`   | `tunnel://www.test.com:443` |\n\nTUNNEL 请求不设计自动路径拼接和禁用路径拼接，其它请求匹配 `tunnel` 协议结果：\n- **WebSocket 请求**：忽略匹配\n- **普通 HTTP/HTTPS 请求**：返回 `502`\n\n"
  },
  {
    "path": "docs/docs/rules/ua.md",
    "content": "# ua\n修改请求头的 `Uer-Agent` 字段的快捷协议，可用于模拟各种机器访问。\n\n## 规则语法\n``` txt\npattern ua://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 自定义请求的 `User-Agent` 字符串<br/>• 内联/内嵌/Values内容<br/>⚠️ 不支持从文件/远程 URL 加载数据 | |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n```` txt\n# 将原来的 user-agent 改成 `Whistle/2.9.100`\nwww.example.com/path ua://Whistle/2.9.100\n\n# 存在空格的 UA\n``` ua.txt\nTest Whistle/2.9.100 \n```\nwww.example.com/path ua://{ua.txt}\n\n# 使用 reqHeaders\n``` ua.json\nuser-agent: Test Whistle/2.9.100 \n```\nwww.example.com/path reqHeaders://{ua.json}\n````\n\n## 关联协议\n1. 直接修改请求头：[reqHeaders://User-Agent=value](./reqHeaders)\n"
  },
  {
    "path": "docs/docs/rules/urlParams.md",
    "content": "# urlParams\n动态修改请求 URL 的查询参数，支持多种参数注入方式。\n\n## 规则语法\n``` txt\npattern urlParams://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 操作数据对象，支持从以下渠道获取：<br/>• 目录/文件路径<br/>• 远程 URL<br/>• 内联/内嵌/Values内容 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## 配置示例\n``` txt\nwww.example.com/path urlParams://test=123\n```\n访问 `https://www.example.com/path/to` 服务器收到的 URL：`https://www.example.com/path/to?test=123`\n\n<img src=\"/img/url-params.png\" width=\"360\" />\n\n```` txt\n``` test.json\ntest1: 1\ntest2:\ntest3: 3\n```\nwww.example.com/path2 urlParams://{test.json}\n````\n访问 `https://www.example.com/path2/to` 服务器收到的 URL：`https://www.example.com/path2?test1=1&test2=&test3=3`\n\n<img src=\"/img/url-params2.png\" width=\"360\" />\n\n#### 本地/远程资源\n\n```` txt\nwww.example.com/path1 urlParams:///User/xxx/test.json\nwww.example.com/path2 urlParams://https://www.xxx.com/xxx/params.json\n# 通过编辑临时文件\nwww.example.com/path3 urlParams://temp/blank.json\n````\n\n## 关联协议\n1. 更灵活的修改请求参数的方式：[pathReplace](./pathReplace)\n2. 删除请求参数：[delete://urlParams.xxx](./delete)\n"
  },
  {
    "path": "docs/docs/rules/weinre.md",
    "content": "# weinre\n在页面中注入 Weinre（Web Inspector Remote）网页远程调试工具，允许开发者在电脑上直接调试移动设备上的网页。\n\n## 规则语法\n``` txt\npattern weinre://id [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| id   | Weinre ID，普通字符串，用于分组过滤                   |                    |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n\n详细用法参考：[界面菜单 Weinre 用法](../gui/weinre)\n"
  },
  {
    "path": "docs/docs/rules/ws.md",
    "content": "# ws\n将 WebSocket 请求转换为新的 ws 请求（服务端将收到转换后的 WebSocket URL）。\n> 只支持 WebSocket 请求 `ws[s]://domain[:port]/[path][?query]`，不支持转换隧道代理和普通 HTTP/HTTPS\n\n## 规则语法\n``` txt\npattern ws://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配 WebSocket 请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]/[path][?query]`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## WebSocket 转换\n``` txt\nws://www.example.com/path1 ws://www.test.com/path/xxx\nwss://www.example.com/path2 ws://www.abc.com/path3/yyy\n```\n1. 自动路径拼接：\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `ws://www.example.com/path1`              | `ws://www.test.com/path/xxx`             |\n    | `ws://www.example.com/path1/a/b/c?query`  | `ws://www.test.com/path/xxx/a/b/c?query` |\n    | `wss://www.example.com/path2`            | `ws://www.abc.com/path3/yyy`             |\n    | `wss://www.example.com/path2/a/b/c?query` | `ws://www.abc.com/path3/yyy/a/b/c?query` |\n2. 禁用路径拼接：使用 `< >` 或 `( )` 包裹路径\n    ``` txt\n    www.example.com/path1 ws://<www.test.com/path/xxx>\n    # www.example.com/path1 ws://(www.test.com/path/xxx)\n    ```\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[wss/ws]://www.example.com/path/x/y/z` | `ws://www.test.com/path/xxx` |\n\n只支持转发 WebSocket 请求，其它请求匹配 `ws` 协议结果：\n- **隧道代理**：忽略匹配\n- **普通 HTTP/HTTPS 请求**：返回 `502`\n\n"
  },
  {
    "path": "docs/docs/rules/wss.md",
    "content": "# wss\n将 WebSocket 请求转换为新的 wss 请求（服务端将收到转换后的 WebSocket URL）。\n> 只支持 WebSocket 请求 `ws[s]://domain[:port]/[path][?query]`，不支持转换隧道代理和普通 HTTP/HTTPS\n\n## 规则语法\n``` txt\npattern wss://value [filters...]\n```\n\n| 参数    | 描述                                                         | 详细文档                  |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | 匹配 WebSocket 请求 URL 的表达式                                        | [匹配模式文档](./pattern) |\n| value   | 目标 URL 格式：`domain[:port]/[path][?query]`<br/>⚠️ 不支持从文件/远程 URL 加载数据 | [操作指令文档](./operation)   |\n| filters | 可选过滤器，支持匹配：<br/>• 请求URL/方法/头部/内容<br/>• 响应状态码/头部 | [过滤器文档](./filters) |\n\n## WebSocket 转换\n``` txt\nws://www.example.com/path1 wss://www.test.com/path/xxx\nwss://www.example.com/path2 wss://www.abc.com/path3/yyy\n```\n1. 自动路径拼接：\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `ws://www.example.com/path1`              | `wss://www.test.com/path/xxx`             |\n    | `ws://www.example.com/path1/a/b/c?query`  | `wss://www.test.com/path/xxx/a/b/c?query` |\n    | `wss://www.example.com/path2`            | `wss://www.abc.com/path3/yyy`             |\n    | `wss://www.example.com/path2/a/b/c?query` | `wss://www.abc.com/path3/yyy/a/b/c?query` |\n2. 禁用路径拼接：使用 `< >` 或 `( )` 包裹路径\n    ``` txt\n    www.example.com/path1 wss://<www.test.com/path/xxx>\n    # www.example.com/path1 wss://(www.test.com/path/xxx)\n    ```\n    | 原始请求                                  | 转换结果（服务端收到的 URL）              |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[wss/ws]://www.example.com/path/x/y/z` | `wss://www.test.com/path/xxx` |\n\n只支持转发 WebSocket 请求，其它请求匹配 `wss` 协议结果：\n- **隧道代理**：忽略匹配\n- **普通 HTTP/HTTPS 请求**：返回 `502`\n\n"
  },
  {
    "path": "docs/docs/rules/xfile.md",
    "content": "# xfile\n\nxfile 是 [file](./file) 规则的穿透版本，主要区别在于文件不存在时的处理方式：\n- ✅ 文件存在时：返回本地文件内容（与 [file](./file) 规则行为一致）\n- ❌ 文件不存在时：继续正常网络请求（而 [file](./file) 规则会返回 `404`）\n\n## 规则语法\n``` txt\npattern xfile://value [filters...]\n```\n\n详细用法参考：[file](./file)\n"
  },
  {
    "path": "docs/docs/rules/xhost.md",
    "content": "# xhost\n\nxhost 是 [host](./host) 规则的穿透版本，主要区别在于请求失败时的处理方式：\n- ✅ 请求正常时：与 [host](./host) 规则行为一致\n- ❌ 请求出错时：忽略该匹配规则，继续正常网络请求（而 [host](./host) 中断请求）\n\n## 规则语法\n``` txt\npattern xhost://value [filters...]\n```\n\n详细用法参考：[host](./host)\n"
  },
  {
    "path": "docs/docs/rules/xhttps-proxy.md",
    "content": "# xhttps-proxy\n\nxhttps-proxy 是 [https-proxy](./https-proxy) 规则的穿透版本，主要区别在于跟目标代理建立连接失败时的处理方式：\n- ✅ 建立连接成功时：与 [https-proxy](./https-proxy) 规则行为一致\n- ❌ 建立连接失败时：忽略该匹配规则，继续正常网络请求（而 [https-proxy](./https-proxy) 中断请求）\n\n## 规则语法\n``` txt\npattern xhttps-proxy://value [filters...]\n```\n\n详细用法参考：[https-proxy](./https-proxy)\n"
  },
  {
    "path": "docs/docs/rules/xproxy.md",
    "content": "# xproxy (xhttp-proxy)\n\nxproxy (xhttp-proxy) 是 [proxy](./proxy) 规则的穿透版本，主要区别在于跟目标代理建立连接失败时的处理方式：\n- ✅ 建立连接成功时：与 [proxy](./proxy) 规则行为一致\n- ❌ 建立连接失败时：忽略该匹配规则，继续正常网络请求（而 [proxy](./proxy) 中断请求）\n\n## 规则语法\n``` txt\npattern xproxy://value [filters...]\n```\n\n详细用法参考：[proxy](./proxy)\n"
  },
  {
    "path": "docs/docs/rules/xrawfile.md",
    "content": "# xrawfile\n\nxrawfile 是 [rawfile](./rawfile) 规则的穿透版本，主要区别在于文件不存在时的处理方式：\n- ✅ 文件存在时：返回本地文件内容（与 [rawfile](./rawfile) 规则行为一致）\n- ❌ 文件不存在时：继续正常网络请求（而 [rawfile](./rawfile) 规则会返回 `404`）\n\n## 规则语法\n``` txt\npattern xrawfile://value [filters...]\n```\n\n详细用法参考：[rawfile](./rawfile)\n"
  },
  {
    "path": "docs/docs/rules/xsocks.md",
    "content": "# xsocks\n\nxsocks 是 [socks](./socks) 规则的穿透版本，主要区别在于跟目标代理建立连接失败时的处理方式：\n- ✅ 建立连接成功时：与 [socks](./socks) 规则行为一致\n- ❌ 建立连接失败时：忽略该匹配规则，继续正常网络请求（而 [socks](./socks) 中断请求）\n\n## 规则语法\n``` txt\npattern xsocks://value [filters...]\n```\n\n详细用法参考：[socks](./socks)\n"
  },
  {
    "path": "docs/docs/rules/xtpl.md",
    "content": "# xtpl\n\nxtpl 是 [tpl](./tpl) 规则的穿透版本，主要区别在于文件不存在时的处理方式：\n- ✅ 文件存在时：返回本地文件内容（与 [tpl](./tpl) 规则行为一致）\n- ❌ 文件不存在时：继续正常网络请求（而 [tpl](./tpl) 规则会返回 `404`）\n\n## 规则语法\n``` txt\npattern xtpl://value [filters...]\n```\n\n详细用法参考：[tpl](./tpl)\n"
  },
  {
    "path": "docs/en/docs/cli.md",
    "content": "# Command Line Operations\nWhistle supports the following command line operations. To view the full command line functionality, execute the command: `w2 -h`:\n\n``` sh\nUsage: w2 <command> [options]\n\n\nCommands:\n\n  status      Display running status\n  add         Add rules from local JS file (.whistle.js by default)\n  proxy       Configure system proxy settings\n  ca          Manage Root CA certificates\n  install     Install Whistle plugin\n  uninstall   Uninstall Whistle plugin\n  exec        Execute plugin command\n  run         Start a front service\n  start       Start a background service\n  stop        Stop current background service\n  restart     Restart current background service\n  help        Display help information\n\nOptions:\n\n  -h, --help                                      output usage information\n  -D, --baseDir [baseDir]                         set storage root path\n  -z, --certDir [directory]                       set custom certificate directory\n  -l, --localUIHost [hostname]                    set web UI domain (local.whistlejs.com by default)\n  -L, --pluginHost [hostname]                     set plugin UI domains  (as: \"script=a.b.com&vase=x.y.com\")\n  -n, --username [username]                       set web UI username\n  -w, --password [password]                       set web UI password\n  -N, --guestName [username]                      set web UI guest username (read-only)\n  -W, --guestPassword [password]                  set web UI guest password (read-only)\n  -s, --sockets [number]                          set max cached connections per domain (256 by default)\n  -S, --storage [newStorageDir]                   set configuration storage directory\n  -C, --copy [storageDir]                         copy configuration from specified directory\n  -c, --dnsCache [time]                           set DNS cache time (default: 60000ms)\n  -H, --host [boundHost]                          set bound host (default: INADDR_ANY)\n  -p, --port [proxyPort]                          set proxy port (default: 8899 by default)\n  -P, --uiport [uiport]                           set web UI port\n  -m, --middlewares [script path or module name]  set startup middlewares (format: xx,yy/zz.js)\n  -M, --mode [mode]                               set startup mode (options: pureProxy|debug|multiEnv|capture|disableH2|network|rules|plugins|prod)\n  -t, --timeout [ms]                              set request timeout (default: 360000\n  -e, --extra [extraData]                         set plugin extra parameters\n  -f, --secureFilter [secureFilter]               set secure filter path\n  -r, --shadowRules [shadowRules]                 set default shadow rules\n  -R, --reqCacheSize [reqCacheSize]               set request data cache size (default: 600)\n  -F, --frameCacheSize [frameCacheSize]           set WebSocket frame cache size (default: 512)\n  -A, --addon [pluginPaths]                       add custom plugin paths\n  --init [bypass]                                 auto configure proxy and install Root CA\n  --cluster [workers]                             start cluster with worker count (default: CPU cores)\n  --config [config]                               load startup config from file\n  --rcPath [rcPath]                               load configuration from file (default: ～/.whistlerc)\n  --dnsServer [dnsServer]                         set custom DNS servers\n  --socksPort [socksPort]                         set SOCKSv5 server port\n  --httpPort [httpPort]                           set HTTP server port\n  --httpsPort [httpsPort]                         set HTTPS server port\n  --allowOrigin [originList]                      set allowed CORS origins (format: a.b.c,x.y.z or *)\n  --no-global-plugins                             disable global plugins\n  --no-prev-options                               ignore previous options on restart\n  --inspect [[host:]port]                         enable inspector (default: 127.0.0.1:9229)\n  --inspectBrk [[host:]port]                      enable inspector with breakpoint (default: 127.0.0.1:9229)\n  -V, --version                                   output the version number\n```\n\n## w2 start\n1. `w2 start`: Starts Whistle and uses the default storage directory.\n2. `w2 start -p 8100`: Starts Whistle on the specified port (default is `8899`).\n3. `w2 start --httpsPort 8001`: Starts Whistle and enables the HTTPS proxy port.\n4. `w2 start --socksPort 1080`: Starts Whistle and enables the SOCKSv5 proxy port.\n5. `w2 start -S storageName`: Starts Whistle on the specified storage directory (uppercase `S`).\n> `storageName` should be a plain directory name (not a full path). For multiple instances, the following requirements must be met:\n> - Use a separate directory for each instance.\n> - Configure different listening ports.\n> ``` sh\n> w2 start\n> w2 start -p 8100 -S storageName2\n> ```\n\n## w2 restart\n1. `w2 restart`: Restarts Whistle.\n- If there's no Whistle instance running for the corresponding port and storage directory, it will be started.\n- If there's a Whistle instance running for the corresponding port and storage directory, it will be shut down before starting.\n2. `w2 restart --no-prev-options`: Equivalent to `w2 stop && w2 start`\n3. `w2 restart -p 8100`: Restarts and changes the port number.\n4. `w2 startrestartt --httpsPort 8001`: Restarts and enables the HTTPS proxy port.\n4. `w2 restart --socksPort 1080`: Restarts and enables the SOCKSv5 proxy port.\n5. `w2 restart -S storageName`: Restarts the Whistle instance for the specified storage directory.\n\n## w2 stop\n1. `w2 stop`: Stops Whistle for the default storage directory.\n2. `w2 stop -S storageName`: Stops Whistle for the specified storage directory.\n\n> You can use `w2 status --all` to view the Whistle instances currently running in the background. Example\n\n## w2 status\n\n1. Output all Whistle instances currently running in the command line background: `w2 status --all`\n``` sh\n[i] All running Whistle instances:\n1. PID: 51512, Port: 8899\n2. PID: 53951, Port: 8080, Storage: 8080\n```\n2. Output the default instance running in the command line background: `w2 status`\n``` sh\n[!] whistle@version is running\n[i] 1. Use your device to visit the following URL list and obtain the IP address of the URL you can access:\nhttp://127.0.0.1:8899/\nhttp://192.168.10.153:8899/\nhttp://10.211.55.2:8899/\nhttp://10.37.129.2:8899/\nNote: If all URLs are inaccessible, check firewall settings.\nFor help, see https://github.com/avwo/whistle\n[i] 2. Set the HTTP proxy on your device with the above IP & PORT (8899)\n[i] 3. Use Chrome to visit http://local.whistlejs.com/ to get started.\n\n## w2 add\n1. `w2 add`: Execute `.whistle.js` (or `.whistle.mjs`) in the current directory and set the exported `name`, `rules`, or `groupName` (optional) to the Rules interface.\n2. `w2 add filepath`: Customize the executed file.\n\n`.whistle.js` file contents:\n``` js\nconst pkg = require('./package.json');\n\nexports.groupName = 'Project Development Environment'; // Optional, set the group. Requires Whistle version >= v2.9.21\nexports.name = `[${pkg.name}] Local Environment Configuration`;\nexports.rules = `\ntest.example.com http://127.0.0.1:5566\n# cgi live network\ntest.example.com/cgi-bin ignore://http\n```\n\nOr asynchronously obtain rules:\n``` js\nconst assert = require('assert');\nconst path = require('path');\nconst pkg = require('./package.json');\n\nmodule.exports = (cb, util) => {\n  // If you depend on a plugin, you can check for it.\n  assert(util.existsPlugin('@scope/whistle.combo')\n    || util.existsPlugin('whistle.combo'), 'Please install the plugin first: w2 i whistle.combo');\n  // You can also obtain rules remotely.\n  // do sth\n  cb({\n    name: `[${pkg.name}] Local Environment Configuration`,\n    rules: `\n      test.example.com/combo whisle.combo://${path.join(__dirname, 'dev')}\n      test.example.com http://127.0.0.1:5566\n      # Interface goes to the live network\n      test.example.com/cgi-bin ignore://http\n      `\n  });\n};\n```\n\nWhistle will check if a rule with the same name exists:\n- If the rule does not exist or is empty: a new rule will be automatically created and enabled.\n- If the rule already exists and is not empty: the user will be prompted for confirmation to prevent accidental overwriting.\n- To force overwriting of existing rules, explicitly add the `--force` parameter:\n``` sh\nw2 add --force\n```\n\n## w2 proxy\n1. `w2 proxy`: Set the system proxy.\n   - IP: `127.0.0.1`\n   - Port: Whistle Running port. If Whistle is not running, use the default port 8899.\n2. `w2 proxy 0`: Disable the system proxy.\n3. `w2 proxy 8100`: Set the system proxy.\n   - IP: `127.0.0.1`\n   - Port: `8100`\n4. `w2 proxy www.test.com:8100`: Set the system proxy.\n   - IP or domain: `www.test.com`\n   - Port: `8100`\n\n## w2 ca\n1. `w2 ca`: Install the local Whistle root certificate (this command is generally used to install the local Whistle root certificate).\n2. `w2 ca 8080`: Download and install the Whistle root certificate from the specified port (IP: `127.0.0.1`).\n4. `w2 ca www.test.com:8080`: Install Whistle for the specified port and IP (or domain). Certificate (can be used to install a remote Whistle root certificate)\n5. `w2 ca certUrl`: Downloads and installs the certificate from the specified URL\n6. `w2 ca localCertPath`: Installs the certificate from the specified local path\n\n## w2 install\n1. `w2 install whistle.script`: Installs the plugin\n2. `w2 install whistle.script --registry=https://npm-registry`: Installs the plugin and specifies the npm registry\n\nRecommended UI installation: [Use plugin](./extensions/usage)\n\n## w2 uninstall\n`w2 uninstall whistle.script`: Uninstalls the specified plugin\n\nRecommended UI uninstall: [Use plugin](./extensions/usage)\n## w2 exec\n`w2 exec xxx`: Executes the `bin` command configured in the plugin's package.json file (i.e., the executable script provided by the plugin).\n> Suitable for plugin developers or when calling the plugin's CLI functionality.\n\n## w2 run\n`w2 run`: Starts Whistle in development and debugging mode, outputting plugin and system log information to the console in real time, automatically refreshing UI code changes, and supports all `w2 start` configuration options.\n\n## Starting Multiple Instances\n\nWhen running multiple instances simultaneously, each instance must be configured with **different ports** and **independent storage directories** to avoid resource conflicts.\n\n### Configuration Examples\n\n```sh\n# Instance 1 - Port 8010, Storage directory 8010\nw2 start --port 8010 --storage 8010\n\n# Instance 2 - Port 8011, Storage directory 8011  \nw2 start --port 8011 --storage 8011\n\n# Instance 3 - Port 8012, Storage directory 8012\nw2 start --port 8012 --storage 8012\n```\n\n### Configuration Description\n\n| Parameter | Purpose | Requirements |\n|-----------|---------|--------------|\n| `--port` | Service listening port | Must be unique, cannot be duplicated |\n| `--storage` | Data storage directory | Must be unique to avoid data confusion |\n\n### Configuration Recommendations\n\n**Recommended Configuration**:\n- Use the same numbering for ports and storage directories (for easier management)\n- Port number range: 8000-9000 (to avoid system port conflicts)\n\n**Configuration Examples**:\n```sh\n# ✅ Recommended: Consistent port and storage directory\nw2 start -p 8010 -S 8010\nw2 start -p 8011 -S 8011\n\n# ✅ Acceptable: Different port and storage directory (ensure uniqueness)\nw2 start -p 8020 -S instance_1\nw2 start -p 8021 -S instance_2\n\n# ❌ Error: Duplicate port or storage directory\nw2 start -p 8010 -S 8010\nw2 start -p 8010 -S 8011  # Port conflict!\nw2 start -p 8011 -S 8010  # Storage directory conflict!\n```\n\n### Important Notes\n\n1. **Port Conflicts**: Using the same port will cause instance startup failure\n2. **Data Security**: Sharing storage directories may cause data overwriting or corruption\n3. **Resource Isolation**: Each instance should have an independent runtime environment\n\nBy assigning unique ports and storage directories to each instance, you can ensure stable parallel operation of multiple instances.\n\n# Startup Configuration File {#whistlerc}\n\nWhistle automatically loads the `~/.whistlerc` configuration file upon startup. You can manage configuration loading in the following ways:\n\n- **Default Path:** `~/.whistlerc`\n- **Custom Path:** Use the `--rcPath [rcPath]` parameter to specify a different configuration file\n- **Ignore Configuration:** Use the `-rcPath none` parameter to completely ignore the configuration file\n\nThe `.whistlerc` file uses a key-value pair format and supports multiple configuration scopes:\n\n```txt\n# Global default configuration (applies when no storage is specified)\nusername=test\npassword=123\n\n# Specific storage configuration (via command line storage parameter)\nxxx.username=test-storage\nxxx.password=123-storage\n\n# Wildcard configuration (applies to all instances, lowest priority)\n*.username=test-default\n*.password=123-default\n```\n\n1. `xxx` corresponds to the value of the startup parameter `--storage` (if no storage is specified, default configuration is used)\n2. `*` applies to all instances (lowest priority)\n3. For other configuration parameters, please refer to this documentation\n\n## Other Options\n1. `-D, --baseDir` `[baseDir]`: Customize the Whistle storage root directory (defaults to `$WWHISTLE_PATH/.whistle`)\n  > Example: `w2 start -D ~/my_whistle_data`\n2. `-n, --username [username]`: Set the management interface login username\n3. `-w, --password [password]`: Set the management interface login password\n  > Example: `w2 start -n abc -w 123`\n4. `-N, --guestName [username]`: Set a read-only guest account (guests can only view configuration and capture packets, but cannot modify them)\n5. `-W, --guestPassword [password]`: Set the guest account password (guests can only view configuration and capture packets, but cannot modify them)\n  > Example: `w2 start -N test -W 123`\n6. `-P, --uiport [uiport]`: Set a separate management interface port (defaults to the same as the proxy port)\n  > Example: `w2 start -P 8889\n7. `-e, --extra [extraData]`: Passes data to the specified plugin at startup (e.g., {inspect: data}`).\n  > Example: `w2 start -e '{\"debug\":true}'`\n8. `--allowOrigin [originList]`: Allows cross-origin requests to the domain name of the management interface.\n  > Example: `w2 start --allowOrigin *`\n9. `--no-global-plugins`: Does not load plugins installed with `npm i -g whistle.xxx` at startup.\n  > Example: `w2 start --no-global-plugins`\n10. `--inspect [[host:]port]`: Enables Node.js debugging (default port 9229), for use with Chrome DevTools.\n  > Example: `w2 start --inspect`\n11. `--inspectBrk [[host:]port]`: Enables debugging and sets a breakpoint on the first line.\n  > Example: `w2 `start --inspectBrk`\n12. `--config [config]`: Load parameters from a configuration file\n  > Example: `w2 start --config /data/xxx.json`\n\nTo set a user password for requests, you need a plugin (or develop your own): [whistle.proxyauth](https://github.com/whistle-plugins/whistle.proxyauth)\n    "
  },
  {
    "path": "docs/en/docs/extensions/dev.md",
    "content": "# Plugin Development\nNow that you've learned about the plugin's features (see the [usage documentation](./usage)), we'll now demonstrate its implementation using a modular approach.\n> Each core feature is implemented as a separate plugin, adhering to the single responsibility principle. These modules can be freely assembled in development.\n\n## Preparation\n1. Create an empty directory, such as\n    ``` sh\n    mkdir examples && cd examples\n    ```\n2. Install the scaffold [lack](https://github.com/avwo/lack)\n    ``` txt\n    npm i -g lack\n    ```\n    > Lack must be the latest version (`>= 1.4.0`)\n3. Update [Whistle](https://github.com/avwo/whistle)\n    > Whistle must be the latest version (`>= 2.9.100`, [client](https://github.com/avwo/whistle-client) `>= 1.3.8`)\n\n## rules.txt\n- The plugin will automatically load and take effect after installation.\n- Its functionality is the same as the rules configured in the UI Rules, but it has a lower priority than UI Rules.\n- The plugin will become ineffective if disabled.\n\n**Example**\n1. Create a plugin containing the `rules.txt` rules file:\n    ```sh\n    # Create a plugin directory\n    mkdir whistle.test-rules && cd whistle.test-rules\n\n    # Initialize the plugin containing rules.txt\n    lack init rules\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-rules` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the generated `rules.txt` file:\n    ``` txt\n    * reqHeaders://x-client=whistle.test-rules\n    ```\n3. All requests passing through Whistle will automatically add the `x-client: whistle.test-rules` request header\n\n    <img src=\"/img/test-rules.png\" width=\"600\" />\n4. What if the modified rules don't take effect? Please confirm:\n   - Has `lack watch` been executed?\n   - Are there any conflicting rules with the UI configuration?\n   - Is the plugin disabled?\n\n## _rules.txt\n- Applies only to requests that match the plugin protocol.\n- Supports both long (whistle.myplugin://) and short (myplugin://) protocols.\n- Applies during the request processing phase.\n\n``` txt\npattern whistle.myplugin://value\npattern myplugin://value\n```\n\n**Example**\n1. Create a plugin containing the `_rules.txt` rules file:\n```sh\n# Create the plugin directory\nmkdir whistle.test-req-rules && cd whistle.test-req-rules\n\n# Initialize the plugin containing the `_rules.txt` file\nlack init _rules\n\n# Run in development mode: Mount the plugin to Whistle. After mounting, you will see the `test-req-rules` plugin in the plugin list.\nlack watch\n```\n2. Edit the generated `_rules.txt` file:\n    ``` txt\n    * file://(hello)\n    ```\n    > All requests return `hello` in the response.\n4. Directly access `https://www.example.com/test` to return to the normal page.\n5. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    https://www.example.com/test whistle.test-req-rules://\n    ```\nThen, access `https://www.example.com/test` to return `hello`.\n\n## resRules.txt\n- Only applies to requests matching the plugin protocol\n- Applies during the response processing phase\n- Can modify the response status code and content.\n    ``` txt\n    pattern whistle.myplugin://value\n    pattern myplugin://value\n    ```\n\n**Example**\n\n1. Create a plugin containing the `resRules.txt` rule file:\n    ```sh\n    # Create a plugin directory\n    mkdir whistle.test-res-rules && cd whistle.test-res-rules\n\n    # Initialize the plugin including resRules.txt\n    lack init resRules\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-res-rules` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the generated `resRules.txt` file:\n    ``` txt\n    * resBody://`(whistle_error_${statusCode})` includeFilter://s:500 includeFilter://s:404\n    ```\n    > Change the response content for requests with `404` and `500` status codes to `whistle_error_404` and `whistle_error_500` respectively.\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    https://www.example.com/500 whistle.test-req-rules:// statusCode://500\n    https://www.example.com/404 whistle.test-req-rules:// statusCode://404\n\n    ``\n4. Effect:\n    - Accessing `https://www.example.com/500` changes the response content to `whistle_error_500`\n    - Accessing `https://www.example.com/404` changes the response content to `whistle_error_404`\n    - Accessing `https://www.example.com/test` returns the original content.\n\n## auth\nAuthenticate requests passing through Whistle.\n\n**Example**\n\n1. Create a request authentication plugin:\n    ```sh\n    # Create a plugin directory\n    mkdir whistle.test-auth && cd whistle.test-auth\n\n    # Initialize the plugin including auth\n    lack init auth\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-auth` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the `src/auth.ts` file:\n    ``` ts\n    export default async (req: Whistle.PluginAuthRequest, options: Whistle.PluginOptions) => {\n    const { fullUrl } = req;\n    // The response status code for a URL containing `/test/forbidden` is 403\n    if (fullUrl.includes('/test/forbidden')) {\n      return false;\n    }\n    // The response code for a URL containing `/test/message/forbidden` is 403, and the response content is customized.\n    if (fullUrl.includes('/test/message/forbidden')) {\n      req.setHtml('<strong>Access Denied</strong>');\n      return false;\n    }\n\n    // The URL contains `/test/login` and requires the user to enter a username and password.\n    if (fullUrl.includes('/test/login')) {\n      const auth = req.headers.authorization || req.headers['proxy-authorization'];\n      if (auth) {\n        // TODO: Verify the username and password. Return true if correct, otherwise return false.\n        return true;\n      }\n      req.setLogin(true);\n      return false;\n    }\n\n    // The response code for a URL containing `/test/redirect` is 302, and redirect to `https://www.example.com/test`\n    if (fullUrl.includes('/test/redirect')) {\n      req.setRedirect('https://www.example.com/test');\n      return false;\n    }\n    // Other requests are ignored\n    // If you need to add custom request headers, use the `req.setHeader` method\n    // Supports adding request headers with a key prefix of `x-whistle-`\n    / // For example: req.setHeader('x-whistle-xxx', 'value');\n    req.setHeader('x-whistle-custom-header', 'lack');\n      return true;\n    };\n    ```\n3. Configure the following rule in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    www.example.com whistle.auth://\n    ```\n4. Effect:\n   - Access `https://www.example.com/test/forbidden` responds with a `403` status code and `Forbidden` content.\n   - Visiting `https://www.example.com/test/message/forbidden` responds with a `403` status code and `<strong>Access Denied</strong>` content.\n   - Visiting `https://www.example.com/test/login` displays a browser login dialog.\n   - Visiting `https://www.example.com/test/redirect` redirects to `https://www.example.com/test`.\n   - Other requests to `www.example.com` complete normally, with the `x-whistle-custom-header: lack` header added.\n\n## sniCallback\nDynamically issues request certificates through the plugin.\n\n**Example**\n\n1. Create a custom certificate plugin:\n```sh\n# Create the plugin directory\nmkdir whistle.test-sni && cd whistle.test-sni\n\n# Initialize the plugin including SNI\nlack init sni\n\n# Install dependencies\nnpm i\n\n# Compile the code\nnpm run dev\n\n# Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-sni` plugin in the plugin list.\nlack watch\n```\n2. Edit the `src/sniCallback.ts` file:\n``` ts\nconst key = `...`;\nconst cert = `...`;\n\n// sniCallback plugin handler function - Dynamically determines the TLS tunneling method based on the request URL\nexport default async (req: Whistle.PluginSNIRequest, options: Whistle.PluginOptions) => {\nconst { fullUrl } = req;\n// Returns false for special domains, maintaining the tunnel state (does not decrypt TLS).\n\nif (fullUrl === 'https://tunnel.example.com') {\nreturn false;\n\n}\n\n// Use a custom certificate to decrypt a specific domain, returning an object containing the key and cert.\n// Supports certificate formats such as .crt, .pem, and .cer.\n\nif (fullUrl === 'https://custom.example.com') {\nreturn { key, cert };\n\n}\n// Use Whistle's built-in certificate to decrypt TLS traffic by default.\nreturn true;\n};\n\n```\n5. Configure the following rule in the Rules interface (or the plugin's `rules.txt` file):\n``` txt\nwww.example.com sniCallback://test-sni\n```\n6. Result:\n- Accessing `https://tunnel.example.com/path` will maintain the tunnel state (does not decrypt TLS).\n- Accessing `https://custom.example.com/test/` reports a certificate error.\n\nOther requests are normal.\n\n## rulesServer\nRule generation is done in real time using JavaScript. These rules function similarly to the static rules in _rules.txt above, but take precedence over those in _rules.txt.\n\n**Example**\n\n1. Create a plugin containing `rulesServer`:\n\n    ```sh\n    # Create a plugin directory\n\n    mkdir whistle.test-sni && cd whistle.test-rules-server\n\n    # Initialize the plugin containing `rulesServer`\n\n    lack init rulesServer\n\n    # Install dependencies\n\n    npm i\n\n    # Compile the code\n\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-rules-server` plugin in the plugin list.\n\n    lack watch\n\n    ```\n2. Edit the `src/rulesServer.ts` file:\n\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest, res: Whistle.PluginResponse) => {\n        resh.end(JSON.stringify({\n          values: {\n            'whistle.test-rules-server/a.html': 'test normal values',\n          },\n          rules: `\n            \\`\\`\\` whistle.test-rules-server/b.html\n            test inject values\n            \\`\\`\\`\n\n            */a file://{whistle.test-rules-server/a.html}\n            */b file://{whistle.test-rules-server/b.html}\n            `,\n        }));\n      });\n    };\n    ```\n    > Supports responding with plain rules text `res.end(rules)` or a serialized object containing rules & values `res.end(JSON.stringify({rules, values}))`\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    test.example.com whistle.test-rules-server://\n    ```\n4. Effect:\n   - Access `https://test.example.com/a` returns `test injected values`\n   - Visiting `https://test.example.com/b` returns `test normal values`\n\n## tunnelRulesServer\ntunnelRulesServer is a dynamic rule generation mechanism specifically designed for handling TUNEL requests. Its core features are the same as the regular [rulesServer](#rulesserver), but its application scenarios are specifically differentiated:\n1. tunnelRulesServer only works for `tunnel://domain:port` tunnel requests.\n2. rulesServer works for `HTTP[S]/WebSocket` requests.\n\n## resRulesServer\nRule generation is done in real time during the request response phase using JavaScript (applicable to all request types). These rules function similarly to the static rules in resRules.txt above, but take precedence over those in resRules.txt.\n\n**Example**\n\n1. Create a plugin containing `resRulesServer`:\n    ```sh\n    # Create a plugin directory\n    mkdir whistle.test-res-rules-server && cd whistle.test-res-rules-server\n\n    # Initialize the plugin containing `resRulesServer`\n    lack init resRulesServer\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-res-rules-server` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the `src/resRulesServer.ts` file:\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest, res: Whistle.PluginResponse) => {\n        res.end('* resBody://(test-res-rules-server)');\n      });\n    };\n    ```\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    test.example.com/res whistle.test-res-rules-server://\n    ```\n4. Result:\n   - Accessing `https://test.example.com/res/path` returns `test-res-rules-server`\n\n## statsServer\nIf you only want to obtain information such as the request URL, request method, request headers, and request content without performing any operations on the request, you can use statsServer.\n\n**Example**\n\n1. Create a plugin containing `statsServer`:\n    ``` sh\n    # Create the plugin directory\n\n    mkdir whistle.test-stats-server && cd whistle.test-stats-server\n\n    # Initialize the plugin containing statsServer\n\n    lack init statsServer\n\n    # Install dependencies\n\n    npm i\n\n    # Compile the code\n\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-stats-server` plugin in the plugin list.\n\n    lack watch\n\n    ```\n2. Edit the `src/statsServer.ts` file:\n    ``` ts\n    export default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n      server.on('request', (req: Whistle.PluginRequest) => {\n        const { originalReq } = req;\n        console.log('Value:', originalReq.ruleValue);\n        console.log('URL:', originalReq.fullUrl);\n        console.log('Method:', originalReq.method);\n        console.log('Request Headers:', originalReq.headers);\n        // Get the request body\n        req.getReqSession((reqSession) => {\n          if (reqSession) {\n            console.log('Request Body:', reqSession.req.body);\n          }\n        });\n      });\n    };\n    ```\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    www.example.com/stats whistle.test-stats-server://test\n    ```\n4. Result:\n      - Visit `https://www.example.com/stats` Console output:\n      ``` sh\n      Value: test\n      URL: https://www.example.com/stats \n      Method: GET \n      Request Headers: { \n        host: 'www.example.com', \n        'cache-control': 'max-age=0', \n        'sec-ch-ua': '\"Not)A;Brand\";v=\"8\", \"Chromium\";v=\"138\", \"Google Chrome\";v=\"138\"', \n        'sec-ch-ua-mobile': '?0', \n        'sec-ch-ua-platform': '\"macOS\"', \n        'upgrade-insecure-requests': '1', \n        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36', \n        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', \n        'sec-fetch-site': 'cross-site', \n        'sec-fetch-mode': 'navigate', \n        'sec-fetch-user': '?1', \n        'sec-fetch-dest': 'document', \n        'accept-encoding': 'gzip, br', \n        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', \n        priority: 'u=0, i', \n        'x-forwarded-for': '127.0.0.1', \n        connection: 'close' \n      } \n      Request Body:\n      ```\n\n## resStatsServer\nTo obtain information such as the response status code, headers, and content without performing any operations on the request, use resStatsServer.\n\n**Example**\n\n1. Create a plugin containing `resStatsServer`:\n```sh\n# Create a plugin directory\nmkdir whistle.test-res-stats-server && cd whistle.test-res-stats-server\n\n# Initialize the plugin containing resStatsServer\nlack init resStatsServer\n\n# Install dependencies\nnpm i\n\n# Compile the code\nnpm run dev\n\n# Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-res-stats-server` plugin in the plugin list.\nlack watch\n```\n2. Install dependencies\n``` sh\nnpm i\n```\n3. Edit the `src/resStatsServer.ts` file:\n``` ts\nexport default (server: Whistle.PluginServer, options: Whistle.PluginOptions) => {\n  server.on('request', (req: Whistle.PluginRequest) => {\n    const { originalReq, originalRes } = req;\n    console.log('Value:', originalReq.ruleValue);\n    console.log('URL:', originalReq.fullUrl);\n    console.log('Method:', originalReq.method);\n    console.log('Server IP', originalRes.serverIp);\n    console.log('Status Code:', originalRes.statusCode);\n    console.log('Response Headers:', originalReq.headers);\n    // Get the complete packet capture data of the request\n    req.getSession((reqSession) => {\n      if (reqSession) {\n        console.log('Response Body:', reqSession.res.body);\n      }\n    });\n  });\n};\n```\n4. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n``` txt\nwww.example.com/res/stats whistle.test-res-stats-server://testResStats\n```\n5. Result:\n- Visit `https://www.example.com/res/stats` Console output:\n    ``` txt\n    Value: testResStats\n    URL: https://www.example.com/res/stats\n    Method: GET\n    Server IP 127.0.0.1\n    Status Code: 200\n    Response Headers: {\n      'accept-ranges': 'bytes',\n      'content-type': 'text/html',\n      etag: '\"84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134\"',\n      'last-modified': 'Mon, 13 Jan 2025 20:11:20' GMT', \n      server: 'AkamaiNetStorage', \n      expires: 'Thu, 24 Jul 2025 02:36:23 GMT', \n      'cache-control': 'max-age=0, no-cache, no-store', \n      pragma: 'no-cache', \n      date: 'Thu, 24 Jul 2025 02:36:23 GMT', \n      'alt-svc': 'h3=\":443\"; ma=93600,h3-29=\":443\"; ma=93600', \n      'x-forwarded-for': '127.0.0.1', \n      host: 'www.example.com', \n      connection: 'close' \n    } \n    Response Body: <!doctype html> \n    <html> \n    <head> \n    <title>Example Domain</title>\n\n    ......\n    ```\n\n## server\nYou can use a plugin as a **server**, which receives and forwards requests. The plugin can respond directly to the request or process it and forward it to the target server. After the target server returns the response, the plugin can further process it and ultimately return the result to the browser or client.\n\n**Example**\n\n1. Create a plugin containing the `server` plugin:\n    ```sh\n    # Create the plugin directory\n    mkdir whistle.test-server && cd whistle.test-server\n\n    # Initialize the plugin containing the server\n    lack init server\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you should see the `test-server` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the `src/server.ts` file: https://github.com/whistle-plugins/examples/tree/master/whistle.test-server\n3. Configure the following rule in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    https://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json test-server://setCookieFromBody\n    ```\n4. Visit `https://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json`\n   - Add `test-name=whistle` to the browser cookie\n   - Add `\"pluginName\":\"whistle\"` to the response content\n\n## pipe\nIf the request/response content is encrypted or needs to be converted to a specific format for display in the packet capture interface, you can use pipe to pass the request/response content to the plugin for processing.\n> In the packet capture interface:\n>\n> Modifications made by `reqWrite` are not visible in the request data\n>\n> Modifications made by `resWrite` are not visible in the response data (this is expected behavior)\n>\n> The actual modifications made by `xxxRexWrite` operations are not displayed in the packet capture interface\n>\n\n#### HTTP/HTTPS Protocol Implementation\n1. Create the pipe http plugin:\n    ```sh\n    # Create the plugin directory\n    mkdir whistle.test-pipe-http && cd whistle.test-pipe-http\n\n    # Initialize the plugin containing pipe http\n    lack init pipeHttp,server\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you will see the `test-pipe-http` plugin in the plugin list\n    lack watch\n    ```\n4. Edit the code: https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-http\n5. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n        www.example.com/test-pipe-http pipe://test-pipe-http test-pipe-http://mirror\n    ```\n    > `test-pipe-http://mirror` is used to match the server's response to the client's request.\n6. In Whistle / Network / Composer, enter `www.example.com/test-pipe-http`, select the `POST` method, and enter `test-pipe-http` in the `Body` field.\n\n    <img src=\"/img/pipe-http.png\" width=\"400\" />\n7. Click the Send button to view the response body as shown below.\n\n    <img src=\"/img/pipe-http-result.png\" width=\"400\" />\n\n#### WebSocket Protocol Implementation\n1. Create the pipe websocket plugin:\n    ```sh\n    # Create the plugin directory\n    mkdir whistle.test-pipe-ws && cd whistle.test-pipe-ws\n\n    # Initialize the plugin containing the pipe websocket plugin\n    lack init pipeWs\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you will see the `test-pipe-ws` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the code: https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-ws\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    wss://echo.websocket.org/ pipe://test-pipe-ws\n    ```\n4. Open the page `https://echo.websocket.org/.ws` You can see the following result:\n\n    <img src=\"/img/pipe-ws.png\" width=\"520\" />\n\n#### TCP Tunnel Implementation\n1. Create the pipe tunnel plugin:\n    ```sh\n    # Create the plugin directory\n    mkdir whistle.test-pipe-tunnel && cd whistle.test-pipe-tunnel\n\n    # Initialize the plugin containing the pipe tunnel\n    lack init pipeTunnel.server\n\n    # Install dependencies\n    npm i --save-dev lack-proxy && npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you will see the `test-pipe-tunnel` plugin in the plugin list.\n    lack watch\n    ```\n2. Edit the code: https://github.com/whistle-plugins/examples/tree/master/whistle.test-pipe-tunnel\n3. Configure the following rules in the Rules interface (or the plugin's `rules.txt` file):\n    ``` txt\n    test-pipe-tunnel.example.com pipe://test-pipe-tunnel test-pipe-tunnel://mirror\n    ```\n4. Execute the `test.js` file in the `whistle.test-pipe-tunnel` root directory:\n    ``` sh\n    node test.js\n    ```\n5. Console output\n   \n    <img src=\"/img/pipe-tunnel.png\" width=\"560\" />\n\n## Plugin Operation Interface\nIn the Whistle plugin list, click `Option` or the plugin name to quickly open the plugin operation interface. The following three modes are supported:\n1. **Tab Mode**: Opens as a tab within the Whistle interface (default mode)\n2. **Dialog Mode**: Opens as a pop-up window within the Whistle interface.\n3. **New Tab Mode**: Opens in a new browser tab or client window.\n\n---\n\n### 1. Tab Mode (Default)\n**Features**\n- The default address for the plugin management interface is: `http[s]://domain[:port]/plugin.xxx/`\n- Open directly using `Options` in the plugin list or by the plugin name\n\n**Disable Tab Mode**\nTo disable this opening mode, configure it in `whistleConfig`:\n```js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"noOption\": true // Disable the default entry\n    ...\n  }\n...\n}\n```\n\n---\n\n### 2. Dialog Mode\n**Features**\n- Supports opening the management interface via pop-up windows\n- API callable by the pop-up window: https://github.com/avwo/whistle/blob/master/assets/modal.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n**Configuration Example**\n```js\n{\n  \"whistleConfig\": {\n    ...\n    \"openInModal\": {\n      \"width\": 360, // Popup width (px)\n      \"height\": 566 // Popup height (px)\n    }\n    ...\n  }\n}\n```\n\n---\n\n### 3. New Tab Mode\n**Scenario**\nUse this when you need to use an external page as the plugin management interface.\n\n**Basic Configuration**\n```js\n{\n  \"pluginHomepage\": \"https://your-external-page.com\" // External page URL\n}\n```\n\n**Advanced Options**\nTo force external links to open within Whistle (Tab or dialog mode):\n\n**Method 1: Dialog Mode**\n```js\n{\n  ...\n  \"pluginHomepage\": \"https://your-external-page.com\",\n  \"whistleConfig\": {\n    ...\n    \"openInModal\": { // Force opening in a pop-up window\n    \"width\": 360,\n    \"height\": 566\n    }\n    ...\n  }\n  ...\n}\n```\n\n**Method 2: Tab Mode**\n```js\n{\n  ...\n  \"pluginHomepage\": \"https://your-external-page.com\",\n  \"whistleConfig\": {\n    ...\n    \"openInPlugins\": true // Force opening in the plugin tab\n    ...\n  }\n  ...\n}\n```\n\n## Complete Hooks API\nReference: https://github.com/avwo/lack/blob/master/assets/ts/src/types/global.d.ts\n\n## Plugin Variable Configuration {#pluginvars}\nSupports `%` notation for configuration within rules. For details, see: [Use of the `%` notation](../rules/plugin-vars)\n\n## Custom Rule Completion {#rules-hint}\nIn summary, each plugin can extend the following rules:\n``` txt\n%myplugin=xxx\n%myplugin.key=xxx\nwhistle.myplugin://xxx\nmyplugin://xxx\nsinCallback://myplugin(sinValue)\npipe://myplugin(pipeValue)\n```\n\nWhistle also supports custom completion for the following rules:\n``` txt\n%myplugin=xxx\n%myplugin.key=xxx\nwhistle.myplugin://xxx\nmyplugin://xxx\n```\n\n**Example**\n1. Create the rules hints plugin:\n    ```sh\n    # Create the plugin directory\n    mkdir whistle.test-rules-hint && cd whistle.test-rules-hint\n\n    # Initialize the plugin including uiServer\n    lack init uiServer\n\n    # Install dependencies\n    npm i\n\n    # Compile the code\n    npm run dev\n\n    # Run in development mode: Mount the plugin to Whistle. After mounting, you will see the `test-rules-hint` plugin in the plugin list.\n    lack watch\n    ```\n    5. Configure `whistleConfig` in `package.json`, adding `pluginVars` and `hintUrl`:\n    ``` js\n    {\n      ...\n      \"whistleConfig\": {\n        \"pluginVars\": {\n          ... \n          \"hintUrl\": '/cgi-bin/plugin-vars' \n          ... \n          }, \n          \"hintUrl\": '/cgi-bin/get-hints' \n        }, \n        ... \n    } \n    ```\n6. Edit the `src/uiServer/router.ts` file: \n    ``` ts \n    export default (router: Router) => { \n    // For `%test-rules-hint[.key]=xxx` \n    router.get('/cgi-bin/plugin-vars', (ctx) => { \n      const { sep, value } = ctx.query; \n      const isKey = sep === '.'; \n      let key = ''; \n      let keyword = ''; \n      if (value && typeof value === 'string') { \n        if (isKey) { \n          const index = value.indexOf('='); \n          // %test-plugin-vars.xxx=yyy \n          if (index !== -1) { \n            key = value.substring(0, index); \n            keyword = value.substring(index + 1).toLowerCase(); \n          } else { \n            // %test-plugin-vars.xxx or %test-plugin-vars.xxx= \n            key = value; \n          } \n        } else { \n          // %test-plugin-vars=yyy \n          keyword = value.toLowerCase(); \n        } \n      } \n      const result: (string | { \n        isKey: true, \n        value: string, \n      })[] = []; \n      VARS_OPTIONS.forEach((option) => { \n        if (keyword && !option.toLowerCase().includes(keyword)) { \n          return; \n        } \n        if (isKey) { \n          result.push({ \n          value: `${key}=${option}`, isKey: true,\n        });\n        } else {\n          result.push(option);\n        }\n      });\n      ctx.body = result;\n    });\n\n    // For `test-rules-hint://xxx` and `whistle.test-rules-hint://xxx`\n    // If `pluginVars.hintUrl` is not configured, `%test-rules-hint[.key]=xxx` will also be effective\n    router.get('/cgi-bin/get-hints', (ctx) => {\n      const { protocol, value } = ctx.query;\n        if (!protocol || typeof protocol !== 'string' || typeof value !== 'string') {\n          return;\n        }\n        const isVar = protocol.startsWith('%');\n        // This situation will not actually occur unless the `pluginVars.hintUrl` configuration is deleted\n        if (isVar) { \n          return; \n        } \n        const isLong = protocol.startsWith('whistle.'); \n        const prefix = isLong ? 'long-' : 'short-'; \n        const keyword = value.toLowerCase(); \n        const result: string[] = []; \n        HINTS_OPTIONS.forEach((option) => { \n        if (`${prefix}${option.toLowerCase()}`.includes(keyword)) { \n          result.push(`${prefix}${option}`); \n        } \n        }); \n        ctx.body = result; \n      }); \n    }; \n\n    ```\n9. Effect: \n\n    <img src=\"/img/rules-hint1.png\" width=\"300\" /> \n\n    <img src=\"/img/rules-hint2.png\" width=\"300\" /> \n\n    <img src=\"/img/plugin-vars-hint-url1.png\" width=\"300\" />\n\n## Interface Extensions\nThe Whistle plugin system supports extending the following interface modules:\n- **Network Module**\n- Data Table Column Extensions\n- Context Menu Extensions\n- Details Panel Tab Extensions\n- **Rules Module** Context Menu\n- **Values Module** Context Menu\n- **Plugins Module** Context Menu\n\n1. Creating a Sample Plugin\n    ```sh\n    # Create a plugin directory\n    mkdir whistle.test-ui-ext && cd whistle.test-ui-ext\n\n    # Initialize a blank plugin\n    lack init blank\n\n    # Run in development mode\n    lack watch\n    ```\n2. Edit `whistleConfig`: https://github.com/whistle-plugins/examples/tree/master/whistle.test-ui-ext/package.json\n3. After executing, you should see the `test-ui-ext` plugin in the Whistle plugin list.\n    \n    ![Interface Extension Image](/img/ui-ext.png)\n\n#### 1. Network Table Column Extension\n**Configuration Example**:\n```js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"networkColumn\": {\n      \"name\": \"Referer\", // Column display name (required)\n      \"key\": \"req.headers.referer\", // Data field path (required)\n      \"iconKey\": \"\", // Icon field (optional)\n      \"showTitle\": true, // Whether to display a hover tooltip (optional)\n      \"width\": 120 // Column width (optional, default 120)\n    }\n    ...\n  }\n  ...\n}\n```\n\n**Advanced Data Processing**:\n1. Create `/public/webWorker.js`:\n    ```js\n    module.exports = function(session, next) {\n      const isGithub = /^https?:\\/\\/github\\.com\\//.test(session.url);\n      next({\n        testWebWorker: 'custom_value',\n        style: isGithub ? {\n          color: '#fff',\n          fontStyle: 'italic',\n          bgColor: 'red'\n        } : null\n      });\n    };\n    ```\n2. Configure webWorker:\n    ``` js\n    {\n      \"whistleConfig\": {\n        \"networkColumn\": {\n          \"name\": \"Test\",\n          \"key\": \"customData.testWebWorker\" // Access webWorker return data\n          ...\n        },\n        \"webWorker\": \"/public/webWorker.js\"\n      }\n    }\n    ```\n\n#### 2. Details Panel Tab Extension\n```js\n{\n  \"whistleConfig\": {\n  \"inspectorsTab\": {\n    \"name\": \"Custom Tab\", // Tab display name\n    \"action\": \"/public/tab.html\", // Function page\n    \"icon\": \"data:image/png;base64,...\", // Icon (optional)\n    \"req\": { // Request sub-Tab \n      \"name\": \"Req SubTab\", \n      \"action\": \"/public/req-tab.html\" \n    }, \n    \"res\": { // Response sub-Tab \n      \"name\": \"Res SubTab\", \n      \"action\": \"/public/res-tab.html\" \n    } \n  }, \n  \"composerTab\": { /* Same as inspectorsTab */ }, \n  \"toolsTab\": { /* Same as inspectorsTab */ } \n  }\n}\n```\n\n- API callable by Tab page: https://github.com/avwo/whistle/blob/master/assets/tab.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 3. Network Context Menu Functionality\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"networkMenus\": [\n      {\n        \"name\": \"Network Menu1\",\n        \"action\": \"/public/network-menu.html\",\n        \"required\": false, // Defaults to false. In list mode, whether to disable this item in the context menu for items other than list items\n        \"requiredTreeNode\": false, // Defaults to false. In tree mode, whether to disable this item in the context menu for items other than list items\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Network Menu2\", // Menu item name\n        \"action\": \"/public/network-menu.html\", // Function page\n        \"required\": true, // Defaults to false. In list mode, whether to disable this item in the context menu for items other than list items\n        \"requiredTreeNode\": true, // Defaults to false. In tree mode, whether to disable this item in the context menu for items other than list items\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  } \n  ...\n}\n```\n- API callable by the context menu's `action` page: https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 3. Rules Context Menu Functionality\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"rulesMenus\": [\n      {\n        \"name\": \"Rules Menu1\", // Menu item name\n        \"action\": \"/public/rules-menu.html\", // Function page\n        \"required\": false, // Defaults to false. Should the context menu for items other than list items in list mode be disabled?\n        \"requiredTreeNode\": false, // Defaults to false. Should the context menu for items other than list items in tree mode be disabled?\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Rules Menu2\",\n        \"action\": \"/public/rules-menu.html\",\n        \"required\": false, // Defaults to false. Should the context menu for items other than list items in list mode be disabled?\n        \"requiredTreeNode\": false, // Defaults to false. Should the context menu for items other than list items in tree mode be disabled?\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- API callable by the `action` page of the context menu: https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 4. Values context menu functionality\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"valuesMenus\": [\n      {\n        \"name\": \"Values Menu1\",\n        \"action\": \"/public/values-menu.html\",\n        \"required\": false, // Defaults to false. In list mode, disable the context menu for items that are not list items.\n        \"requiredTreeNode\": false, // Defaults to false. In tree mode, disable the context menu for items that are not list items.\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Values Menu2\",\n        \"action\": \"/public/values-menu.html\",\n        \"required\": false, // Defaults to false. Should the context menu for items other than list items in list mode be disabled?\n        \"requiredTreeNode\": false, // Defaults to false. Should the context menu for items other than list items in tree mode be disabled?\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- Context menu `action` page API: https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n#### 5. Plugins Context Menu Functionality\n``` js\n{\n  ...\n  \"whistleConfig\": {\n    ...\n    \"pluginsMenus\": [\n      {\n        \"name\": \"Plugins Menu1\",\n        \"action\": \"/public/plugins-menu.html\",\n        \"required\": false, // Defaults to false. Should this item be disabled in the context menu for items other than list items in list mode?\n        \"requiredTreeNode\": false, // Defaults to false. Should this item be disabled in the context menu for items other than list items in tree mode?\n        \"urlPattern\": \"\"\n      },\n      {\n        \"name\": \"Plugins Menu2\",\n        \"action\": \"/public/plugins-menu.html\",\n        \"required\": false, // Defaults to false. Should this item be disabled in the context menu for items other than list items in list mode?\n        \"requiredTreeNode\": false, // Defaults to false. Should this item be disabled in the context menu for items other than list items in tree mode?\n        \"urlPattern\": \"\"\n      }\n    ],\n    ...\n  }\n  ...\n}\n```\n- The `action` page of the context menu can call API: https://github.com/avwo/whistle/blob/master/assets/menu.html\n- `window.whistleBridge` API: https://github.com/avwo/whistle/blob/master/biz/webui/htdocs/src/js/bridge.js\n\n## Special path\n\nWhistle provides a dedicated path format for directly requesting plug-in interfaces under various types of pages, as well as paths that can only be accessed normally in the Whistle environment.\n\n#### 1. Direct access to the Whistle interface\nIn the Whistle interface or plug-in management interface, you can directly access the plug-in interface using the following formats:\n```\npath/to\n```\n\n- This path will automatically resolve to `/whistle.xxx/path/to`\n- It is recommended to use relative path format to avoid using absolute paths directly `/path/to`\n\n#### 2. Regular page access\nIf you need to call the plug-in interface in a normal web page that is not Whistle, use the following special path format:\n```\n/.whistle-path.5b6af7b9884e1165./whistle.xxx/path/to\n```\n\n- When requests arrive at the plug-in, they will be standardized to: `/whistle.xxx/path/to`\n- Prefix `/.whistle-path.5b6af7b9884e1165./` is a dedicated identifier for Whistle, used to mark internal request routing\n\n#### 3. Environment adaptive path conversion\nImplementing uses different paths when passing or not through Whistle proxy, but ensuring that the backend service receives a unified path:\n``` txt\n/_WHISTLE_5b6af7b9884e1165_/\n```\n\n- Original request: `https://www.example.com/.../_WHISTLE_5b6af7b9884e1165_/path/to?query`\n- After Whistle, it is converted to: `https://www.example.com/.../path/to?query`\n\n#### illustrate:\n- `xxx` for your plugin name\n- `path/to` is the specific path to the plug-in interface\n- Hash value in a special path `/.whistle-path.5b6af7b9884e1165./` is a fixed identifier\n\n## Other whistleConfig Configurations\n``` js\n{\n  ...,\n  \"homepage\": '', // Plugin help page address\n  \"pluginHomepage\": '', // Custom plugin operation page address, defaults to `/plugin.xxx/`\n  \"whistleConfig\": {\n    \"hideLongProtocol\": false, // Whether to hide the plugin's long protocol. If set to true, the plugin's protocol will be displayed as deleted when configured in the Rules interface.\n    \"hideShortProtocol\": false, // Whether to hide the plugin's short protocol. If set to true, the plugin's protocol will be displayed as deleted when configured in the Rules interface.\n    \"priority\": 0, // The default value is 0. Plugin priority is sorted from oldest to newest by update time (older updates have higher priority). This field can be used to adjust the priority order.\n    \"favicon\": '', // The icon on the plugin tab. This can be the plugin's relative path `/public/xxx.png` Or an absolute path `https://xxx` or `data:image/png;base64,xxx`\n    \"registry\": '', // Plugin's npm registry\n    \"noOption\": false, // If the operation interface does not exist, set this to true to gray out the Option button\n    \"enableAuthUI\": false, // Whether the plugin's auth hook applies to the plugin operation interface (use with caution)\n    \"tunnelKey\": '', // String or string array. If you need to continue to proxy the request through the tunnel to another HTTP proxy, you can specify which request headers to pass through\n    ...\n  },\n  ...\n}\n```\n## Plugin Page Internal Path Specifications\nPlugin page URLs typically follow the following format:\n- `/plugin.xxx/path/yyy.html`\n- `/whistle.xxx/path/yyy.html`\n\nThe **root directory** is `/plugin.xxx/` or `/whistle.xxx/`, so:\n\n✅ It is recommended to use relative paths instead of absolute paths (e.g., /path/to).\n\n---\n\n#### **Relative Path Usage Examples**\n\n**Scenario 1: Page Located in the Plugin Root Directory**\n**Page Address**: `/plugin.xxx/yyy.html`\n**Correct Method**:\n- `./path/to`\n- `path/to`\n**Incorrect Method**:\n- ❌ `/path/to` (Absolute path, may result in access errors)\n\n---\n\n**Scenario 2: Page Located in a Subdirectory**\n**Page Address**: `/plugin.xxx/a/b/c/yyy.html`\n**Correct Method**:\n- `../../../path/to` (Goes back three levels and then to the target path)\n\n**Incorrect Method**:\n- ❌ `/path/to` (Absolute path, may result in access errors)\n- ❌ `path/to` (Based on the current directory `a/b/c/`) (This can lead to path errors)\n\n---\n\n**Best Practices**\n1. **It is recommended to use `./` prefix** to explicitly indicate relative to the current directory (e.g., `./assets/style.css`).\n\n2. **Avoid hard-coding paths starting with `/`** to prevent invalid paths in different deployment environments.\n\n3. **Test path references**: Verify that resources load properly both locally and in production environments.\n\n---\n\n**Frequently Asked Questions**\n\n❓ **Q: Why can't absolute paths be used?**\n\n📌 **A**: Plugins may be deployed in different environments (e.g., testing/production/embedded in other projects). Absolute paths may cause resource loading failures.\n\n\n❓ **Q: Some build tools, such as Vite, resolve resource paths based on the base setting by default. If base is set to `/`, `../` may be optimized to `/`. How can this be resolved?**\n\n📌 **A**: Try changing the base to `./`:\n  ``` js\n  // vite.config.js\n  export default defineConfig({\n    base: './', // Force relative paths\n  });\n  ```\n  > Recommended to optimize this with the backend routing configuration\n\n## Publishing Plugins\nThe Whistle plugin publishing method is exactly the same as a regular NPM package. Simply follow the standard NPM publishing process:\n\n1. Log in to NPM (or your enterprise private repository; the latter requires setting `npm config set registry https://xxx` for your enterprise private repository):\n    ``` sh\n    npm login\n    ```\n2. Execute the publish command in the plugin root directory:\n    ``` sh\n    npm publish\n    ```\n\n## References\n1. Example source code repository: https://github.com/whistle-plugins/examples\n2. Scaffolding command: https://github.com/avwo/lack\n"
  },
  {
    "path": "docs/en/docs/extensions/npm.md",
    "content": "# NPM Module\nWhistle can be integrated into your project as an NPM module. It's strongly recommended that you run Whistle in a separate child process to avoid interfering with the main process and to facilitate management.\n\n## Install dependencies\n``` sh\nnpm i --save whistle pfork\n```\n\n## Standalone Service Mode\n1. Create the Whistle startup script (`/lib/whistle.js`):\n    ``` js\n    const startWhistle = require('whistle');\n\n    module.exports = (options, callback) => {\n      startWhistle({ port: options.port }, () => callback());\n    };\n    ```\n    > For startup parameters such as disabling plugins, customizing plugin directories, and modifying storage directories, refer to: https://github.com/avwo/whistle/blob/master/index.d.ts\n2. Start the Whistle process code:\n    ``` js\n    const { fork } = require('pfork');\n    const path = require('path');\n\n    const script = path.join(__dirname, './whistle.js');\n    const options = {\n      script,\n      port: 8080,\n    };\n    const forkWhistle = (retry) => {\n      fork(options, (err, result, child) => {\n        if (err) {\n          // Initialization error, exit the service directly\n          if (!retry) {\n            throw err;\n          }\n          // Retry error, delay 100 milliseconds before retrying\n          return setTimeout(forkWhistle, 100);\n        }\n        // Child process automatically retry after exit\n        child.once('close', () => forkWhistle(true));\n      });\n    };\n\n    forkWhistle();\n    ```\n\nStarting Whistle this way is the same as starting Whistle independently or as a Whistle client from the command line.\n\n## Internal Service Mode (Recommended)\n\n1. Create the Whistle startup script (`/lib/whistle.js`):\n    ``` js\n    const startWhistle = require('whistle');\n    const http = require('http');\n\n    let curPort = 30013;\n\n    const getPort = (callback) => {\n      const server = http.createServer();\n      server.on('error', () => {\n        if (++curPort % 5 === 0) {\n          ++curPort;\n        }\n        getPort(callback);\n      });\n      server.listen(curPort, '127.0.0.1', () => {\n        server.removeAllListeners();\n        server.close(() => callback(curPort));\n      });\n    };\n\n    module.exports = (options, callback) => {\n    // Get a random port\n    getPort((port) => {\n    // Start at a random port Whistle, after successful startup, passes startup parameters to the parent process.\n    const options = {\n      port,\n      host: '127.0.0.1',\n      storage: 'xxx', // It is recommended to set a new storage directory to ensure that it does not conflict with the directories of other instances\n    };\n    startWhistle(options, () => callback(options));\n    });\n    };\n    ```\n    > Reference: https://github.com/Tencent/nohost/blob/master/lib/whistle.js\n    >\n    > Whistle will\n2. Start the Whistle process method (`/lib/fork.js`):\n    ``` js\n    const { fork } = require('pfork');\n    const path = require('path');\n\n    const script = path.join(__dirname, './whistle.js');\n    const options = {\n      script,\n    };\n\n    module.exports = () => {\n      return new Promise((resolve, reject) => {\n        fork(options, (err, result) => {\n          if (err) {\n            return reject(err);\n          }\n          resolve(result);\n        });\n      });\n    };\n    ```\n    > Reference: https://github.com/Tencent/nohost/blob/master/lib/main/whistleMgr.js\n3. Forward the specified request in the project to Whistle:\n    ``` js\n    const { createServer } = require('http');\n    const forkWhistle = require('./fork');\n\n    const server = createServer(async (req, res) => {\n      try {\n      const { host, port } = await forkWhistle();\n      // Forward the request to host, port\n      } catch (e) {\n      // handle errors\n      }\n    });\n\n    server.listen(8010);\n    ```\n    > Reference: https://github.com/Tencent/nohost/blob/master/lib/index.js#L160\n\nStarted in this way Whistle uses a random port and is started on demand. External requests do not access Whistle directly but are forwarded through the project's services. This provides more flexibility and is the recommended method for project integration.\n\n## Related Documentation\n\n1. For all Whistle startup parameters, see the source code type definitions: https://github.com/avwo/whistle/blob/master/index.d.ts\n2. pfork: https://github.com/avwo/pfork\n3. Complete example: https://github.com/Tencent/nohost\n"
  },
  {
    "path": "docs/en/docs/extensions/usage.md",
    "content": "# Using Plugins\n\n## Installing Plugins\n\n1. Click the Plugins tab in the left navigation bar.\n2. Click the Install button at the top.\n3. Enter the plugin name in the pop-up window (multiple plugins can be installed simultaneously):\n   - Separate multiple plugins with spaces or line breaks.\n   - You can specify a custom npm mirror:\n   - Simply add `--registry=mirror_url` after the plugin name.\n   - Or select a previously used mirror from the drop-down list.\n\n<img width=\"1000\" alt=\"install plugins\" src=\"/img/install-plugins.png\" />\n\n> Redundant content in the text box will not affect the operation result. Whistle will automatically filter and extract only the plugin name and registry installation information. If the dialog box does not show an Install button, please reinstall the global Node.js: https://nodejs.org/\n\nAfter successful installation, wait a moment for the newly installed plugin to appear in the plugin list:\n\n<img width=\"1000\" alt=\"plugins\" src=\"/img/plugin-list.png\" />\n\n## Custom Protocols\n\nEach plugin can register two protocol types, configured the same way as regular protocols:\n``` txt\n# Long Protocol\npattern whistle.plugin-name://value [inludeFilter://pattern1 ... excludeFilter://pattern2 ...]\n# Short Protocol\npattern plugin-name://value [inludeFilter://pattern1 ... excludeFilter://pattern2 ...]\n```\n\n## Hooks\n\nWhistle connects requests matching plugin rules with the corresponding plugin hooks, enabling the following functionality:\n> For specific functionality of plugin protocols, refer to the help documentation for each plugin. Plugins can also choose to hide these protocols.\n1. Automatically generate and issue HTTPS certificates\n2. Authenticate proxy requests, requiring users to provide credentials for access\n3. Real-time access to metadata such as request headers and response status codes\n4. Dynamically set rules for requests\n5. Forward requests to plugins, giving them full control over the request processing flow (requires matching the short protocol for this to work)\n\n## Pipe Functionality\nSome request/response content may be encrypted or Protobuf serialization prevents you from viewing the plaintext. You can view and modify the content in the following two ways:\n1. **Plugin Full Control**\n   - Directly forwards the request to the plugin\n   - The plugin has complete control over the processing flow\n   - Notes:\n   - Whistle's built-in rules will be ineffective\n   - The plugin must implement all processing logic on its own\n   - To display decrypted content in the packet capture interface, the plugin must call the Whistle API to return the data\n2. **Pipeline Streaming (Recommended)**\n   - Establish a processing pipeline using the pipe protocol\n   - Request/Response Flow:\n   - Upon entering Whistle: Plugin decrypts\n   - Upon leaving Whistle: Plugin encrypts\n   - Advantages:\n   - Maintains plaintext request characteristics\n   - Full support for Whistle's built-in rules\n   - Plaintext can be viewed directly in the packet capture interface\n\n**Technical Notes:**\n- The pipe streaming feature is optional. Please refer to the documentation for each plugin for support.\n- Requests processed in Option 2 are identical to normal plaintext requests\n\n## User Interface\n\n#### Accessing the Plugin User Interface\nIn Whistle In the plugin management panel, you can access plugin functionality in any of the following ways:\n1. Click the `Options` button to the right of a plugin entry\n2. Click the plugin name directly in the plugin list\n\n#### Interface Features\n**Display Format:** Modal Dialog Box or Standalone Tab\n\n**Core Capabilities:**\n1. Provides visual configuration and management capabilities\n2. Supports real-time interaction with the Whistle core\n3. Displays plugin status, request statistics, log information, etc.\n\n#### Technical Description\n1. A plugin is essentially an HTTP server that interacts directly with Whistle\n2. Each plugin implements its own user interface functionality\n3. A plugin may choose not to provide a user interface (providing only backend services or command-line operations)\n\n### Extending the Whistle Interface\n\nPlugins can enhance the Whistle user interface in the following ways:\n\n#### Network Extensions\n1. Capture List Context Menu\n    > Add custom actions to the right-click menu of a capture list item\n2. Main View Tabs\n    > Create a first-level function tab in the main panel on the right.\n2. Inspectors Analysis Tool Extension\n    > Add the following to the Inspectors panel:\n    > - Second-level function tab\n    > - Third-level detail view tab\n3. Composer Request Builder Extension\n    > Add a second-level function tab to the Composer tool area\n4. Tools Toolset Extension\n    > Add a second-level function tab to the Tools ribbon\n\n#### Rules Extension\nLeft-side List Context Menu\n> Add a function item to the right-click menu of the rule file list on the left.\n\n#### Values Extension\nLeft-side List Context Menu\n> Add a function item to the right-click menu of the variable file list on the left.\n\n**Implementation Notes:**\n- Each plugin can select the appropriate extension location based on its functional requirements.\n- For specific extension functions and implementation methods, please refer to the development documentation of each plugin.\n- Unused extension points will not affect the plugin's basic functionality.\n\n## Plugin Built-in Rules\n\nPlugins support extending system functionality through the following rule files:\n\n1. **Global Rules (rules.txt)**\n   - Automatically loaded when: During plugin initialization\n   - Scope: Global requests\n   - Priority: Lower than rules configured in the Rules interface\n   - Typical use: Plugin default rule configuration\n2. **Private Rules (_rules.txt)**\n   - Triggering Condition: Requests matching the plugin's custom protocol\n   - Effective Phase: Request processing flow\n   - Execution Order: Applied after global rules\n   - Typical use: Request pre-processing/rewriting\n3. **Response Phase Rules (resRules.txt)**\n   - Triggering Condition: Requests matching the plugin's custom protocol\n   - Effective Phase: Response processing flow\n   - Execution Order: Applied after global rules\n   - Typical use: Response post-processing/rewriting\n\nThe plugin supports a flexible built-in rule configuration mechanism:\n- **Optionality:** All built-in rules are optional\n- **On-demand Configuration:** Plugins only need to declare necessary rules\n- **Viewing Rules:** All rules configured in the plugin can be viewed through the Rules button in the plugin management interface\n> Note: Plugins without configured rules can still function normally.\n\n## Updating and Uninstalling Plugins\n\n**In Whistle In the plugin management interface:** \n- **Update a plugin:** Click the `Update` button to the right of the plugin entry\n- **Uninstall a plugin:** Click the `Uninstall` button to the right of the plugin entry\n"
  },
  {
    "path": "docs/en/docs/faq.md",
    "content": "# FAQ\n\nPlease file an [issue](https://github.com/avwo/whistle/issues/new) if you encounter any issues or suggestions.\n\n## Q: Why does the capture list show `Tunnel to`? {#tunnel-to}\n\nThis typically occurs in the following scenarios. Please identify your specific situation and follow the corresponding solution:\n\n1.  **Regular TCP connection (cannot be parsed as HTTP/HTTPS request)**\n2.  **HTTPS Capture Not Enabled (Most Common)**\n    *   **Cause**: You have not installed the root certificate or enabled the `Enable HTTPS` option. As a result, Whistle cannot decrypt HTTPS traffic and can only display it as an encrypted tunnel.\n    *   **Solution**: Please install the root certificate and enable the `Enable HTTPS` option.\n        *   **Whistle Client (GUI)**: Please follow the installation and configuration guide in the official repository: [https://github.com/avwo/whistle-client](https://github.com/avwo/whistle-client)\n        *   **Command-Line Version**: Please refer to the official repository for instructions: [https://github.com/avwo/whistle](https://github.com/avwo/whistle)\n        *   **Mobile Device Capture**: For configuration steps on mobile devices, please see: [Mobile Capture](./mobile)\n\n3.  **Capture Error (`captureError`)**\n    *   **Cause**: An error occurred during the HTTPS decryption process.\n    *   **Solution**: Please see the dedicated question below for troubleshooting steps: [Q: How to resolve `captureError`?](#capture-error)\n\n4.  **Request to an IP Address**\n    *   **Cause**: The request uses a literal IP address (e.g., `https://192.168.1.1`) instead of a domain name. Most clients do not send SNI information for such requests, preventing Whistle from automatically recognizing them as HTTPS.\n    *   **Solution**: You need to explicitly configure a rule to tell Whistle to capture HTTPS for that IP. For details, please see the question below: [Q: How to capture HTTPS requests made to an IP address?](#capture-ip)\n\n## Q: Why does `captureError` appear in the packet capture list? {#capture-error}\n1. The client making the request does not have the root certificate installed. To install it, refer to the following:\n   - PC: [Install the root certificate](./)\n   - Mobile: [Install the root certificate](./mobile)\n2. SSL pinning issue\n   - HTTPS requests to the domain are not decrypted: `domain-name disable://capture` or only for requests from a specific client: `domain-name disable://capture includeFilter://reqH.user-agent=/xiaomi/i`\n   - Run the client on a system or emulator that can circumvent SSL pinning\n   - Find other workarounds: https://blog.csdn.net/chiehfeng/article/details/134033846\n3. The system-trusted root certificate is not available to Firefox by default; you need to configure a certificate for Firefox separately.\n    > **Solution 1: Install a certificate separately for Firefox**\n    >\n    > In Firefox settings:\n    > - Go to Options > Privacy & Security > Certificates\n    > - Click \"View Certificate\" → \"Certificate Authority\"\n    > - Import the downloaded .cer file\n    > - Check all \"Trust this CA\" options\n    >\n    > **Solution 2: Force Firefox to use the system certificate (Recommended)**\n    >\n    > - Search for preferences: security.enterprise_roots.enabled\n    > - Change the value to true\n    > - Restart the browser for the changes to take effect\n\n## Q: Why are HTTPS requests made to an IP address still not decrypted after installing the root certificate and enabling `Enable HTTPS`?{#capture-ip}\n\nThis is because for HTTPS requests directly initiated using an IP address, the client typically does not send SNI (Server Name Indication) information during the handshake phase. The absence of SNI makes it difficult for Whistle to quickly determine whether it is an HTTPS request that needs decryption, and since most such requests are ordinary TCP traffic, Whistle may treat them as regular TCP traffic. You can configure rules to explicitly instruct Whistle to recognize requests to specific IPs as HTTPS and perform decryption capture:\n\n```txt\nip[:port] enable://captureIp\n\n# Or\nip[:port] enable://capture\n```\n\n## Q: How do I configure HTTPS requests with mutual authentication (mTLS)?\n\nClient certificate settings reference: [@clientCert://](./rules/@)\n\n## Q: How do I view Whistle runtime logs?\n1. View the error log in Network > Tools > Server.\n2. The log file for exceptions that caused the process to crash is: `~/.WhistleAppData/whistle.log`\n\n## Q: How do I start multiple Whistle instances simultaneously?\nRunning multiple instances requires the following:\n- Use a separate directory for each instance\n- Configure different listening ports\n``` sh\nw2 start\n2 start -p 8100 -S storageName2\n```\n\n## Q: How can I access Whistle directly without going through a proxy and preventing it from being treated as an internal request?\nBy default, Whistle treats all requests sent to the proxy port (e.g., 127.0.0.1:8899) as internal management requests. You can use the `/-/` path prefix to bypass internal request detection. For example:\n1. `http://127.0.0.1:8899/-/xxx`: Whistle automatically converts the request to a normal request `http://127.0.0.1:8899/xxx`\n2. Configure a rule to forward the request to the target URL:\n\n``` txt\nhttp://127.0.0.1:8899/xxx https://www.test.com/xxx\n```\n\n## Q: How do Rules support multiple selections?\n\nOpen the Settings dialog box in the Rules interface and check `Use multiple rules`.\n\n## Q: How do I match rules based on request content?\n\nUse filters:\n- [includeFilter](./rules/includeFilter)\n- [excludeFilter](./rules/excludeFilter)\n\n## Q: How do I debug WebSocket/TCP requests?\n\n1. Using the UI: [Network](./gui/network#websocket)\n2. Using rules: [frameScript](./rules/frameScript)\n3. Using plugins: [Plugin Development](./extensions/dev)\n\n## Q: Why can't I open HTTPS pages on iOS even after installing the root certificate?\n\nCheck whether \"Full Trust\" is set: Settings → General → About This Device → Certificate Trust Settings\n\n## Q: Why can't I open HTTPS pages on Android even after installing the root certificate?\n1. SSL Pinning Issue\n   - Do not decrypt HTTPS requests for this domain: `domain disable://capture` or only for requests from a specific client: `domain disable://capture includeFilter://reqH.user-agent=/xiaomi/i`\n   - Run the client on a system or emulator that can circumvent SSL Pinning\n   - Find other circumvention measures: https://blog.csdn.net/chiehfeng/article/details/134033846\n2. If this is your company's app, refer to the [Android Development Documentation](https://developer.android.com/training/articles/security-config#base-config) to enable trust for user-defined root certificates.\n\n## Q: How do I set a username and password for requests forwarded by Whistle? 1. Whistle internal request authentication: `w2 start -n username -w password` or develop your own plugin (./extensions/dev) to prevent unauthorized access to rules and configurations.\n2. Proxy request permission control: Requires the plugin [whistle.proxyauth](https://github.com/whistle-plugins/whistle.proxyauth) or develop your own plugin (./extensions/dev).\n\n## Q: How do I add a custom certificate? {#custom-certs}\n\nGo to the certificate management page\n1. Click HTTPS > View Custom Certs > Upload in the top menu bar\n2. Upload the certificate file\n   - Certificate file: Must use the `.crt` suffix\n   - Private key file: Must use the `.key` suffix\n   > File name requirements:\n   >\n   > Standard domain name certificate\n   >\n   > `example.com.crt` ↔ `example.com.key`\n   >\n   > Root certificate (must be named precisely)\n   >\n   > `root.crt` ↔ `root.key`\n\n## Q: Version update issue {#update}\n> For the client version, simply download and install the latest version: https://github.com/avwo/whistle-client\n\n**Command line version update:**\n``` sh\nnpm i -g whistle && w2 restart\n```\n> If you encounter slow installation or installation failures, try changing the mirror: `npm i -g whistle --registry=https://registry.npmmirror.com && w2 restart`\n>\n> If you encounter permission issues, add `sudo`:\n>\n> ``` sh\n> sudo npm i -g whistle\n> w2 restart\n> ```\n\nAfter restarting, the Whistle version displayed in the command line may not match the currently installed version. This may be due to a Node.js version update that changed your PATH.\n\n**Solution:**\n1. Verify the version:\n  ``` sh\n  w2 -V\n  ```\n2. Find the command path (common to all systems)\n  ``` sh\n  which w2 # Linux/Mac\n  where w2 # Windows\n  ```\n3. Clear conflicts\n  ``` sh\n  # Delete the old version (based on the path found in the previous step)\n\n  rm -f /usr/local/bin/w2 # Example path\n\n  # Windows example (requires administrator privileges)\n  del \"C:\\Program Files\\nodejs\\w2.cmd\"\n  ```\n4. Resume the operation and, after completion, execute `w2 -V` to check if the version is updated:\n   - If the output version still has issues, repeat the above steps.\n   - If the `w2` command cannot be found, manually configure the PATH.\n\n\n## Q: How to filter out frequent polling requests in the packet capture interface?\n\nFrequent polling requests (such as heartbeat detection or status reporting) can flood the screen and interfere with the analysis of primary requests. You can quickly hide them by following these steps:\n\nLocate any polling request in the packet list, then right-click and select the appropriate filtering rule from the context menu:\n- `Settings / Exclude All Matching Hosts`: Hides all requests from the corresponding domain (effective only for the current browser session).\n- `Settings / Exclude All Matching URLs`: Hides all requests matching the current URL (excluding query parameters, and effective only for the current browser session).\n\n## Q: How do I modify Whistle documentation?\n\nWhistle documentation source file address: https://github.com/avwo/whistle/tree/master/docs\n\nStart the documentation server locally:\n``` sh\nnpm run docs:dev\n```\n## Q: How can I report an issue?\n\nNew issue: https://github.com/avwo/whistle/issues\n"
  },
  {
    "path": "docs/en/docs/getting-started.md",
    "content": "# Getting Started with Whistle\n\nOnce installed, launch Whistle according to your installation type:\n*   **Desktop Application Version**: Launch the desktop app directly.\n*   **Command-Line Version**: Access `http://local.whistlejs.com` in your browser.\n\nThis will open the main Whistle **Web Interface**.\n\n![Whistle Web Interface](/img/whistle.png)\n\nThe main interface provides four core functional areas:\n*   **Network**: The main panel for real-time packet capture, analysis, and tools like request replay/compose.\n*   **Rules**: The interface for configuring rules to modify requests and responses.\n*   **Values**: The interface for managing configurable values (which can be referenced as variables in rules).\n*   **Plugins**: The interface for managing plugins.\n\n## Interface Examples\n\n1.  **Replay a Request**\n    > Select a request in the Network list and click the `Replay` button in the toolbar, or right-click the request and choose `Actions -> Replay`.\n    ![Replay Request](/img/replay-req.png)\n\n2.  **Compose or Edit a Request**\n    > Select a request in the Network list and click the `Edit` button in the toolbar, or right-click the request and choose `Actions -> Edit Request`.\n    ![Edit Request](/img/edit-req.png)\n\n## Introduction to Rules\n\n![Rules Interface](/img/rules.png)\n\nWhistle allows you to efficiently modify and debug network requests and responses by configuring concise rules. All rules are based on a core syntax structure:\n\n```txt\npattern operation [lineProps...] [filters...]\n```\n\n**Interpretation**: When a request's URL matches the `pattern`, Whistle performs the action defined by the `operation` on it. Additionally, you can:\n*   Use the optional `lineProps` to set special properties for the rule on the same line.\n*   Use the optional `filters` to perform a secondary, more precise筛选 (filtering) on the already matched requests.\n\n| Name | Description & Examples |\n| :--- | :--- |\n| **`pattern`** | An expression to match request URLs (supports domain, path, regex, wildcard).<br/>1.  `www.test.com` (Domain)<br/>2. `https://www.test.com` (Domain with protocol)<br/>3. `www.test.com:8080` (Domain with port)<br/>4. `*.test.com` (Domain with wildcard)<br/>5. `www.test.com/path` (Path)<br/>6.  `https://www.test.com/path` (Path with protocol)<br/>7.  `www.test.com/path?query` (Domain with query string)<br/>8. `/api/i` (Regular Expression)<br />9. ``^**.test.com/path/index.*.js`` (Wildcard)<br />10. ``^https://**.test.com/path/index.*.js`` (Wildcard with protocol) |\n| **`operation`** | The specific action to execute, formatted as `protocol://value`.<br/>1. `reqHeaders://x-proxy=whistle` (Set request headers)<br/>2. `file:///User/xxx` (Map to a local file) |\n| **`lineProps`** | Additional configurations that apply only to the current rule line, used for behaviors like increasing priority or refining matching (supports combination).<br/>1. `lineProps://important` (Increase the rule's priority)<br/>2. `lineProps://safeHtml` (Ensure the response content is not a JSON object)<br/>3. `lineProps://proxyHost` (Allow both `proxy` and `host` protocols to take effect) |\n| **`filters`** | Perform more precise filtering based on the `pattern` match (supports combination).<br/>1. `includeFilter://b:cmdname=xxx` (Keep requests whose body contains `cmdname=xxx`, case-insensitive)<br/>2. `excludeFilter://reqH.user-agent=/android/i` (Exclude requests whose User-Agent contains 'android', case-insensitive) |\n\nLet's start with some common features to get an intuitive feel for basic Whistle rule usage. Detailed explanations will follow.\n\n## Local Replacement / Proxying\n\nDuring development or troubleshooting, it's often necessary to forward page, static resource, or API requests to a local environment or a specified test server. Whistle rules make this mapping and proxying convenient.\n\n```txt\n# 1. Forward page requests to a local dev server\nwww.example.com http://localhost:5173 excludeFilter://*/static excludeFilter://*/api\n\n# 2. Map static resources to a local directory\nwww.example.com/static file:///User/xxx/statics\n\n# 3. Forward API requests to a test environment server\nwww.example.com/api 10.1.0.1:8080\n```\n\n#### Rule Explanation\n\n| Rule | Effect | Notes |\n| :--- | :--- | :--- |\n| `www.example.com/static file:///User/xxx/statics` | Maps all requests under `www.example.com/static/…` to the local `/User/xxx/statics/…` directory. | Returns a 404 error if the corresponding local file does not exist. |\n| `www.example.com/api 10.1.0.1:8080` | Forwards all `www.example.com/api/…` requests to the test server `10.1.0.1:8080`. | The URL seen by the server remains unchanged; only the host and port are replaced. |\n| `www.example.com http://localhost:5173 excludeFilter://*/static excludeFilter://*/api` | Forwards all other requests for `www.example.com` (excluding `static` and `api` paths) to the local service `localhost:5173`. | This rule forwards both **HTTP and HTTPS** requests to `http://localhost:5173`. The server receives the forwarded URL. |\n\nBy adjusting the domain, local path, or test server address in the above rules according to your actual development environment, you can quickly achieve request forwarding to improve development and debugging efficiency.\n\n## Modifying Request/Response Content\n\nDynamically modify the content of requests and responses passing through Whistle.\n\n``` txt\n# Set or replace a request header x-client: Whistle\nwww.example.com/path/to reqHeaders://({\"x-client\":\"Whistle\"})\n\n# Modify request form data or JSON object\nwww.example.com/path/to reqMerge://({\"test\":123})\n\n# Set CORS response header Access-Control-Allow-Origin: *, excluding OPTIONS requests\nwww.example.com/path/to resCors://* excludeFilter://m:option\n\n# Inject script `alert(123)` at the end of the response page (supports HTML or JS type pages)\nwww.example.com/path/to  jsAppend://(alert(123))\n```\n\n## Remote Debugging: Inspecting DOM & Logs\n\nUse the built-in **weinre** and **log** protocols for remote page debugging.\n\n```txt\n# Simultaneously enable remote DOM debugging (weinre) and log capture (log)\nhttps://www.qq.com weinre://test log://\n```\n\n1.  **Access the weinre debugger**: After the rule is active, hover over the top menu `Weinre` and click the session name (e.g., `test`) to open the debugger interface.\n    <img src=\"/img/weinre-menu.png\" alt=\"Open weinre menu\" width=\"360\" />\n2.  **Start debugging**: Open the target page (e.g., `https://www.qq.com`) in your browser. Then, select that page in the weinre interface. You can now inspect **Elements**, **Console**, etc., just like using browser developer tools.\n    <img src=\"/img/weinre-main.png\" alt=\"weinre main interface\" width=\"800\" />\n3.  **View complete logs**: All page Console output and JavaScript errors will be displayed in the Whistle web interface under **Network → Right-side Tools → Console** tab.\n    ![log basic demo](/img/log-basic.gif)\n\n## Detailed Documentation\n1. **[Interface Features Details](./gui/network)**: Deep dive into all features of the Network panel and others.\n2. **[Complete Rules Configuration](./rules/rule)**: Reference all supported rule protocols and advanced usage.\n"
  },
  {
    "path": "docs/en/docs/gui/composer.md",
    "content": "# Composer Interface\n\nComposer is an HTTP request construction tool provided by Whistle. It can be used to quickly create, modify, and send custom requests. Its main purpose is to quickly edit captured data and simulate requests, meeting the needs of quickly locating interface issues during joint debugging.\n\n<img src=\"/img/composer.png\" alt=\"Composer Interface\" width=\"420\" />\n\nRight-click the send button:\n\n<img src=\"/img/right-click-send.png\" alt=\"Right-click the send button\" width=\"260\" />\n\n| Component                     | Function |\n| ------------------------ | ---- |\n| **History Button**         | Show or hide history |\n| **Select Method**       |   Select the request method, supporting various methods such as GET/POST/PUT   |\n| **URL Input Box**           | Edit or input the request URL |\n| **Params**               | Add, modify, or delete request parameters |\n| **Send Button**             | Execute the current request |\n| **Send Body Via File**  | Load request content from a local file for sending large request content |\n| **Replay Times**             | Set the number of replays, up to 100 times |\n| **Show History**             | Show history, same function as the icon button on the left |\n| **Rules** | Whether to enable the rules in the input box below |\n| **Whistle** | Whether to enable the rules configured in Whistle |\n| **Pretty** |   Format and display content   |\n| **Body** |   Whether to include the request body (methods like GET/HEAD/OPTIONS will automatically ignore the request body)   |\n| **HTTP/2** | Whether to use the HTTP/2 protocol |\n| **Import**  | Import request data |\n| **Export**  | Export request data |\n| **AsCURL** | Generate executable cURL commands |\n| **Rule Input Box** |   Custom rules, only effective for the currently constructed request   |\n| **Request Headers** |   Custom request headers   |\n| **Request Content** |   Custom request content    |\n\n## Saving Request/Response Data\n1. Automatically saved to Composer history when sending (up to 64 records saved)\n2. Right-click the captured data in the packet capture list and click Save to save\n"
  },
  {
    "path": "docs/en/docs/gui/console.md",
    "content": "# Console Interface\n\nThe Console interface is a visual display platform for [log rules](../rules/log), specifically used for monitoring and analyzing:\n- Page JavaScript errors\n- Console output such as `console.log` and `console.warn`\n\n<img src=\"/img/console.png\" alt=\"Console Interface\" width=\"360\" />\n\n| Function Area | Function Description |\n| -------------------- | ------------------------------------------------------------ |\n| **All logs** | Displays all log categories (corresponding to the id in `log://id`). Click a category to quickly filter logs for a specific page. |\n| **All levels** | Filter by log level (Debug/Log/Info/Warn/Error/Fatal) |\n| **Expand JSON Root** | Automatically expand the first level of the JSON data structure |\n| **ecord** | Start/stop/pause logging |\n| **Import** | Import historical log files (.json/.txt) |\n| **Export** | Export the current log |\n| **Clear** | Clear the current log |\n"
  },
  {
    "path": "docs/en/docs/gui/https.md",
    "content": "# HTTPS Dialog\n\nThe HTTPS Settings dialog box is used to configure HTTPS packet capture and certificate management. The interface is as follows:\n\n<img src=\"/img/https.png\" alt=\"HTTPS Dialog Box\" width=\"380\" />\n\n| Function/Option | Description | Notes |\n| ----------------------------------------- | ------------------------------------ | ------------------------------------------------- |\n| **Download RootCA** | Download the Whistle root certificate to your local computer | Used for initial installation or when the certificate expires |\n| **rootCA.xxx Selector** | Switch certificate formats: `.crt`, `.cer`, `.pem` | If installation of a certain certificate format fails, try another format |\n| **QR Code Address Selector** | Switch mobile certificate download address | Convenient for scanning and downloading certificates on mobile phones. See [Mobile Packet Capture](../mobile) for details |\n| **Enable HTTPS (Capture Tunnel Traffic)** | Enable HTTPS traffic decryption (requires root certificate installation) | Can also be controlled through rules: `enable://https` `disable://https` |\n| **Enable HTTP/2** | Enable HTTP/2 protocol support (enabled by default) | Can also be controlled via rules: `enable://http2` `disable://http2` |\n| **Custom Certs Settings** | View, Upload, or Delete User-Defined Certificates | Direct uploading of root certificates is not supported. If you need to customize the root certificate, you must place it in the custom certificate directory and restart Whistle for it to take effect. |\n\n## Custom Certs Settings {#custom-certs}\nClick the **Custom Certs Settings** button to open the custom certificate management panel. In this panel, you can upload server certificates used in your official business (commonly used to resolve SSL pinning detection issues).\n\nCertificate files must be uploaded in pairs, meaning each upload must include one key file and one certificate file. Supported file pairing formats are as follows (actual filenames can be customized, but extensions must meet the requirements):\n1. `.key` file + `.cer` file\n2. `.key` file + `.crt` file\n3. `.key` file + `.pem` file\n\nFor example:\n- `server.key` and `server.cer`\n- `mycert.key` and `mycert.crt`\n- `cert.key` and `cert.pem`\n\nPlease ensure that each certificate and key file pair matches in content; otherwise, certificate configuration may fail.\n"
  },
  {
    "path": "docs/en/docs/gui/network.md",
    "content": "# Network Interface\n\nInterface for viewing and managing captured packet data\n\n<img src=\"/img/network.png\" alt=\"Network Interface\" width=\"1000\" />\n\n## Bottom Search Box {#filter}\n\nThe search box supports various advanced filtering methods, allowing you to quickly locate specific types of requests by prefix:\n\n| Prefix | Filter Target | Example |\n| -------------- | ----------------------------------- | --------------------------------- |\n| `No Prefix` | Request URL | `example.com/api` `/abc=123/i` |\n| `m:pattern` | Request Method | `m:pos` `m:/get\\|post/i` |\n| `h:pattern` | Request/Response Header Raw Text | `h:image/` `h:/cookie:\\s*test=123/i` |\n| `H:pattern` | Request Header Host Field | `H:example.com` `H:/test/i` |\n| `b:pattern` | Request/response body original content | `b:\"error\":true` `b:/\\d{3}/` |\n| `i:pattern` | Client IP or server IP | `i:11.2` `i:/^11\\.2/` |\n| `s:pattern` | Response status code | `s:404` `s:/5\\d{2}/` |\n| `t:pattern` | Response header Content-Type | `t:json` `t:/html\\|xml/i` |\n| `mark:pattern` | Request URL manually marked in the right-click menu | `mark:example.com` `mark:/\\d{5}/` |\n| `app:pattern` | Filter by app name | `app:wechat` `app:chrome` |\n| `fc:pattern` | Request URL issued by Composer | `fc:/test/` `fc:www.test.com` |\n| `e:pattern` | Errored request URL | `e:timeout` `e:/abort/i` |\n| `style:pattern` | [style](../rules/style) Operation content | `style:italic` `style:/ita/i` |\n\n`pattern` is a **keyword** or **regular expression**. Use \"AND\" to search for multiple conditions:\n``` txt\nb:/\"success\":false/ m:POST s:200 H:api.example.com\n```\n\n## Settings\n**Exclude Filter** and **Include Filter** allow for refined filtering of captured data not displayed in the Network view. Multiple condition combinations are supported:\n\n| **Filter Type** | **Function** | |\n| ------------------ | ---------------------------------------- | ---- |\n| **Include Filter** | **Only retain** requests that meet the conditions (equivalent to a whitelist) | |\n| **Exclude Filter** | **Exclude** requests that meet the conditions (equivalent to a blacklist) | |\n\n1. Conditions within a single input box\n   - Separation method: Use spaces or newlines to separate multiple conditions\n   - Logical relationship: Conditions are ORed\n   > Example: `example.com api.test.com` → Matches requests from example.com or api.test.com\n2. Conditions between multiple input boxes\n   - Logical relationship: Conditions are ANDed\n   - Example:\n     - Include Filter input: `m:GET`\n     - Exclude Filter input: `h:/cookie:[^\\r\\n]*test=123/`\n     - Result: Only GET requests are retained, and requests containing `test=123` in the request header cookie are excluded\n\n**Supported filter conditions**:\n\n| **Syntax** | **Effect** | **Example** |\n| ----------- | ------------------------------- | -------------------------------------------- |\n| `no prefix` | Matches requests whose request URL contains this keyword | `.example.com` |\n| `m:pattern` | Matches request method | `m:POST` (matches POST requests) |\n| `h:pattern` | Matches original request header | `h:/cookie:[^\\r\\n]*test=123/` (matches cookies) |\n| `H:pattern` | Matches request header Host field | `H:example.com` `H:/test/i` |\n| `i:pattern` | Matches client IP | `i:11.2` `i:/^11\\.2/` |\n\nDifferent from the search box at the bottom of the Network list:\n\n| **Function** | **Network search box** | **Filter in Settings** |\n| ---------------- | -------------------------- | ------------------------------------ |\n| **Match range** | All data in the request and response stages | **Matches only the request phase** (does not include response data) |\n| **Effective Time** | Filters displayed and future requests in real time | **Effective only for new future requests** |\n| **Affects Historical Data** | Can filter existing packet capture records | **Does not affect displayed requests** |\n\n**Other Options:**\n\n| Item Name | Function Description |\n| ----------------------------------------- | -------------------------------------------------------- |\n| **Network Columns** | Customize the columns displayed in the packet capture list (such as status code, method, size, etc.) |\n| **Maximum Rows** | Set the maximum number of packets displayed simultaneously (to prevent memory overflow) |\n| **Viewing only your computer's requests** | Displays only requests sent by the local computer (filters requests from other devices/remote requests) |\n| **View All in new window** | Click \"View All\" to open the full content in a new window (suitable for viewing large response bodies) |\n| **Show Tree View** | Displays requests in a tree structure (grouped by domain name/path) |\n\n## Details Panel {#detail}\n\n1. Overview: Basic information about matching rules and requests\n2. Inspectors: Detailed information about request/response headers and content\n3. Timeline: Request performance information\n4. Composer: [Composer interface](./https)\n5. Tools: Various tools\n   - Console: Displays remote console logs. For details, see [Console](./console)\n   - Server: Displays exceptions that occur during Whistle execution\n   - Toolbox: Common tools and methods\n\n## Other Menus\n1. Replay: Replays the selected capture data\n2. Edit: Populates the capture data in the Composer window on the right\n3. Arrow button in the upper right corner: Switches to top-down panel mode, suitable for portrait-oriented monitors\n\n## Debugging WebSocket/TCP {#websocket}\nAfter selecting a WebSocket/TCP request, you can use the Inspectors/Frames panel to debug the request/response data:\n\n![Frames](/img/frames.png)\n\nSupported:\n1. Use the `Send/Receive`, `Pause`, and `Ignore` buttons in the top menu to start, pause, or ignore WebSocket/TCP data transmission.\n2. Use Composer in the bottom details panel to send custom data to the client or server.\n\nIf you need to ignore or pause the connection immediately after it's established, you can use the following rules:\n``` txt\nwww.example.com/path enable://ignoreReceive\nwww.example.com/path enable://ignoreSend\nwww.example.com/path enable://pauseReceive\nwww.example.com/path enable://pauseSend\n```\n\nOther debugging methods:\n1. Using rules: [frameScript](../rules/frameScript)\n2. Using plugins: [Plugin Development](../extensions/dev)\n"
  },
  {
    "path": "docs/en/docs/gui/online.md",
    "content": "# Online Dialog Box\n\nThe Online dialog box provides a real-time dashboard of the Whistle proxy service, helping you fully understand the health of the proxy service.\n\n<img src=\"/img/online.png\" alt=\"Online Dialog Box\" width=\"1000\" />\n\n## Service Status Overview\n\n| Metrics | Description |\n| -------------- | --------------------------------------------------- |\n| **Uptime** | Duration of the Whistle service (format: days:hours:minutes:seconds) |\n| **Memory Usage** | Current process memory usage (including detailed heap and external memory data) |\n| **CPU Usage** | Process CPU usage percentage (refreshed in real time) |\n\n## Traffic Statistics\n\n| Metrics | Description |\n| -------------- | ------------------------------------------- |\n| **Total Requests** | Cumulative number of requests processed by the proxy service |\n| **Real-time Throughput** | Current number of requests processed per second (QPS) |\n\n## Options\n\n| Options | Description | Technical Details |\n| ---- | ---- | -------- |\n| **Disable dark mode** | Disable the dark theme and use a light interface Dark mode is enabled by default.\n|**IPv6-only network** | Forces IPv6 addresses to be returned (AAAA record). Requires network support for IPv6; does not fall back to IPv4 if failed.\n|**Verbatim network** | Strictly returns IP addresses in the order configured by DNS. Check: Maintain original DNS response order, check: Smart sorting (IPv4 first).\n|**IPv4-first network** | Prefers returning IPv4 addresses (A record). Compatibility-first mode, with IPv6 as a fallback.\n"
  },
  {
    "path": "docs/en/docs/gui/plugins.md",
    "content": "# Plugins Screen\n\nThe Plugins screen is Whistle's plugin management hub, supporting the full lifecycle of plugins, including installation, configuration, updates, and uninstallation.\n\n<img src=\"/img/plugins.png\" alt=\"Plugins Interface\" width=\"1000\" />\n\n## Basic Interface Functions\n\n| Controls | Functions |\n| ------------ | ---- |\n| **ON** | Turn all plugins on or off. Red: On, Gray: Off |\n| **Install** | Install a plugin. See below for details. |\n| **Checkbox** | Whether to enable this plugin |\n| **Plugin Name** | Click to open the plugin management interface |\n| **Version Number** | Click to open the plugin help documentation |\n| **Option** | Click to open the plugin management interface |\n| **Rules** | Open the plugin's static rules (rules.txt, reqRules.txt, and resRules.txt files) |\n| **Update** | Update a plugin |\n| **Uninstall** | Uninstall a plugin |\n| **Help** | Click to open the plugin help documentation |\n| **Sync** | Synchronize the plugin's Rules and Values |\n\n## Installation Process\n1. Click the `Install` button at the top\n2. Enter the plugin name (e.g., `whistle.inspect`)\n3. Select `npm registry` (default `--registry=https://registry.npmjs.org`)\n4. Click `Install` to begin installation\n\n<img width=\"1000\" alt=\"install plugins\" src=\"/img/install-plugins.png\" />\n\n"
  },
  {
    "path": "docs/en/docs/gui/rules.md",
    "content": "# Rules Interface\n\nThe Rules interface is the core configuration area of Whistle, used to manage all proxy rules and their grouping.\n\n<img src=\"/img/rules.png\" alt=\"Rules Interface\" width=\"1000\" />\n\n## Basic Interface Functions\n\n| Controls | Functions |\n| ------------ | ---------------------------------------------- |\n| **ON** | Turns rules on or off. Red: On, Gray: Off |\n| **Import** | Imports rules |\n| **Export** | Exports rules |\n| **Save** | Saves and activates rules |\n| **Create** | Creates a new rule/group |\n| **Delete** | Deletes a rule/group |\n| **Rename** | Renames a rule/group |\n| **Settings** | Sets the editing style |\n| **Right-Click Menu** | In addition to the above menu functions, plugins are also supported for extended menu functions |\n\n## Advanced Editing Functions\n\n**Temporary File Editing**\n\n``` txt\n... protocol://temp/blank.js\n```\n- `protocol`: [Action protocol](../rules/protocols)\n- `temp/blank.js`: A blank temporary file\n\n**Workflow:**\n1. Enter the path to the protocol action content (e.g., `file://temp/blank.js`)\n2. Ctrl+click (Win) / Cmd+click (Mac) the path\n3. Modify the content in the pop-up editor\n4. Automatically associate the file with the rule after saving\n\n<img src=\"/img//temp.png\" width=\"600\" />\n\n## Bottom Search Box\n| Prefix | Filter Target | Example |\n| -------------- | ----------------------------------- | ---------------------------------- |\n| `No Prefix` | Rule Name and Content | `test` `/test\\d/i` |\n| `k:pattern` | Rule Name | `k:test` `k:/test\\d/i` |\n| `v:pattern` | Rule Content | `v:test` `v:/test\\d/i` |\n"
  },
  {
    "path": "docs/en/docs/gui/shortcut.md",
    "content": "# Shortcut Keys\n\n## Network\n1. `Ctrl[Command] + I`: Import network sessions\n2. `Ctrl[Command] + E`: Export network sessions\n3. `Ctrl[Command] + S`: Save network sessions\n4. `Ctrl[Command] + O`: Turn captured requests ON or OFF\n5. `Ctrl[Command] + L`: Toggle Network Panel layout: Left-right or top-bottom\n6. `Ctrl[Command] + .`: Open network settings\n7. `Ctrl[Command] + D`: Remove selected network sessions\n8. `Ctrl[Command] + B`: Switch between tree and list view of network sessions\n9. `Ctrl[Command] + Enter`: Replay selected requests\n10. `Ctrl[Command] + Shift + Enter`: Set the number of times to replay the selected requests\n11. `Ctrl[Command] + A`: Abort requests\n12. `Ctrl[Command] + X`: Clear network sessions\n13. `/ `: Focus the network search box at the bottom\n\n\n## Frames\n1. `Ctrl[Command] + Enter`: Replay selected frames\n2. `Ctrl[Command] + X`: Clear frames\n\n\n## Rules\n1. `Ctrl[Command] + I`: Import rules\n2. `Ctrl[Command] + E`: Export rules\n3. `Ctrl[Command] + S`: Save rules changes\n4. `Ctrl[Command] + O`: Turn rules ON or OFF\n5. `Ctrl[Command] + L`: Toggle line numbers\n6. `Ctrl[Command] + .`: Open rules settings\n7. `Ctrl[Command] + D`: Remove selected rules\n8. `/ `: Focus the rules search box at the bottom\n\n\n## Values\n1. `Ctrl[Command] + I`: Import values\n2. `Ctrl[Command] + E`: Export values\n3. `Ctrl[Command] + S`: Save values changes\n4. `Ctrl[Command] + L`: Toggle line numbers\n5. `Ctrl[Command] + .`: Open: values settings\n6. `Ctrl[Command] + D`: Remove selected values\n7. `/ `: Focus the values search box at the bottom\n\n\n## Plugins\n1. `Ctrl[Command] + I`: Open the plugin installation dialog box\n2. `Ctrl[Command] + O`: Turn all plugins ON or OFF\n\n## Global\n1. `Ctrl[Command] + <--`: Toggle Network, Rules, Values, and Plugins in reverse order\n2. `Ctrl[Command] + -->`: Toggle Network, Rules, Values, and Plugins\n<!-- 3. `Ctrl[Command] + J`: Open service dialog（Need login） -->\n\n## Client\n1. `Ctrl[Command] + R`: Open or closes the system proxy\n2. `Ctrl[Command] + Shift + R`: Restarts the client\n3. `Ctrl[Command] + Q`: Exits the client\n4. `Ctrl[Command] + ,`: Open Proxy Settings\n"
  },
  {
    "path": "docs/en/docs/gui/values.md",
    "content": "# Values Interface\n\nAction Content Quick Configuration Interface\n\n<img src=\"/img/values.png\" alt=\"Values Interface\" width=\"1000\" />\n\n## Basic Interface Functions\n\n| Controls | Function |\n| ------------ | ---------------------------------------------- |\n| **Import** | Import Values |\n| **Export** | Export Rules |\n| **Create** | Create New Action Content/Group |\n| **Delete** | Delete Action Content/Group |\n| **Rename** | Rename Action Content/Group |\n| **Settings** | Set Editing Style |\n| **Right-Click Menu** | In addition to integrating the above menu functions, plugins are also supported for extended menu functions. |\n\n## Bottom Search Box\n| Prefix | Filter Target | Example |\n| --------------- | ----------------------------------- | --------------------------------- |\n| `No Prefix` | Rule Name and Content | `test` `/test\\d/i` |\n| `k:pattern` | Rule name | `k:test` `k:/test\\d/i` |\n| `v:pattern` | Rule content | `v:test` `v:/test\\d/i` |\n"
  },
  {
    "path": "docs/en/docs/gui/weinre.md",
    "content": "# Weinre Remote Debugging\n\nWeinre (Web Inspector Remote) is a webpage remote debugging tool integrated with Whistle, allowing developers to debug webpages on mobile devices directly from their computers.\n\n<img src=\"/img/weinre.png\" alt=\"Weinre Menu\" width=\"1000\" />\n\n## Usage\n1. Configure the debugging rule. Add the following to the Whistle rule configuration:\n    ``` txt\n    pattern weinre://your-debug-id\n    ```\n2. Visit the target page\n   - Open/refresh the page you want to debug on your mobile device\n   - Ensure your device and debugging computer are on the same network\n3. Find the Weinre option in the Whistle top menu bar\n   - Hover your mouse and select the \"your-debug-id\" submenu you set\n   - Click to open the Weinre debugging page\n4. You can set separate debug-ids for different pages to enable parallel debugging:\n    ``` txt\n    mobile.example.com weinre://mobile-debug\n    tablet.example.com weinre://tablet-debug\n    ```\n\n<img src=\"/img/weinre-menu.png\" width=\"300\" />\n"
  },
  {
    "path": "docs/en/docs/index.md",
    "content": "# Installation\n\nPlease refer to the README document in the corresponding GitHub repository to complete the installation based on your operating system.\n\n## 🖥️ Desktop Installation\n1. Applicable Systems: macOS / Windows / Linux (Ubuntu / Fedora, etc. with a graphical user interface)\n2. We recommend using the official Whistle client: [https://github.com/avwo/whistle-client](https://github.com/avwo/whistle-client)\n\n## 💻 Command Line Installation\n1. Applicable Scenarios: Linux servers without a graphical user interface / Docker container environments / embedded systems / IoT devices, etc.\n2. Using the command line version of Whistle: [https://github.com/avwo/whistle](https://github.com/avwo/whistle)\n"
  },
  {
    "path": "docs/en/docs/mobile.md",
    "content": "# Mobile Packet Capture\nMobile packet capture debugging requires the following configuration:\n1. Install the root certificate (required for HTTPS packet capture)\n2. Set up the system proxy\n\n## Install the root certificate (required for HTTPS packet capture)\n\n#### Downloading the root certificate:\n\n1. Click the HTTPS button at the top of the Whistle interface. The HTTPS Settings dialog box will pop up.\n\n    <img width=\"320\" alt=\"HTTPS Settings\" src=\"/img/https-settings.png\" />\n2. Use your phone's camera to scan the QR code in the dialog box → Click the link that pops up. (If the download fails, try another QR code until the scan succeeds.)\n\n    <img width=\"320\" alt=\"Scan qrcode\" src=\"/img/https-qrcode.png\" />\n\n    > If the native Android browser fails to download, try using Chrome to download the certificate: http://[Computer IP]:[Whistle port]/cgi-bin/rootca, or download the certificate from a PC and transfer it to your phone.\n    > \n    > If all QR codes fail to download:\n    > - Check that your device and Whistle host are on the same local area network.\n    > - Verify that your firewall isn't blocking the proxy port.\n3. After the download is successful, record the IP and port number of the QR code address for use in setting up the system proxy. Then, install and trust the root certificate as follows.\n\n#### Installing the Root Trust Certificate:\n\n**iOS**\n\n1. Install the profile\n   - Go to: Settings → General → VPN & Device Management\n   - Find the \"Downloaded Profile\" and install it.\n2. Enable Full Trust (this step is essential).\n   - Go to: Settings → General → About → Certificate Trust Settings\n   - Enable Full Trust for the Whistle root certificate.\n\n   <img width=\"320\" alt=\"Certificate Trust Settings\" src=\"/img/https-trust.png\" />\n\n**Android**\n\n1. Go to: Settings → Security → Encryption & Credentials → Install Certificates → CA Certificates\n2. Select the downloaded certificate file.\n3. Enter your lock screen passcode to confirm.\n4. Name the certificate (e.g., \"Whistle\").\n\n> **Version Differences:**\n>\n> Android 12+: Requires \"More Security Settings\"\n>\n> Huawei EMUI: Requires \"Clean Mode\" to be disabled first\n>\n> Other brands: The path may vary slightly\n>\n\n## Set up the system proxy\n1. Access Wi-Fi settings\n   - Go to: Settings → Wi-Fi\n   - Tap the icon next to the currently connected network (Android may require long-pressing the Wi-Fi name).\n2. Configure a manual proxy\n   - Select \"Manual\" for the proxy type.\n   - Server: Enter the IP address from which the certificate QR code was successfully downloaded.\n   - Port: Enter the port from which the certificate QR code was successfully downloaded (Whistle defaults to port 8899).\n   - Save settings\n\n  <img width=\"320\" alt=\"Set Proxy\" src=\"/img/proxy-settings.jpg\" />\n\n## Successful root certificate installation and proxy setup indicator\n\n1. Accessing the web works fine on my phone.\n2. Whistle can capture HTTPS requests.\n3. No security warnings appear.\n\n## FAQ\n\n1. Certificate is untrusted.\n   - Check that \"Full Trust\" is set up (iOS).\n   - Confirm that the certificate is installed in the correct location (Android).\n2. Proxy is not working.\n   - Check the \"Online\" dialog box in the upper right corner of the Whistle interface to see if the IP and port have changed.\n   - If you're not sure which IP address is available, try each one.\n3. Packet capture fails for a specific app.\n   - Check that the app uses a custom certificate.\n   - Try adding network configuration to the AndroidManifest.xml file. For details, see: [https://developer.android.com/training/articles/security-config#base-config](https://developer.android.com/training/articles/security-config#base-config)\n"
  },
  {
    "path": "docs/en/docs/rules/@.md",
    "content": "# Usage of the `@` Symbol  \n\nWhen you want to store rules on a remote server or in a local project file and have Whistle automatically synchronize and update them, you can use the `@` symbol in the rules.  \n\n## Syntax Rules  \n\n## 1. Importing Rule Files (`@path`)  \nDynamically load the content of external rule files. Whistle will periodically update the content from the rule files.  \n\n**Usage Format:** `@file-path-or-url`  \n\n**Supported Types:**  \n- Remote URL: `@https://example.com/rules.txt`  \n- Local File: `@~/projects/rules.txt` (supports all operating system paths)  \n- Plugin Interface: `@whistle.nohost/api/rules` (requires the plugin to implement the interface)  \n\n> **Windows Path Compatibility:** Supports mixing `/` and `\\` as path separators:  \n> ```txt  \n> @C:/whistle/rules.txt  \n> @D:\\config\\proxy.rules  \n> ```  \n\n## Configuration Examples  \n\n### Import Remote Rule Files  \n```txt  \n@https://raw.githubusercontent.com/your-org/configs/master/whistle-rules.txt  \n```  \n\n### Import Local Project Rules  \n```txt  \n@/Users/username/projects/my-app/whistle-config.txt  \n@~/dev/configs/rules.txt  # Using the user directory  \n```  \n\n### Import Rules Provided by Plugins  \n```txt  \n@whistle.nohost/api/rules  \n```  \n\n### Mixed Usage Example  \n```txt  \n# Import team-shared base rules  \n@https://team.example.com/configs/base-rules.txt  \n\n# Import personal project-specific rules  \n@~/projects/my-app/whistle-overrides.txt  \n\n```  \n\n## File Format Requirements  \n\nImported external files should contain standard Whistle rule syntax:  \n\n```txt  \n# Example of a remote rule file  \nwww.example.com proxy://127.0.0.1:8080  \napi.example.com resCors://*  \nstatic.example.com cache://3600  \n```\n"
  },
  {
    "path": "docs/en/docs/rules/attachment.md",
    "content": "# attachment\n\nThe `attachment` protocol is used to set the `Content-Disposition` field in the response headers, instructing the browser to treat the server's response directly as an attachment for download and save the file with the name specified by `attachment`, instead of displaying it within the page.\n\n> Similar to Koa's `attachment` method: https://koajs.com/\n\n## Rule Syntax\n\n`attachment` supports multiple ways to specify the download filename:\n\n### 1. Inline Value (Direct Specification)\nWrite the filename directly in the rule.\n```txt\npattern attachment://filename [lineProps...] [filters...]\n```\n**Example:**\n```txt\nhttps://example.com/report attachment://Annual-Report.pdf\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when the filename is long or needs to be reused. Reference a custom key in the rule and define its value in a subsequent code block.\n````txt\npattern attachment://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\nQuarterly-Data-2024Q1.csv\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a value pre-defined in the `Values` panel.\n```txt\npattern attachment://{key-of-values} [lineProps...] [filters...]\n```\n**Prerequisite:** A key named `key-of-values` with the target filename as its value must exist in `Values`.\n\n### 4. File/Remote URL Loading\n**Currently NOT supported** to dynamically load filename content from a local file or remote URL.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **filename** | Yes | The filename displayed during download.<br>• Examples: `document.pdf`, `export.xlsx`.<br>• Can be specified via the three methods above (inline/embedded/Values).<br>• ⚠️ Loading from files or remote URLs is not supported. |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Example\nMake visiting the homepage of example.com automatically download a file named `example.html`.\n```txt\nhttps://www.example.com/ attachment://example.html\n```\n\n### Using with Filters\nTrigger PDF download only when the request contains a specific query parameter `download=true`.\n```txt\nhttps://api.example.com/document attachment://User-Manual.pdf includeFilter:///[?&]download=true/\n```\n\n### Using Embedded Values\nUse an embedded code block for clarity when the filename is complex or needs annotation.\n````txt\nhttps://assets.example.com/data.json attachment://{my-file-name}\n\n``` my-file-name\nConfig-Backup-20240514.json\n```\n````\n\n## Implementing Remote Loading\n\nThe `attachment` protocol itself does not support loading values from file paths or remote URLs. If remote loading functionality is needed, consider the following two alternative approaches:\n\n### 1. Extend Functionality via Plugins\nYou can develop a plugin to implement more complex filename retrieval logic, such as dynamically fetching filenames from a remote API. Please refer to the [Plugin Development Documentation](../extensions/dev) for specific methods.\n\n### 2. Using @url to Load Complete Rules\nStore the complete configuration file containing `attachment` rules on a remote server and then load the entire rule set via the [`@url`](./@) protocol.\n\n**Example:**\n1. Create a rule file `xxx.txt` on a remote server:\n    ```txt\n    https://api.example.com/export attachment://remote-file.csv\n    https://static.example.com/docs attachment://Document.pdf\n    ```\n2. Reference the remote rules in the local Rules configuration:\n    ```txt\n    @https://remote-url/xxx.txt\n    ```\n   This way, the `attachment` rules in the remote file will be loaded and take effect.\n\n## How It Works & Related Protocols\n\n1.  **Core Principle**: The `attachment` protocol essentially sets response headers automatically.\n    The example above is completely equivalent to manually setting headers using the [`resHeaders`](./resHeaders) protocol:\n    ```txt\n    https://www.example.com/ resHeaders://content-disposition=attachment;filename=\"example.html\"\n    ```\n\n2.  **Advantage**: Compared to directly using `resHeaders`, the `attachment` syntax is more concise, readable, focuses on the file download scenario, and automatically handles details like filename escaping.\n\n3.  **Note**: The actual content of the response body returned by the server remains unchanged. This rule simply instructs the browser how to handle that content by modifying the response headers.\n"
  },
  {
    "path": "docs/en/docs/rules/auth.md",
    "content": "# auth\n\nThe `auth` protocol is used to quickly modify the `Authorization` header of a request, automatically adding the credentials required for HTTP Basic Authentication to matching requests.\n\n## Rule Syntax\n\n`auth` supports multiple ways to set the request authentication header:\n\n### 1. Inline Value (Direct Specification)\nWrite the username and password directly in the rule.\n```txt\npattern auth://username:password [lineProps...] [filters...]\n# or\npattern auth://username=test&password=123 [lineProps...] [filters...]\n```\n**Example:**\n```txt\nhttps://example.com/api1/ auth://admin:secret\nhttps://example.com/api2/ auth://username=admin&password=secret\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when the credentials are complex or need to be reused. Reference a custom key in the rule and define its value in a subsequent code block.\n````txt\npattern auth://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\nusername: admin\npassword: my secret password\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a value pre-defined in the `Values` panel.\n```txt\npattern auth://{key-of-values} [lineProps...] [filters...]\n```\n**Prerequisite:** A key named `key-of-values` with an object containing `username` and `password` as its value must exist in `Values`.\n\n### 4. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern auth://temp.json\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows)\n2. Click with the mouse on `auth://temp.json`\n3. Enter the response content in the pop-up editing dialog\n4. Click `Save` to save\n\nAfter saving, the rule will automatically change to a format similar to this:\n```txt\nhttps://example.com/report auth://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.json\n```\n\nTo edit again, click the temporary file link in the same way.\n\n### 5. Loading from a File or Remote URL\nLoad a JSON or simple YAML file containing authentication information from a local file or remote URL.\n```txt\n# Load from a local file\npattern auth:///User/xxx/auth.json\n\n# Load from a remote URL (supports http and https)\npattern auth://https://config.example.com/auth.json\n```\n\n**File Format Requirements:**\nThe file content should be in JSON or simple YAML format, containing `username` and `password` fields:\n```json\n{\n  \"username\": \"admin\",\n  \"password\": \"secret\"\n}\n```\nor\n```yaml\nusername: admin\npassword: secret\n```\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | Authentication credentials, supporting multiple formats:<br>• **Direct format**: `username:password` or `username=test&password=123`<br>• **Object format**: An object containing `username` and `password` fields<br>• Supports loading from local files or remote URLs<br>• Supports inline, embedded, Values, local file path, remote URL references |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Example\nAdd Basic Authentication to the API endpoints of example.com:\n```txt\nhttps://api.example.com/ auth://admin:secret\n```\n\n### Using Embedded Values\nUse the embedded method for more security when passwords contain special characters or spaces:\n````txt\nhttps://internal.example.com/ auth://{prod-credentials}\n\n``` prod-credentials\nusername: service-account\npassword: P@ssw0rd!2024\n```\n````\n\n### Loading from a File\nLoad authentication information from a local configuration file:\n```txt\nhttps://example.com/api/ auth:///Users/john/config/auth.json\n```\n\n### Using with Filters\nAdd authentication only for POST requests:\n```txt\nhttps://example.com/api/ auth://admin:secret includeFilter://m:POST\n```\n\n### Referencing Configuration from Values\nAssuming the `api-auth` configuration already exists in Values:\n```txt\nhttps://example.com/api/ auth://{api-auth}\n```\n\n## How It Works & Related Protocols\n\n1. **Core Principle**: The `auth` protocol automatically calculates the Base64 encoding of the username and password and sets the `Authorization` request header.\n\n   The example above is equivalent to manually setting it using the [`reqHeaders`](./reqHeaders) protocol:\n   ```` txt\n   https://example.com/api/ reqHeaders://{auth.txt} # Content has spaces, cannot be inline\n\n   ``` auth.txt\n   authorization: Basic YWRtaW46c2VjcmV0\n   ```\n   ````\n   Where `YWRtaW46c2VjcmV0` is the Base64 encoding of `admin:secret`.\n\n2. **Advantage**: Compared to directly using `reqHeaders`, the `auth` syntax is more concise and intuitive, requiring no manual calculation of the Base64 encoded value.\n\n## Notes\n- The `auth` protocol only supports HTTP Basic Authentication\n- For more complex authentication methods (such as Bearer Token, OAuth, etc.), please use the [`reqHeaders`](./reqHeaders) protocol to directly set the corresponding `Authorization` header\n- When loading from a remote URL, ensure the target URL is secure and reliable\n"
  },
  {
    "path": "docs/en/docs/rules/cache.md",
    "content": "# cache\n\nThe `cache` protocol is used to quickly set cache control headers for responses, helping to manage caching behavior for browsers and servers. It can set caching policies for matched responses, including:\n- Setting a fixed cache duration\n- Disabling caching (`no-cache`)\n- Completely prohibiting storage (`no-store`)\n\nThis can help optimize website performance or ensure specific resources are not cached.\n\n## Rule Syntax\n\n`cache` supports multiple ways to set caching policies:\n\n### 1. Inline Value (Direct Specification)\nSpecify the caching policy directly in the rule.\n```txt\n# Set cache for 5 seconds. Supports positive integers, 0, and negative integers.\npattern cache://5 [lineProps...] [filters...]\n\n# Set cache headers: `cache-control: no-cache`, `pragma: no-cache`\n# `no-cache` can be abbreviated as `no`.\npattern cache://no-cache [lineProps...] [filters...]\n\n# Set cache headers: `cache-control: no-store`, `pragma: no-cache`\npattern cache://no-store [lineProps...] [filters...]\n```\n\n**Examples:**\n```txt\nhttps://example.com/api/data cache://no-cache\nhttps://example.com/static/js cache://86400  # Cache for 24 hours\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when the caching policy needs to be reused or is complex.\n````txt\npattern cache://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\nno-cache\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a caching policy pre-defined in the `Values` panel.\n```txt\npattern cache://{key-of-values} [lineProps...] [filters...]\n```\n**Prerequisite:** A key named `key-of-values` with the caching policy as its value must exist in `Values`.\n\n### 4. File/Remote URL Loading\n**Currently NOT supported** to dynamically load cache configuration from a local file or remote URL.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | Caching policy value:<br>• **Number**: Cache duration in seconds (e.g., `60` means cache for 60 seconds)<br>• **no-cache** or **no**: Allows caching but requires revalidation on each use<br>• **no-store**: Completely prohibits caching<br>• Supports inline, embedded, and Values references<br>• ⚠️ Loading from files or remote URLs is not supported |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n**Response Headers Affected:**\n- `Cache-Control`\n- `Expires`\n- `Pragma`\n\n## Caching Policy Explanation\n\n| Policy | Meaning | Typical Use Cases |\n| :--- | :--- | :--- |\n| **Number (e.g., `60`)** | Caches the resource for the specified number of seconds | Static resources, API responses that don't change often |\n| **no-cache** (or **no**) | Allows caching but must validate before each use | Frequently changing content, such as user data, real-time quotes |\n| **no-store** | Completely prohibits caching any version | Sensitive data, payment pages, one-time tokens |\n\n## Configuration Examples\n\n### Basic Examples\n```txt\n# Cache static resources for 1 hour\nhttps://example.com/static cache://3600\n\n# Disable caching for API endpoints\nhttps://example.com/api cache://no-cache\n\n# Prohibit caching for sensitive data\nhttps://example.com/account cache://no-store\n```\n\n### Using Embedded Values\n````txt\nhttps://example.com/reports cache://{report-cache-policy}\n\n``` report-cache-policy\nno-cache\n```\n````\n\n### Using with Filters\nSet caching only for specific response status codes:\n```txt\nhttps://example.com/api cache://300 includeFilter://s:200\n```\n\n### Referencing Configuration from Values\nAssuming the `static-cache` configuration already exists in Values:\n```txt\nhttps://example.com/assets cache://{static-cache}\n```\n\n## How It Works & Related Protocols\n\n1. **Core Principle**: The `cache` protocol automatically sets the corresponding cache control headers:\n\n   - `cache://5` sets:\n     ```http\n     Cache-Control: max-age=5\n     Expires: [Current time + 5 seconds]\n     ```\n   \n   - `cache://no-cache` sets:\n     ```http\n     Cache-Control: no-cache\n     Pragma: no-cache\n     ```\n   \n   - `cache://no-store` sets:\n     ```http\n     Cache-Control: no-store\n     Pragma: no-cache\n     ```\n\n2. **Related Protocols**:\n   - **Disable Caching**: A more thorough cache disabling solution that also removes cache-related headers from both requests and responses. See: [disable://cache](./disable)\n   - **Manual Header Setting**: For more complex cache control, you can use the [`resHeaders`](./resHeaders) protocol to manually set cache headers.\n\n## Notes\n- Cache duration is in seconds; decimals are not supported.\n- Setting `no-cache` does not mean \"do not cache\" but rather \"must validate before use\".\n- For scenarios requiring complete cache disabling, it is recommended to also use the [`disable://cache`](./disable) protocol.\n- Actual caching behavior is also influenced by server configuration and browser implementation.\n"
  },
  {
    "path": "docs/en/docs/rules/cipher.md",
    "content": "# tlsOptions\n\nThe `tlsOptions` protocol is used to configure security parameters for HTTPS/TLS connections, including encryption cipher suites, client certificates, etc., to establish encrypted communication channels and server authentication.\n\n> **Version Requirement**: Only the latest version of Whistle (≥ v2.9.101) supports this feature.\n\nWith the `tlsOptions` protocol, you can:\n- Customize TLS encryption cipher suites\n- Configure client certificates required for mutual authentication (mTLS)\n- Set other security parameters for TLS connections\n- Control TLS versions and connection options\n\n**Rule Merging Mechanism**: `tlsOptions` supports setting any number of configuration rules. Whistle automatically merges these rules in top-to-bottom order, allowing you to flexibly combine different TLS configurations.\n\n## Rule Syntax\n\n`tlsOptions` supports multiple ways to configure security parameters for HTTPS/TLS connections:\n\n### 1. Inline Value (Direct Specification)\nWrite the operation directly in the rule.\n```txt\n# Set client certificate\npattern tlsOptions://key=/path/to/client.key&cert=/path/to/client.crt\n\n# Customize cipher suite\npattern tlsOptions://ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384 [lineProps...] [filters...]\n\n# Or use the ciphers parameter\npattern tlsOptions://ciphers=ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384 [lineProps...] [filters...]\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when the configuration is complex or needs to be reused.\n````txt\npattern tlsOptions://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n{\n  passphrase: \"123456\",\n  pfx: \"-----BEGIN PKCS7-----\\n...\"\n}\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a configuration pre-defined in the `Values` panel (central configuration area).\n```txt\npattern tlsOptions://{key-of-values} [lineProps...] [filters...]\n```\n**Prerequisite:** A key named `key-of-values` with a TLS configuration object as its value must exist in `Values`.\n\n### 4. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern tlsOptions://temp.json\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows)\n2. Click with the mouse on `tlsOptions://temp.json`\n3. Enter the response content in the pop-up editing dialog\n4. Click `Save` to save\n\nAfter saving, the rule will automatically change to a format similar to this:\n```txt\nhttps://example.com/report tlsOptions://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.json\n```\n\nTo edit again, click the temporary file link in the same way.\n\n### 5. Loading from a File or Remote URL\nLoad a JSON or YAML file containing TLS configuration from a local file or remote URL.\n```txt\n# Load from a local file\npattern tlsOptions:///User/xxx/tlsOptions.json\n\n# Load from a remote URL (supports http and https)\npattern tlsOptions://https://config.example.com/tlsOptions.json\n```\n\n**File Format Requirements:**\nThe file content should be in JSON or YAML format:\n```json\n{\n  \"ciphers\": \"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\",\n  \"minVersion\": \"TLSv1.2\",\n  \"maxVersion\": \"TLSv1.3\",\n  \"secureProtocol\": \"TLSv1_2_method\"\n}\n```\nor\n```yaml\nciphers: ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\nminVersion: TLSv1.2\nmaxVersion: TLSv1.3\nsecureProtocol: TLSv1_2_method\n```\n\n---\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | TLS configuration data, supporting multiple formats:<br>• Cipher suite names (separated by `:`)<br>• `tls.connect(options)` parameter object<br>• Supports references from local files, remote URLs, inline, embedded, and Values |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n---\n\n## Configuration Object Reference\n\nComplete TLS connection configuration object, supporting all parameters of Node.js [`tls.connect()`](https://nodejs.org/docs/latest/api/tls.html#tlscreatesecurecontextoptions):\n\n```js\n{\n  // Encryption cipher suites\n  ciphers: \"ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256\",\n  \n  // TLS protocol version control\n  minVersion: \"TLSv1.2\",\n  maxVersion: \"TLSv1.3\",\n  secureProtocol: \"TLSv1_2_method\",\n  \n  // Certificate-related configurations\n  ca: \"-----BEGIN CERTIFICATE-----\\n...\",  // CA certificate\n  cert: \"-----BEGIN CERTIFICATE-----\\n...\", // Client certificate\n  key: \"-----BEGIN PRIVATE KEY-----\\n...\",  // Private key\n  pfx: \"-----BEGIN PKCS7-----\\n...\",       // PFX/P12 format certificate\n  passphrase: \"cert-password\",             // Certificate password\n  \n  // Other security options\n  honorCipherOrder: true,     // Prioritize the server's cipher suite order\n  requestCert: true,          // Request client certificate (mutual authentication)\n  rejectUnauthorized: true,   // Reject unauthorized certificates\n  \n  // Advanced options\n  ecdhCurve: \"auto\",         // ECDH curve\n  dhparam: \"-----BEGIN DH PARAMETERS-----\\n...\",\n  secureOptions: 0,          // SSL option flags\n  sessionTimeout: 300,       // Session timeout (seconds)\n  sessionIdContext: \"whistle\"\n}\n```\n\n---\n\n## Configuration Examples\n\n### 1. Custom Cipher Suite\nRestrict TLS encryption algorithms for specific domains:\n```txt\nwww.example.com/path tlsOptions://ECDHE-ECDSA-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384\n```\n\n> **Use Case**: Solve compatibility issues with specific servers. Reference: [GitHub Issue #963](https://github.com/avwo/whistle/issues/963)\n\n### 2. Configuring Client Certificate (Mutual Authentication mTLS)\n\n**cert format certificate:**\n```txt\nwww.exaple.com/path tlsOptions://key=/User/xxx/test.key&cert=/User/xxx/test.crt\n```\n\n**pem format certificate:**\n```txt\nwww.exaple.com/path tlsOptions://key=E:\\test.key&cert=E:\\test.pem\n```\n\n**pfx format certificate:**\n```txt\nwww.exaple.com/path tlsOptions://passphrase=123456&pfx=/User/xxx/test.pfx\n```\n\n**p12 format certificate:**\n```txt\nwww.exaple.com/path tlsOptions://passphrase=123456&pfx=E:/test.p12\n```\n\n> **Windows Paths**: Supports mixing `/` and `\\` as path separators.\n\n### 3. Embedding Certificate Content\nEmbed certificate content directly into the configuration:\n````txt\n``` test.json\n{\n  key: '----xxx----- ... ----xxx-----',\n  cert: '----yyy----- ... ----yyy-----'\n}\n```\n\nwww.exaple.com/path tlsOptions://{test.json}\n````\n\n### 4. Loading Configuration from Local or Remote Files\n**Load from local file:**\n```txt\nwww.example.com/path1 tlsOptions:///User/xxx/test.json\n```\n\n**Load from remote URL:**\n```txt\nwww.example.com/path2 tlsOptions://https://www.xxx.com/xxx/params.json\n```\n\n**Use temporary file for editing:**\n```txt\nwww.example.com/path3 tlsOptions://temp/blank.json\n```\n\n### 5. Rule Merging Example\nUtilize rule merging feature to gradually refine configuration:\n```txt\n# First layer: Set basic TLS version for the entire domain\n*.example.com tlsOptions://minVersion=TLSv1.2\n\n# Second layer: Add client certificate for API subdomain\napi.example.com tlsOptions://key=/certs/client.key&cert=/certs/client.crt\n\n# Third layer: Further restrict cipher suite for specific paths\napi.example.com/secure  tlsOptions://ciphers=ECDHE-RSA-AES256-GCM-SHA384\n```\n\n---\n\n## Notes\n\n1. **Version Compatibility**: Ensure the Whistle version used is ≥ v2.9.101.\n2. **Certificate Format**: Different certificate formats require corresponding parameters:\n   - PEM format: Use `key` and `cert` parameters.\n   - PFX/P12 format: Use `pfx` and `passphrase` parameters.\n3. **Path Handling**:\n   - Relative paths are relative to the Whistle configuration file directory.\n   - Windows paths can mix `/` and `\\`.\n4. **Security Warnings**:\n   - Use `rejectUnauthorized: false` with caution, only in development environments.\n   - Safeguard client private keys to avoid leaks.\n5. **Performance Impact**: Complex encryption algorithms may affect connection performance.\n6. **Debugging Recommendations**: When encountering connection issues, test with simple configurations first, then gradually add complex options.\n\n---\n\n## Troubleshooting\n\n### Q: Certificate loading fails.\n**A:** Check:\n1. Whether the certificate file path is correct.\n2. Whether the certificate format matches the parameter type.\n3. Whether the PFX certificate password is correct.\n\n### Q: Connection is rejected.\n**A:** Check:\n1. Whether the TLS version is supported by the server.\n2. Whether the cipher suite is supported by the server.\n3. Whether the client certificate is valid and trusted by the server.\n\n### Q: Configuration does not take effect.\n**A:** Check:\n1. Whether the rule pattern correctly matches the target URL.\n2. Whether the Whistle version meets requirements.\n3. Whether the configuration syntax is correct, especially JSON format.\n4. Whether conflicting rules exist (rules are merged in top-to-bottom order).\n\n### Q: Rule merging doesn't work as expected.\n**A:** Check:\n1. Whether the order of rules is correct (merged from top to bottom).\n2. Whether configurations in different rules conflict.\n3. Whether more specific patterns override general configurations.\n"
  },
  {
    "path": "docs/en/docs/rules/cssAppend.md",
    "content": "# cssAppend\n\nThe `cssAppend` protocol is used to insert specified content at the end of an existing CSS-type response body.\n\n> **Effective Condition**: Only takes effect on responses with a `content-type` header containing `html` or `css`, and a status code that includes a response body (e.g., `200`/`500`, etc.).\n> \n> ⚠️ Note: Requests without a response body, such as `204`, `304`, are not affected.\n> \n> HTML pages will automatically be wrapped in `<style>...</style>`.\n\nWith the `cssAppend` protocol, you can:\n- Append new style rules to the end of existing CSS files.\n- Dynamically add CSS override rules.\n- Inject custom styles for specific pages.\n- Extend styling functionality without modifying the source files.\n\n## Rule Syntax\n\n`cssAppend` supports multiple ways to specify the content to be appended:\n\n### 1. Inline Value (Direct Specification)\nWrite the CSS content to be appended directly in the rule.\n```txt\n# No spaces or line breaks are allowed inside the parentheses of `value`.\npattern cssAppend://(value) [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/index.html cssAppend://(.custom-btn{background:red;})\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when the CSS content to be appended contains spaces, line breaks, or needs to be reused.\n````txt\npattern cssAppend://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n.dark-mode {\n  background: #333;\n  color: #fff;\n}\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference CSS content pre-defined in the `Values` panel (central configuration area).\n```txt\npattern cssAppend://{key-of-values} [lineProps...] [filters...]\n```\n**Prerequisite:** A key named `key-of-values` with CSS content as its value must exist in `Values`.\n\n### 4. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern cssAppend://temp.css\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows)\n2. Click with the mouse on `cssAppend://temp.css`\n3. Enter the response content in the pop-up editing dialog\n4. Click `Save` to save\n\nAfter saving, the rule will automatically change to a format similar to this:\n```txt\nhttps://example.com/test.css cssAppend://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.css\n```\n\nTo edit again, click the temporary file link in the same way.\n\n### 5. Loading from a File or Remote URL\nLoad the CSS content to be appended from a local file or remote URL.\n```txt\n# Load from a local file\npattern cssAppend:///User/xxx/custom.css\n\n# Load from a remote URL (supports http and https)\npattern cssAppend://https://cdn.example.com/styles/override.css\n```\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | The CSS content to be appended, supporting multiple formats:<br>• Plain CSS code text<br>• Supports references from local files, remote URLs, inline, embedded, and Values |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n**Special Notes:**\n- Only takes effect on requests where the `content-type` response header contains `css`.\n- Does not take effect on status codes without a response body, such as `204`, `304`.\n- Appended content is directly concatenated to the end of the original CSS file.\n\n## Configuration Examples\n\n#### Scenario 1: Adding Responsive Breakpoints\n```` txt\nwww.example.com/responsive.css cssAppend://{test.css}\n\n``` test.css\n@media (max-width: 768px) {\n  .sidebar { display: none; }\n  .main-content { width: 100%; }\n}\n```\n\n````\n\n#### Scenario 2: Overriding Third-party Library Styles\n```` txt\ncdn.example.com/bootstrap.min.css cssAppend://{test2.css}\n\n``` test2.css\n/* Override Bootstrap default styles */\n.btn-primary { background-color: #007bff !important; }\n.container { max-width: 1400px; }\n```\n````\n\n#### Scenario 3: Adding Print Styles\n````txt\nwww.example.com/print.css cssAppend://{test3.css}\n\n``` test3.css\n@media print {\n  .no-print { display: none; }\n  a::after { content: \" (\" attr(href) \")\"; }\n}\n```\n````\n\n## Related Protocols\n\n1. **Inject Content Before the Response Body**: [reqAppend](./reqAppend)\n   - Inserts content before the body of all types of responses.\n\n2. **Inject Content Before CSS Response Body**: [cssPrepend](./cssPrepend)\n   - Inserts content before the CSS response body (opposite of `cssAppend`).\n\n3. **Replace CSS Response Content**: [cssBody](./cssBody)\n   - Completely replaces the CSS response content (rather than appending).\n\n4. **Other Content Type Operations**:\n   - [jsAppend](./jsAppend): Appends content after JavaScript response bodies.\n   - [htmlAppend](./htmlAppend): Appends content after HTML response bodies.\n"
  },
  {
    "path": "docs/en/docs/rules/cssBody.md",
    "content": "# cssBody\nReplaces the existing response body with the specified content. (This only works for responses with `content-type` containing `css` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern cssBody://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported: <br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path1 cssBody://(Hello) file://(-test-)\nwww.example.com/path2 cssBody://(Hello) file://(-test-) resType://js\nwww.example.com/path3 cssBody://(Hello) file://(-test-) resType://css\n```\n- Requesting `https://www.example.com/path1/to` results in a response of `<style>Hello</style>`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-`\n- Requesting `https://www.example.com/path3/to` results in a response of `https://www.example.com/path3/to` `Hello`\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 cssBody://{body.txt} file://(-test-)\nwww.example.com/path2 cssBody://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 cssBody://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path1/to` results in `<style>Hello world.</style>`\n- Requesting `https://www.example.com/path2/to` results in `-test-`\n- Requesting `https://www.example.com/path3/to` results in `Hello world.`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 cssBody:///User/xxx/test.css\nwww.example.com/path2 cssBody://https://www.xxx.com/xxx/params.css\n# Editing a temporary file\nwww.example.com/path3 cssBody://temp/blank.css\n````\n## Associated Protocols\n1. Replace the response content: [resBody](./resBody)\n2. Inject content before the CSS response content: [cssPrepend](./cssPrepend)\n3. Inject content after the CSS response content: [cssAppend](./cssAppend)\n"
  },
  {
    "path": "docs/en/docs/rules/cssPrepend.md",
    "content": "# cssPrepend\nInserts the specified content before the existing response body. (This only works for responses with `content-type` containing `css` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern cssPrepend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported: <br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching: • Request URL/Method/Header/Content • Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path1 cssPrepend://(Hello) file://(-test-)\nwww.example.com/path2 cssPrepend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 cssPrepend://(Hello) file://(-test-) resType://css\n```\n- Request `https://www.example.com/path1/to`. The response content becomes\n  ``` html\n  <!DOCTYPE html>\n  <style>Hello</style>-test-\n  ```\n- Request `https://www.example.com/path2/to`. The response content becomes `-test-`\n- Request The response content of `https://www.example.com/path3/to` becomes `Hello-test-`\n\n#### Embedded/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 cssPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 cssPrepend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 cssPrepend://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path1/to`, the response content becomes\n  ``` html\n  <!DOCTYPE html>\n  <style>Hello world.</style> -test-\n  ```\n- Requesting `https://www.example.com/path2/to`, the response content becomes `-test-`\n- Request `https://www.example.com/path3/to`. The response content becomes `Hello world.-test-`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 cssPrepend:///User/xxx/test.css\nwww.example.com/path2 cssPrepend://https://www.xxx.com/xxx/params.css\n# Editing a temporary file\nwww.example.com/path3 cssPrepend://temp/blank.css\n````\n\n## Associated Protocols\n1. Inject content before the response content: [reqPrepend](./reqPrepend)\n2. Replace CSS-type response content: [cssBody](./jsBody)\n3. Inject content after CSS-type response content: [cssPrepend](./jsAppend)\n"
  },
  {
    "path": "docs/en/docs/rules/delete.md",
    "content": "# delete\nUsed to delete the request URL, request/response headers, and request/response content.\n\n## Rule Syntax\n``` txt\npattern delete://prop1|prop2|... [filters...]\n\n# Equivalent to:\npattern delete://prop1 delete://prop2 ... [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | `pathname`: Delete the request path (excluding request parameters)<br/>`pathname.index`: `index` is the path segment index `..., -1, 0, 1, ...` or the keyword `last` (see below for details)<br/>`urlParams`: Delete all request parameters<br/>`urlParams.xxx`: Delete the `xxx` parameter of the URL<br/>`reqHeaders.xxx`: Delete the `xxx` field of the request header<br/>`resHeaders.xxx`: Delete the `xxx` field of the response header<br/>`reqBody`: Delete all request content<br/>`resBody`: Delete all response content<br/>`reqBody.xxx.yyy`: Delete the `xxx.yyy` field of the request content whose type is form or JSON<br/>`resBody.xxx.yyy`: Delete the `xxx.yyy` field of the response content whose response type is JSONP or JSON<br/>`reqType`: Delete the type in the request header `content-type`, excluding the possible charset<br/>`resType`: Delete the type in the response header `content-type`, excluding the possible charset<br/>`reqCharset`: Delete the possible charset in the request header `content-type`<br/>`resCharset`: Delete the possible charset in the response header `content-type`<br/>`reqCookies.xxx`: Delete the request header named Cookies named `xxx`<br/>`resCookies.xxx`: Deletes the cookie named `xxx` in the response header. | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\nhttps://www.example.com/path delete://reqCookies.token|resCookies.token\n\nhttps://raw.githubusercontent.com/avwo/whistle/refs/heads/master/package.json delete://resBody.name resType://json\n```\nThe above cookie deletion operation only affects cookies in the request/response process and does not modify cookies stored locally in the browser. To modify browser persistent cookies, you can do so in the following ways:\n- Deleting them by injecting JavaScript using [jsPrepend](./jsPrepend)\n- Setting cookie expiration times using [resCookies](./resCookies)\n\n## Deleting Paths\n> Supported versions: v2.9.102 and above\n\n### Rule Syntax\n```\n# Basic Format\nTarget Domain delete://pathname.index\n\n# Example\nwww.example.com/api/path/to delete://pathname.0 delete://pathname.1 delete://pathname.-1\n```\n\n### Rule Description\nThis rule deletes specific segments of the request URL's path. Whistle extracts the request path excluding query parameters, delimits it by `/`, and then deletes it.\n\n**Example Parsing:**\n- Request URL: `https://www.example.com/api/path/to/xxx?query`\n- Extracted Path: `api/path/to/xxx`\n- Split Array: `['api', 'path', 'to', 'xxx']`\n- Apply Rules:\n    - `delete://pathname.0` → Delete 'api'\n    - `delete://pathname.1` → Delete 'path'\n    - `delete://pathname.-1` → Delete 'xxx'\n- Final Path: `/to`\n- Complete Result: `https://www.example.com/to?query`\n\n### Indexing Rules\n- **Positive Index**: Starting at 0, indicating order from front to back\n- **Negative Index**: Starting at -1, indicating order from back to front (-1 = last segment, -2 = second to last segment, and so on)\n- **Special Value**: Use `last` removes the last segment of a path but preserves the trailing slash.\n- **Border case**: Out-of-range indices are ignored.\n\n### Important Note\nWhen a path ends with `/`, the split array will contain an empty string item at the end:\n- Path: `/api/path/to/xxx/`\n- Split result: `['api', 'path', 'to', 'xxx', '']`\n\n### Usage Tips\n- Use `pathname.-1` to remove the last segment without preserving the trailing slash.\n- Use `pathname.last` to remove the last segment but preserve the trailing slash.\n\n**Comparison example:**\n```\nwww.example.com/api/path/to delete://pathname.0 delete://pathname.1 delete://pathname.last\n```\n- Request: `https://www.example.com/api/path/to/xxx?query`\n- Result: `https://www.example.com/to/?query` (preserves the trailing slash)\n"
  },
  {
    "path": "docs/en/docs/rules/disable.md",
    "content": "# disable\nDisable HTTPS, hide requests, terminate requests, and other features through rules.\n\n## Rule Syntax\n``` txt\npattern disable://action1|action2|... [filters...]\n\n# Equivalent to:\npattern disable://action1 disable://action2 ... [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| action | Specific action, see the description below | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n- `301`: Disable `301` redirects, force `301` to `302` (can also use [replaceStatus](./replaceStatus))\n- `abort`: Disable the abort feature, see: [enable](./enable)\n- `abortReq`: Disable the abortReq feature, see: [enable](./enable)\n- `abortRes`: Disable the abortRes feature, see: [enable](./enable)\n- `authCapture`: Disable the `authCapture` feature, see: [enable](./enable)\n- `auto2http`: Disable the auto2http feature, see: [enable](./enable)\n- `autoCors`: When using the [file](./file) protocol to replace requests, if Whistle detects that the request is a cross-origin request, it automatically adds necessary CORS (Cross-Origin Resource Sharing) headers. This can be disabled via `disable://autoCors`\n- `ajax`: Remove the request header `x-requested-with`\n- `bigData`: Disable the bigData feature, see: [enable](./enable)\n- `capture` or `https`: Disable `Enable HTTPS`, see: [enable](./enable)\n- `captureIp`: Disable the captureIp feature, see: [enable](./enable)\n- `captureStream`: Disable the captureStream feature, see: [enable](./enable)\n- `clientCert`: Disable the clientCert feature, see: [enable](./enable)\n- `clientId`: Remove the request header `x-whistle-client-id`\n- `clientIp`: Remove the request header `x-forwarded-for`\n- `customParser`: Disable the customParser feature, see: [enable](./enable)\n- `cache`: Disable caching\n- `dnsCache`: Disable DNS caching\n- `csp`: Disable CSP\n- `cookies`: Disable cookies for requests and responses\n- `reqCookies`: Disable request cookies\n- `resCookies`: Disable response cookies\n- `flushHeaders`: Disable the flushHeaders feature, see: [enable](./enable)\n- `forHttp`: Disable the forHttp feature, see: [enable](./enable)\n- `forHttps`: Disable the forHttps feature, see: [enable](./enable)\n- `forceReqWrite`: Disable the forceReqWrite feature, see: [enable](./enable)\n- `forceResWrite`: Disable the forceResWrite feature, see: [enable](./enable)\n- `gzip`: Disable response compression\n- `h2`: Disable the h2 feature, see: [enable](./enable)\n  > Disable: Whistle proxy -xx-> server enables HTTP2\n- `http2`: Disable the http2 feature, see: [enable](./enable)\n  > Disable: Browser -xx-> Whistle proxy -xx-> server all enable HTTP2\n- `httpH2`: Disable the httpH2 feature, see: [enable](./enable)\n  > Disable: Whistle proxy -xx-> server HTTP requests enable HTTP2\n- `hide`: Disable the hide feature, see: [enable](./enable)\n- `hideComposer`: Disable the hideComposer feature, see: [enable](./enable)\n- `hideCaptureError`: Disable the hideCaptureError feature, see: [enable](./enable)\n- `interceptConsole`: Disable the interceptConsole feature, see: [enable](./enable)\n- `internalProxy`: Disable the internalProxy feature, see: [enable](./enable)\n- `proxyFirst`: Disable prioritizing the use of [proxy](./proxy) rules\n- `proxyHost`: Disable the proxyHost feature, see: [enable](./enable)\n- `proxyTunnel`: Disable the proxyTunnel feature, see: [enable](./enable)\n- `keepCSP`: Disable the keepCSP feature, see: [enable](./enable)\n- `keepAllCSP`: Disable the keepAllCSP feature, see: [enable](./enable)\n- `keepCache`: Disable the keepCache feature, see: [enable](./enable)\n- `keepAllCache`: Disable the keepAllCache feature, see: [enable](./enable)\n- `keepAlive`: Disable caching of request connections\n- `keepClientId`: Disable the keepClientId feature, see: [enable](./enable)\n- `keepH2Session`: No need for each TUNNEL connection to correspond to one HTTP2 connection (default is `yes`, recommended to use default)\n- `safeHtml`: Disable the safeHtml feature, see: [enable](./enable)\n- `strictHtml`: Disable the strictHtml feature, see: [enable](./enable)\n- `proxyConnection`: Set `proxy-connection: close` when forwarding requests via proxy, socks, or other proxy protocols\n- `ua`: Remove the request header `user-agent`\n- `proxyUA`: Remove the request header `user-agent` when forwarding requests via proxy, socks, or other proxy protocols\n- `referer`: Disable `referer`\n- `rejectUnauthorized`: Set `rejectUnauthorized` to `true`\n- `requestWithMatchedRules`: Disable the requestWithMatchedRules feature, see: [enable](./enable)\n- `responseWithMatchedRules`: Disable the responseWithMatchedRules feature, see: [enable](./enable)\n- `secureOptions`: Disable the default `options` for establishing HTTP2 connections\n- `timeout`: Disable request timeout settings\n- `trailerHeader`: Remove the response header `trailer`\n- `trailers`: Remove trailers\n- `tunnelAuthHeader`: Remove the proxy authentication header `proxy-authorization`\n- `tunnelHeadersFirst`: Disable the tunnelHeadersFirst feature, see: [enable](./enable)\n- `useLocalHost`: Disable the useLocalHost feature, see: [enable](./enable)\n- `useSafePort`: Disable the useSafePort feature, see: [enable](./enable)\n- `userLogin`: Disable showing the login box when setting [statusCode://401](./statusCode)\n- `weakRule`: Disable the weakRule feature, see: [enable](./enable)\n\n## Configuration Example\n``` txt\n# Disable request caching and remove cache fields from both request and response headers.\n# Unlike the cache protocol, cache only sets cache headers for responses.\nwwww.example.com/path disable://cache\n\n# Disable cookies for both requests and responses.\nwwww.example.com/path disable://cookies\n\n# Disable cookies for requests only.\nwwww.example.com/path disable://reqCookies\n\n# Disable cookies for responses only.\nwwww.example.com/path disable://resCookies\n\n# Remove user-agent.\nwwww.example.com/path disable://ua\n\n# Delete the referer\nwwww.test.com/path disable://referer\n\n# Delete the CSP policy\nwwww.test.com/path disable://csp\n\n# Disable the timeout. By default, Whistle will consider a request timed out if no data is transferred within 36 seconds.\nwwww.test.com/path disable://timeout\n\n# Convert 301 responses to 302s to prevent caching\nwwww.test.com/path disable://301\n\n# Disable HTTPS interception\nwwww.test.com/path disable://capture\n\n# Do not cache DNS results\nwwww.test.com/path disable://dnsCache\n\n# Disable proxy server request connection reuse\nwwww.test.com/path disable://keepAlive\n\n# Delete the `x-requested-with` request header\nwwww.test.com/path disable://ajax\n\n# You can also disable multiple requests simultaneously.\nwww.example.com/path disable://cache|cookies|ua|referer|csp|timeout|301|intercept|dnsCache|keepAlive|autoCors\n```\n\nAssociation operation: [enable](./enable)\n\n"
  },
  {
    "path": "docs/en/docs/rules/enable.md",
    "content": "# enable\nEnable HTTPS, hide requests, terminate requests, and other features through rules.\n\n## Rule Syntax\n``` txt\npattern enable://action1|action2|... [filters...]\n\n# Equivalent to:\npattern enable://action1 enable://action2 ... [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| action | Specific action, see the description below | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n- `abort`: Interrupt the request during the request or response phase (based on the matching phase)\n- `abortReq`: Interrupt the request during the request phase\n- `abortRes`: Interrupt the request during the response phase\n- `authCapture`: Force execution of the [auth hook](../extensions/dev#auth) before converting to HTTPS (default behavior is to execute the plugin's auth hook after converting to an HTTPS request)\n- `auto2http`: Enable automatic fallback to HTTP requests when HTTPS requests encounter TLS errors. By default, this is automatically enabled if the serverIP is a local IP\n- `bigData`: Increase the data display limit for captured packets (2M → 16M)\n- `br`: Enable BR compression for response content\n- `gzip`: Enable GZIP compression for response content\n- `deflate`: Enable Deflate compression for response content\n- `capture` or `https`: Enable HTTPS (same as the [HTTPS menu function](../gui/https.html))\n- `captureIp`: For requests where the domain is an IP, HTTPS requests are not decrypted by default. Use `enable://captureIp` to enable HTTPS request parsing\n- `captureStream`: Output captured request and response content in stream form to the capture interface in real time, with dynamic appending\n- `clientCert`: Enable mutual authentication (mTLS) between the client and server\n- `clientId`: Add the request header `x-whistle-client-id: Whistle locally generated unique ID`\n- `clientIp`: Set the `x-forwarded-for` request header for matched non-local requests to forward the client's real IP address to upstream services\n- `customParser`: Customize the display content of the capture interface. For usage, refer to the plugin: https://github.com/whistle-plugins/whistle.custom-parser\n- `flushHeaders`: Call [response.flushHeaders](https://nodejs.org/docs/latest/api/http.html#responseflushheaders) after `response.writeHead(...)` (enabled by default)\n- `forHttp`: Make the `capture` function only effective for HTTP requests\n- `forHttps`: Make the `capture` function only effective for HTTPS requests\n- `forceReqWrite`: When using [reqWrite](./reqWrite) or [reqWriteRaw](./reqWriteRaw) to write request data to a local file, if the corresponding file already exists, the write operation is skipped by default to protect the existing file. Use `enable://forceReqWrite` to force overwriting\n- `forceResWrite`: When using [resWrite](./resWrite) or [reqWriteRaw](./reqWriteRaw) to write response data to a local file, if the corresponding file already exists, the write operation is skipped by default to protect the existing file. Use `enable://forceResWrite` to force overwriting\n- `h2`: Enable HTTP2 from Whistle proxy → server\n- `http2`: Enable HTTP2 for browser → Whistle proxy → server\n- `httpH2`: Enable HTTP2 for HTTP requests from Whistle proxy → server\n- `hide`: Hide captured packet data on the interface (excluding `captureError` and requests sent by Composer)\n- `hideComposer`: Hide requests sent by Composer\n- `hideCaptureError`: Hide `captureError` requests\n- `showHost`: Set the server IP to the response header `x-host-ip`\n- `ignoreSend`: Ignore sending data frames for WebSocket and TUNNEL requests (TUNNEL requests require `inspect` to be enabled)\n- `ignoreReceive`: Ignore receiving data frames for WebSocket and TUNNEL requests (TUNNEL requests require `inspect` to be enabled)\n- `pauseSend`: Pause sending data frames for WebSocket and TUNNEL requests (TUNNEL requests require `inspect` to be enabled)\n- `pauseReceive`: Pause receiving data frames for WebSocket and TUNNEL requests (TUNNEL requests require `inspect` to be enabled)\n- `inspect`: Enable viewing of TUNNEL request content in Inspectors / Frames\n- `interceptConsole`: Intercept `console.xxx` requests and display them in the Log panel of the Whistle management interface (enabled by default)\n- `internalProxy`: Use proxy protocols like `proxy` or `socks` to forward requests to other proxy servers (e.g., another Whistle instance). After enabling this, HTTPS requests decrypted by the first-layer proxy will be transmitted in plain text within the proxy chain, allowing upstream proxies to directly access plain text data\n- `proxyFirst`: Prioritize using [proxy](./proxy) rules (by default, when both `host` and `proxy` match, only `host` takes effect)\n- `proxyHost`: Make both [proxy](./proxy) and [host](./host) take effect simultaneously\n- `proxyTunnel`: Use with `proxyHost` to allow the upstream proxy to tunnel through to an upper-layer HTTP proxy. See the example below for details\n- `keepCSP`: Automatically remove the `csp` field from response headers when injecting content via `htmlXxx`/`jsXxx`/`cssXxx`. Use `enable://keepCSP` to retain these fields\n- `keepAllCSP`: Automatically remove the `csp` field from response headers when injecting content via `htmlXxx`/`jsXxx`/`cssXxx`/`weinre`/`log`. Use `enable://keepAllCSP` to retain these fields\n- `keepCache`: Automatically remove cache fields from response headers when injecting content via `htmlXxx`/`jsXxx`/`cssXxx`. Use `enable://keepCache` to retain the original cache headers\n- `keepAllCache`: Automatically remove cache fields from response headers when injecting content via `htmlXxx`/`jsXxx`/`cssXxx`/`weinre`/`log`. Use `enable://keepAllCache` to retain the original cache headers\n- `keepClientId`: Retain the original `x-whistle-client-id` request header (by default, the incoming `x-whistle-client-id` is deleted)\n- `safeHtml`: A security mechanism that checks if the first non-whitespace character of the response content is `{` or `[` (indicating a JSON object) before injecting content via `htmlXxx`/`jsXxx`/`cssXxx` into an HTML page. If not, the injection proceeds. This prevents accidental injection into non-HTML responses (e.g., JSON interfaces)\n- `strictHtml`: A security mechanism that checks if the first non-whitespace character of the response content is `<` before injecting content via `htmlXxx`/`jsXxx`/`cssXxx` into an HTML page. If not, the injection proceeds. This prevents accidental injection into non-HTML responses (e.g., JSON interfaces)\n- `multiClient`: When Whistle acts as a public proxy with `enable://clientId` enabled, a fixed `x-whistle-client-id` header is added to all requests, preventing upstream services from distinguishing between clients. Enabling `enable://multiClient` generates and maintains a unique and consistent identifier for each client connection, ensuring upstream services can accurately identify request sources\n- `requestWithMatchedRules`: Include the currently matched rules in the request headers\n- `responseWithMatchedRules`: Include the currently matched rules in the response headers\n- `tunnelHeadersFirst`: Controls the priority of request header merging. Plugins can pass tunnel (TUNNEL) request headers to subsequent stages via [tunnelKey](../extensions/dev). The default merging rule is: if a tunnel header and a parsed regular request header have the same key, the regular request header value is retained. Enabling `enable://tunnelHeadersFirst` changes this behavior, ensuring tunnel request headers take priority and override any conflicting regular headers\n- `useLocalHost`: Modify the domain of `log` and `weinre` request URLs to use built-in domains\n- `useSafePort`: Modify the port of `log` and `weinre` request URLs to use built-in ports\n- `userLogin`: Set whether [statusCode://401](./statusCode) displays a login box (enabled by default)\n- `weakRule`: By default, when protocols like [file](./file) are configured, [proxy](./proxy) rules automatically become invalid. Setting the `weakRule` property increases the priority of [proxy](./proxy) rules, allowing them to remain effective in such scenarios\n- `socket`: After enabling HTTPS parsing (`Enable HTTPS` or `enable://https`), TUNNEL requests sent to ports `80/443` are forcibly attempted to be parsed as HTTP/HTTPS traffic. By default, if parsing fails, the connection is destroyed; requests to other ports continue as TUNNEL connections if parsing fails. Setting `enable://socket` allows requests to ports `80/443` to degrade to TUNNEL connections if parsing fails, avoiding connection destruction\n- `websocket`: Used to handle non-standard WebSocket connections. Some requests use the WebSocket protocol for transmission but have non-standard Upgrade headers (e.g., `Upgrade: websocket`). By default, Whistle treats them as ordinary TCP connections without parsing the data. Enabling `enable://websocket` forces Whistle to recognize such connections as WebSocket protocols and parse the data\n\n## Configuration Example\n``` txt\n# Enable HTTPS\nwww.example.com enale://https\n\n# Abort the request with a 3000ms delay\nwww.example.com/path reqDelay://3000 enable://abortReq\n\n# Abort the response with a 5000ms delay\nwww.example.com/path resDelay://5000 enable://abortRes\n\n# Enable GZIP for local replacement content\nwww.example.com/path file:///User/xxx/test enable://gzip\n\n# Set hosts for the upstream proxy (10.10.10.20:8888)\nwww.example.com/path proxy://10.1.1.1:8080 10.10.10.20:8888 enable://proxyHost\n\n# Tunnel requests through the upstream HTTP proxy (10.1.1.1:8080) to the specified HTTP proxy (10.10.10.20:8080)\nwww.example.com proxy://10.1.1.1:8080 10.10.10.20:8080 enable://proxyHost|proxyTunnel\n\n# Enable HTTP2 for the entire connection from browser to Whistle proxy to server Functionality\nwww.example.com enable://http2\n\n# Enable HTTP2 functionality for Whistle proxy -> server\nwww.example.com enable://h2\n\n# Force HTTP requests from Whistle proxy -> server to use HTTP2 transport\nwww.example.com enable://httpH2\n\n# Safe injection mode: When using the htmlXxx/jsXxx/cssXxx injection directives, inject only if the first character of the response is not `{`\nwww.example.com/path enable://safeHtml\n\n# Strict HTML injection mode: When using the htmlXxx/jsXxx/cssXxx injection directives, inject only if the first character of the response is not `<`\nwww.example.com/path enable://strictHtml\n\n# Automatically add the x-forwarded-for request header to convey the client's real IP address\nwww.example.com enable://clientIp\n\n# Expand the packet capture data display limit (2MB → 16MB)\nwww.example.com/path enable://bigData\n\n# Modify the domain name or port of the log/weinre request URL.\nwww.example.com/path enable://useLocalHost | useSafePort\n\n# Force reqWrite/reqWriteRaw/resWrite/resWriteRaw to overwrite existing files.\nwww.example.com/path enable://forceReqWrite | forceResWrite\n\n# Force HTTPS requests to execute the auth hook before parsing (the default is to execute the plugin's auth hook after converting them to HTTPS).\nwww.example.com enable://authCapture\n\nAssociated Action: [disable](./disable)\n"
  },
  {
    "path": "docs/en/docs/rules/excludeFilter.md",
    "content": "# excludeFilter\nBased on matching [pattern](./pattern), further filter requests that meet the specified conditions (requests that meet any of the conditions will be retained).\n\n## Syntax Rules\n``` txt\npattern operation excludeFilter://p1 excludeFilter://p2 ...\n```\n> Requests must match `pattern` and not any of `p1`, `p2`, ... for this to take effect.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| operation | Operation Instructions | [Operation Instructions Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Not effective for GET or POST requests only\nwww.example.com/api/data proxy://127.0.0.1:8080 excludeFilter://m:GET excludeFilter://m:post\n```\n\nCan be used with [includeFilter](./includeFilter). For detailed usage, see [Filter Documentation](./filters).\n"
  },
  {
    "path": "docs/en/docs/rules/file.md",
    "content": "# file\n\nThe `file` protocol is used to map requests to the local file system, returning the content of local files as responses to the client. It is suitable for the following scenarios:\n\n- **Setting up a local development environment**: Quickly set up a local development server.\n- **Debugging local front-end pages**: Directly debug local HTML, CSS, and JavaScript files.\n- **API Mocking scenarios**: Use local JSON files to simulate API responses.\n- **Static resource serving**: Use a local directory as a static resource server.\n\n## Rule Syntax\n\nThe `file` protocol supports multiple ways to specify local file content:\n\n### 1. Inline Value (Direct Specification)\nWrite the content to be returned directly in the rule, suitable for short text content. Note: `value` cannot contain spaces or line breaks.\n\n```txt\npattern file://(value) [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/api/data file://({\"status\":\"ok\"})\n```\n\n**Note: Inline values must be wrapped in parentheses `()`; otherwise, they will be interpreted as local file paths, which may lead to 404 errors:**\n```txt\n# Correct: Returns the string \"success\"\npattern file://(success)\n\n# Incorrect: Will be treated as a path, potentially causing 404\npattern file://success\n```\n\n> **Key points about the inline value syntax of the file protocol**:\n> - Must use `()` to wrap the content.\n> - Content cannot contain spaces or line breaks.\n> - Can be a string, JSON, HTML fragment, etc.\n> - Without `()`, Whistle will interpret it as a file path or `{key}` and attempt to load the corresponding file or key value.\n\n### 2. Embedded Value (Using Code Block)\nThis method is recommended when dealing with complex content containing spaces, line breaks, or when you want to reuse a configuration.\n\n````txt\npattern file://{custom-key.json} [lineProps...] [filters...]\n\n``` custom-key.json\n{\n  \"status\": \"success\",\n  \"data\": {\n    \"message\": \"Hello from local file\"\n  }\n}\n```\n````\n\n**Best Practice**: It is recommended to add a response type suffix (e.g., `.json`, `.html`, `.css`) to the `key`. Whistle will automatically set the correct `Content-Type` based on the suffix.\n\n### 3. Referencing a Value from the Values Panel\nWhen the content is large, you can store it in the `Values` configuration area.\n\n```txt\npattern file://{key-of-values.html} [lineProps...] [filters...]\n```\n\n**Prerequisite**: A key named `key-of-values.html` with the content to be returned as its value must exist in `Values`.\n\n**Capacity Recommendations**:\n- Content less than 2KB: Recommended to use inline or embedded methods.\n- Content between 2KB and 200KB: Recommended to store in `Values`.\n- Content larger than 200KB: Recommended to use local files.\n\n### 4. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern file://temp.html\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows).\n2. Click with the mouse on `file://temp.html`.\n3. Enter the response content in the pop-up editing dialog.\n4. Click `Save` to save.\n\nAfter saving, the rule will automatically change to a format similar to this:\n```txt\nhttps://example.com/report file://temp/11adb9c9e1142df67b30d7646ec59bcd34c855d9011d1a2405c7fc2dfc94568d.html\n```\n\nTo edit again, click the temporary file link in the same way.\n\n### 5. Loading from a File Path or Remote URL\nLoad the response content from the local file system or a remote URL.\n\n```txt\n# Load from a local file\npattern file:///User/xxx/test.html\n\n# Load from a remote URL\npattern file://https://example.com/template.html\n```\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | The file content or path to return, supporting multiple formats:<br>• Local file or directory path<br>• Remote URL<br>• Inline, embedded, Values references |\n| **lineProps** | No | Sets additional properties for the rule.<br>**Examples**:<br>• `lineProps://important`: Increase rule priority.<br>• `lineProps://weakRule`: When both [file](./file) and [proxy](./proxy) rules are configured, the [proxy](./proxy) rule will be disabled by default. This property can increase the priority of the [proxy](./proxy) rule.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### 1. Basic File Path Mapping\n```txt\n# Map a domain to a local directory\nwww.example.com/path file:///Users/username/projects/my-site\n\n# Windows system path\nwww.example.com/path file://D:\\projects\\my-site\n```\n\n**Feature Description**:\n- **Automatic path concatenation**: Accessing `https://www.example.com/path/x/y/z` will map to `/Users/username/projects/my-site/x/y/z`.\n- **Disable path concatenation**: Use `< >` to wrap the path to disable automatic concatenation.\n    ```txt\n    www.example.com/path file://</Users/username/projects/my-site/index.html>\n    ```\n    > Accessing `https://www.example.com/path/x/y/z` will only return the file `/Users/username/projects/my-site/index.html`.\n\n### 2. JSONP Interface Mocking\n````txt\n# Inline method\nwww.example.com/jsonp file://`(${query.callback}({\"status\":\"ok\"}))`\n\n# Embedded method\n``` jsonp-response\n${query.callback}({\n  \"status\": \"error\",\n  \"message\": \"Invalid parameter\"\n})\n```\nwww.example.com/jsonp file://`{jsonp-response}`\n````\n\n> **Template String Limitations**: Template strings cannot take effect directly in the following scenarios:\n> - When referencing local file paths.\n> - When referencing remote URLs.\n> \n> When encountering these limitations, you can use the [tpl](./tpl) protocol as an alternative.\n\n### 3. Using with Filters\n```txt\n# Exclude specific interfaces\nwww.example.com/api/path file:///Users/username/mock-data excludeFilter://*/api/auth\n\n# Match based on request body content\nwww.example.com/api/search file:///Users/username/search-results.json includeFilter://b:/\"type\":\\s*\"advanced\"/i\n\n# Only match POST requests\nwww.example.com/api/submit file:///Users/username/success.json includeFilter://m:POST\n```\n\n### 4. Multi-directory Search\n```txt\n# Multiple files or directories separated by `|`\nwww.example.com/static/path file:///path/to/project1|/path/to/project2|/path/to/project3\n```\n\n**Search Logic**:\n1. Check each directory in order:\n   - `/path/to/project1/static/file.js`\n   - `/path/to/project2/static/file.js`\n   - `/path/to/project3/static/file.js`\n2. Return the first existing file found.\n3. If none are found, return `404 Not Found`.\n\n## Advanced Usage\n\n### Dynamic Path Handling\n```txt\n# Use regex capture groups\n/^https?://www\\.example\\.com/user/(\\d+)/profile file:///Users/username/mock/profiles/user-$1.json\n\n# If not limited to numbers, you can use wildcards\n^www.example.com/user/*/profile file:///Users/username/mock/profiles/user-$1.json\n```\n\n### Environment-Specific Configuration\n````txt\n``` dev-config\n{\n  \"apiBase\": \"http://localhost:3000\",\n  \"debugMode\": true\n}\n```\n\n``` prod-config\n{\n  \"apiBase\": \"https://api.example.com\",\n  \"debugMode\": false\n}\n```\n\n# Determine if it's a development environment via cookie: env=dev\nwww.example.com/config file://{dev-config} includeFilter://reqH:cookie=/env=dev/\nwww.example.com/config file://{prod-config} # Default production environment\n````\n\n### Combining with Other Protocols\n```txt\n# First use file to provide static files, then set response headers\nwww.example.com file:///Users/username/static-files cache://3600\n\n# For non-existent files, use xfile to allow the request to continue\nwww.example.com xfile:///Users/username/static-files\n```\n\n## Differences from resBody\n\nThe main distinctions between the `file` protocol and the [`resBody`](./resBody) protocol lie in the request processing flow and response mechanism:\n\n**`file` Protocol:**\n- **Request Flow**: Requests matching the `file` protocol are **not sent to the backend server**.\n- **Response Mechanism**: Directly returns the content of the specified file with a 200 status code.\n- **Network Overhead**: Zero network latency; entirely processed locally.\n\n**Diagram:**\n```\nClient Request → Whistle (matches file rule) → Reads Local File → Returns Response (200)\n                        ↓\n                  (Not sent to server)\n```\n\n**`resBody` Protocol:**\n- **Request Flow**: The request **is first sent to the backend server** to obtain the original response.\n- **Response Mechanism**: After receiving the server's response, **replaces** the original response body with the specified content.\n- **Network Overhead**: Includes a complete request-response network round trip.\n\n**Diagram:**\n```\nClient Request → Whistle → Sends to Server → Receives Original Response → Replaces Response Body → Returns Modified Response\n```\n\n## Related Protocols\n\n1. **Allow unmatched files to continue access**: [xfile](./xfile)\n   - When a local file does not exist, allow the request to continue to the original server.\n   - Suitable for development scenarios with static resource servers.\n\n2. **Replace with remote URL content**: [https](./https) or [http](./http)\n   - Use content from a remote server as the response.\n   - Note: It is not recommended to use the form `file://https://xxx`.\n\n3. **Domain resolution redirection**: [host](./host)\n   - Modify domain name resolution to direct requests to a specified IP.\n\n4. **Template rendering**: [tpl](./tpl)\n   - Supports more complex template rendering and dynamic content generation.\n\n## Notes\n\n### Path Handling\n1. **Relative paths**: Except for rules inside plugins, it is not recommended to use relative paths. Relative paths are relative to the directory where the Whistle configuration file is located.\n2. **Absolute paths**: System absolute paths are supported.\n3. **User directory**: `~` is supported to represent the user's home directory.\n4. **Windows paths**: Mixing `/` and `\\` as path separators is supported.\n\n### File Encoding\n1. **Text files**: UTF-8 encoding is used by default.\n2. **Encoding issues**: If you encounter garbled characters, check the actual encoding format of the file.\n\n## Troubleshooting\n\n### Q: File Not Found (404)\n**A:** Check:\n1. Whether the file path is correct.\n2. Whether the file exists and has read permissions.\n3. Whether `< >` was used to disable path concatenation.\n\n### Q: Garbled Characters\n**A:** Check:\n1. The actual encoding format of the file.\n2. Whether the `Content-Type` response header is correct.\n3. Whether the file content contains illegal characters.\n\n### Q: Rule Not Taking Effect\n**A:** Check:\n1. Whether the rule pattern matches correctly.\n2. Whether other rules are overriding it.\n3. Whether the filter conditions are met.\n\n### Q: Template Strings Not Taking Effect\n**A:** Check:\n1. Whether template strings are used in file path or remote URL scenarios.\n2. If so, use the [tpl](./tpl) protocol instead.\n\n## Further Reading\n\n- [Matching Pattern Documentation](./pattern): Learn more about URL matching rules.\n- [Operation Commands Documentation](./operation): Learn about multiple ways to load content.\n- [Additional Configuration](./lineProps): Learn more about special features.\n- [Filters](./filters): Learn more about filter functionalities.\n"
  },
  {
    "path": "docs/en/docs/rules/filters.md",
    "content": "# Filters\n\nWhen matching based on request or response attributes (not just the URL), you can use filters to achieve more granular rule control. The syntax is:\n\n``` txt\npattern operator includeFilter://pattern1 ... excludeFilter://patternx ...\n```\n> Multiple filters are matched \"or\"; as long as one of the filter conditions matches, the match is true.\n\n## Filter Types\n\n| Filter Type | Syntax | Purpose |\n| -------------- | ------------------------- | ------------------------ |\n| **Include Filter** | `includeFilter://pattern` | Match only requests that match the specified conditions |\n| **Exclude Filter** | `excludeFilter://pattern` | Exclude requests that match the specified conditions |\n\n## Pattern Types\n\n| Syntax | Purpose | Example |\n| ------------------- | --------------------- | ----------------------------- |\n| `b:pattern` | Match request body content | `includeFilter://b:keyword` `excludeFilter://b:/regexp/[i]` |\n| `m:pattern` | Match HTTP method | `includeFilter://m:keyword` `excludeFilter://m:/regexp/[i]` |\n| `i:pattern` | Match client or server IP | `includeFilter://i:keyword` `excludeFilter://i:/regexp/[i]` |\n| `chance:probability`| `Math.random() < probability` | `includeFilter://chance:0.5` `excludeFilter://chance:0.3` |\n| `clientIp:pattern` | Match only client IP | `includeFilter://clientIp:/regexp/[i]` `excludeFilter://clientIp:keyword` |\n| `serverIp:pattern` | Match only server IP | `includeFilter://serverIp:/regexp/[i]` `excludeFilter://serverIp:keyword` |\n| `s:pattern` | Match response status code | `includeFilter://s:/^20/` `excludeFilter://s:30` |\n| `h:name=pattern` | Match request/response headers | `includeFilter://h:content-type=json` `excludeFilter://h:content-type=/regexp/i` |\n| `reqH:name=pattern` | Match request headers only | `includeFilter://reqH:content-type=json` `excludeFilter://reqH:content-type=/regexp/i` |\n| `resH:name=pattern` | Match response headers only | `includeFilter://resH:content-type=json` `excludeFilter://resH:content-type=/regexp/i` |\n| Other `xxxxxx` | Match request URLs (same as [pattern](./pattern)) | `includeFilter://*/cgi-*` `excludeFilter://www.test.com` `includeFilter://https://www.test.com/path` |\n"
  },
  {
    "path": "docs/en/docs/rules/forwardedFor.md",
    "content": "# forwardedFor\nModify the `x-forwarded-for` request header field to customize the client IP address.\n\n## Rule Syntax\n``` txt\npattern forwardedFor://ip [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| ip | Custom client IP<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Setting a fixed IP address\nwww.example.com/path forwardedFor://1.1.1.1\n\n# Using the real client IP address (transparent mode)\nwww.example.com forwardedFor://`${clientIp}`\n```\n\n## Associated Protocols\n1. Directly modify the request header: [reqHeaders://x-forwarded-for=value](./reqHeaders) (the `value` in this method is not limited to the IP address)\n2. Enable automatic `x-forwarded-for`: [enable://clientIp](./enable)\n"
  },
  {
    "path": "docs/en/docs/rules/frameScript.md",
    "content": "# frameScript\nManipulate WebSocket and regular TCP request data frames through JavaScript scripts.\n\n## Rule Syntax\n``` txt\npattern frameScript://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | JS script to generate rules. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values ​​Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n``` test-frame.js\n// Send preset data\nctx.sendToServer('1 = 0x12300000');\nctx.sendToClient('1 = 0x1236666666');\n\n// Process the data frame sent to the server\nctx.handleSendToServerFrame = function(buf, opts) {\n// Can return empty, null, undefined, etc.\nreturn (buf + '').replace(/1/g, '***');\n};\n\n// Process the data frame sent to the client\nctx.handleSendToClientFrame = function(buf, opts) {\n// Can return empty, null, undefined, etc.\nreturn (buf + '').replace(/1/g, '+++');\n};\n```\n\nwss://echo.websocket.org/ frameScript://{test-frame.js}\n````\nVisiting `https://echo.websocket.org/.ws` Results:\n\n<img width=\"1200\" alt=\"frame-script\" src=\"/img/frame-script.png\" />\n\n#### Available Global Variables\n\n| Variable/Method | Description |\n|--------------------|---------------------------------------------------------------------|\n| `url` | Full request URL |\n| `method` | Request method (GET/POST, etc.) |\n| `ip`/`clientIp` | Client IP address |\n| `headers` | Request header object |\n| `rules` | Rule array, add new rules via push |\n| `values` | Temporary value storage object |\n| `render(tpl,data)` | Micro template rendering function |\n| `getValue(key)` | Get the value in `Values` |\n| `parseUrl` | Same as `url.parse` in Node.js |\n| `parseQuery` | Same as `querystring.parse` in Node.js |\n| `sendToClient(frame, options)` | Sends data (object, string, buffer) to the client. See the example below. |\n| `sendToServer(frame, options)` | Sends data (object, string, buffer) to the server. See the example below. |\n| `handleSendToClientFrame(buffer, options)` | Processes (filters or deletes) data sent to the client. See the example below. |\n| `handleSendToServerFrame(buffer, options)` | Processes (filters or deletes) data sent to the server. See the example below. |\n\n"
  },
  {
    "path": "docs/en/docs/rules/headerReplace.md",
    "content": "# headerReplace\nReplaces the specified request/response header by matching a keyword or regular expression.\n\n## Rule Syntax\n``` txt\npattern headerReplace://value [filters...]\n```\n> `header-name` is case-insensitive\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Three cases: <br/>• `req.header-name:p1=v1&p2=v2`<br/>• `res.header-name:p1=v1&p2=v2`<br/>• `trailer.trailer-name:p1=v1&p2=v2` | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching: <br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Change the first `html` keyword in the `accept` request header field to `abc`\nwww.example.com/path headerReplace://req.accept:html=abc\n\n# Change all `ml` keywords in the `accept` request header field to `abc`\nwww.example.com/path2 headerReplace://req.accept:/ml/g=abc\n\n# Modify the response header\nwww.example.com/path3 headerReplace://res.Content-Type:html=plain\n```\n\n`headerReplace` is used to replace partial request/response header content. To modify request/response field content, you can also use:\n- Set request headers: [reqHeaders](./reqHeaders)\n- Set response headers: [resHeaders](./resHeaders)\n"
  },
  {
    "path": "docs/en/docs/rules/host.md",
    "content": "# host\nThis command modifies the DNS resolution result of a request, resolving the specified request to a specific IP address (domain name) and port. This can be considered the ultimate system hosts configuration feature.\n\n## Rule Syntax\n``` txt\npattern host://ipOrDomain[:port] [filters...]\n```\n> If `port` is left blank, the original port of the request URL will be used. If it points to a domain name, it functions like `cname`.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | IP + optional port or domain name + optional port<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Can be omitted if only the IP or port is specified `host://`\nwww.example.com/test0 127.0.0.1 # Do not change the port; use the original port in the request URL.\nwww.example.com/test1 127.0.0.1:5173\n\n# CNAME functionality\nwww.example.com/test2 host://www.test.com\nwww.example.com/test3 host://www.test.com:8080\n\n# Advanced configuration: Get the target address from the request parameters. IncludeFilter ensures the parameter exists.\nwww.example.com/test4 host://`${query.target}$:8080` includeFilter:///[?&]target=[\\w-]+/i\n```\n## Matching precedence with proxy\nThe following is an optimized document with clearer structure and more concise and accurate language:\n\n---\n\n## Matching precedence with host\n\n#### Default behavior\n\nWhen a request matches both `host` and `proxy` When configuring rules:\n- Only `host` rules take effect\n- `proxy` rules are automatically ignored\n\n#### Changing Priority\n| Configuration Method | Syntax | Effect |\n|--------|------|------|\n| **Prioritize proxy** | [`enable://proxyFirst`](./enable) or [`lineProps://proxyFirst`](./lineProps) | Only `proxy` takes effect (overrides `host`) |\n| **Both take effect** | [`enable://proxyHost`](./enable) or [`lineProps://proxyHost`](./lineProps) | Both `proxy` and `host` take effect |\n\n#### Usage Recommendations\n- Use the default behavior in most scenarios\n- Use `proxyFirst` only when special proxy logic is required\n- Use `proxyHost` when dual matching is required\n\n## Notes\n\nThe `host` protocol only applies to the substituted URL (i.e., the `Final URL` shown in the Overview). If the `Final URL` is empty, it will take effect on the original request URL.  \n\nFor example, with the rule:  \n```  \nwww.example.com/api www.example.com 127.0.0.1:1234  \n```  \nA request to `https://www.example.com/api/path` will be processed by Whistle, and the `Final URL` becomes `https://www.example.com/path`. As a result, it will not match `127.0.0.1:1234`.  \n\nIf you want to match the substituted request, you can modify the rules as follows:  \n```  \nwww.example.com/api www.example.com  \nwww.example.com 127.0.0.1:1234  \n```\n\n## FAQ\n1. Differences from URL conversion:\n    ``` txt\n    # The URL received by the server is still www.example.com\n    www.example.com 127.0.0.1:5173\n\n    # The URL received by the server is http://127.0.0.1:5173\n    www.example.com http://127.0.0.1:5173\n    ```\n2. Automatic downgrade to HTTP requests: If the configured target IP is `127.0.0.1` and HTTPS requests report errors, they will automatically downgrade to HTTP requests to facilitate access to local services. Users can disable this feature using the following rule:\n    ``` txt\n    # Disable automatic downgrade of local HTTPS requests\n    pattern disable://auto2http\n    ```\n"
  },
  {
    "path": "docs/en/docs/rules/htmlAppend.md",
    "content": "# htmlAppend\nAppends the specified content after the existing response body. (This only works for responses with `content-type` containing `html` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern htmlAppend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path htmlAppend://(Hello) file://(-test-)\nwww.example.com/path2 htmlAppend://(Hello) file://(-test-) resType://js\n```\n- Requesting `https://www.example.com/path/to` results in a response of `-test-Hello`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-`\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlAppend://{body.txt} file://(-test-)\nwww.example.com/path2 htmlAppend://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path/to` results in a response of `-test-Hello world.`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-`\n\n#### Avoiding Non-Standard Requests\nWhen the API response type (Content-Type) is not standardized and returned as `text/html`, this may result in:\n- The front-end mistakenly parsing the API data as HTML\n- Injected content corrupts the original data structure\n- Triggering front-end parsing errors\n\nUse `enable://strictHtml` or `enable://safeHtml` mode to protect non-HTML content:\n```` txt\nwww.example.com/path1 htmlAppend://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlAppend://(test) file://([-test-]) enable://strictHtml\nwww.example.com/path3 htmlAppend://(test) file://([-test-]) enable://safeHtml\nwww.example.com/path4 htmlAppend://(test) file://(<div>Test</div>) enable://strictHtml\n````\n- Requesting `https://www.example.com/path1/to` results in a response of `-test-`\n- Requesting `https://www.example.com/path2/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path3/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path4/to` results in a response of `<div>Test</div>test`\n\n`safeHtml`/`strictHtml` Function reference: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n\n#### Local/Remote Resources\n\n``` txt\nwww.example.com/path1 htmlAppend:///User/xxx/test.txt\nwww.example.com/path2 htmlAppend://https://www.xxx.com/xxx/params.txt\n# Editing a temporary file\nwww.example.com/path3 htmlAppend://temp/blank.txt\n```\n\n## Associated Protocols\n1. Inject content before the response: [reqAppend](./reqAppend)\n2. Inject content before HTML-type response content: [htmlPrepend](./htmlPrepend)\n3. Replace HTML-type response content: [htmlBody](./htmlBody)\n4. Validate HTML Content format: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n"
  },
  {
    "path": "docs/en/docs/rules/htmlBody.md",
    "content": "# htmlBody\n\nReplaces the existing response body with the specified content. (This only works for responses with `content-type` containing `html` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern htmlBody://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path htmlBody://(Hello) file://(-test-)\nwww.example.com/path2 htmlBody://(Hello) file://(-test-) resType://js\n```\n- Requesting `https://www.example.com/path/to` results in a response of `Hello`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-`\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlBody://{body.txt} file://(-test-)\nwww.example.com/path2 htmlBody://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path/to` results in a response of `Hello world.`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-`\n\n#### Avoiding Non-Standard Requests\nWhen the API response type (Content-Type) is not standardized and returned as `text/html`, this may result in:\n- The front-end mistakenly parsing the API data as HTML\n- Injected content corrupts the original data structure\n- Triggering front-end parsing errors\n\nUse `enable://strictHtml` or `enable://safeHtml` mode to protect non-HTML content:\n``` txt\nwww.example.com/path1 htmlBody://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlBody://(test) file://([-test-]) enable://strictHtml\nwww.example.com/path3 htmlBody://(test) file://([-test-]) enable://safeHtml\nwww.example.com/path4 htmlBody://(test) file://(<div>Test</div>) enable://strictHtml\n```\n- Requesting `https://www.example.com/path1/to` results in a response of `-test-`\n- Requesting `https://www.example.com/path2/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path3/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path4/to` results in a response of `test`\n\n`safeHtml`/`strictHtml` feature reference: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 htmlBody:///User/xxx/test.txt\nwww.example.com/path2 htmlBody://https://www.xxx.com/xxx/params.txt\n# Editing a temporary file\nwww.example.com/path3 htmlBody://temp/blank.txt\n````\n\n## Associated Protocols\n1. Replace the response content: [resBody](./resBody)\n2. Inject content before the HTML response content: [htmlPrepend](./htmlPrepend)\n3. Inject content after the HTML response content: [htmlBody](./htmlBody)\n4. Validate the HTML content format: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n"
  },
  {
    "path": "docs/en/docs/rules/htmlPrepend.md",
    "content": "# htmlPrepend\nInserts the specified content before the existing response body. (This only works for responses with `content-type` containing `html` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: 204, 304, and other requests without a body are not affected.\n\n## Rule Syntax\n``` txt\npattern htmlPrepend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path htmlPrepend://(Hello) file://(-test-)\nwww.example.com/path2 htmlPrepend://(Hello) file://(-test-) resType://js\n```\n- Request `https://www.example.com/path/to`. The response content becomes\n    ``` html\n    <!DOCTYPE html>\n    Hello-test-\n    ```\n- Request `https://www.example.com/path2/to`. The response content becomes `-test-`\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path htmlPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 htmlPrepend://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path/to` results in the response content becoming\n    ``` html\n    <!DOCTYPE html>\n    Hello world.-test-\n    ```\n- Requesting `https://www.example.com/path2/to` results in the response content becoming `-test-`\n\n#### Avoiding Non-Standard Requests\nWhen the API response type (Content-Type) is non-standardly returned as `text/html`, this may result in:\n- The frontend mistakenly parsing the API data as HTML\n- Injecting content that corrupts the original data structure\n- Causing frontend parsing errors\n\nUse enable://strictHtml or enable://safeHtml mode to protect non-HTML content:\n``` txt\nwww.example.com/path1 htmlPrepend://(test) file://(-test-) enable://strictHtml\nwww.example.com/path2 htmlPrepend://(test) file://([-test-]) enable://strictHtml\nwww.example.com/path3 htmlPrepend://(test) file://([-test-]) enable://safeHtml\nwww.example.com/path4 htmlPrepend://(test) file://(<div>Test</div>) enable://strictHtml\n```\n- Requesting `https://www.example.com/path1/to` results in a response of `-test-`\n- Requesting `https://www.example.com/path2/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path3/to` results in a response of `[-test-]`\n- Requesting `https://www.example.com/path4/to` results in a response of\n    ``` html\n    <!DOCTYPE html>\n    test<div>Test</div>\n    ```\n\n`safeHtml`/`strictHtml` function reference: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 htmlPrepend:///User/xxx/test.txt\nwww.example.com/path2 htmlPrepend://https://www.xxx.com/xxx/params.txt\n# Editing a temporary file\nwww.example.com/path3 htmlPrepend://temp/blank.txt\n````\n\n## Associated Protocols\n1. Inject content before the response: [reqPrepend](./reqPrepend)\n2. Replace the HTML response content: [htmlBody](./htmlBody)\n3. In HTML Inject content after the response content of the type: [htmlPrepend](./htmlPrepend)\n4. Verify the HTML content format: [enable://safeHtml](./enable), [lineProps://strictHtml](./lineProps)\n"
  },
  {
    "path": "docs/en/docs/rules/http.md",
    "content": "# http\nConvert the following three requests into HTTP requests (the server will receive the converted HTTP URL):\n1. **Tunnel proxy:** `tunnel://domain:port`\n    > Example: `tunnel://www.test.com:443`\n2. **WebSocket:** `ws[s]://domain[:port]/[path/to[?query]]`\n    > Example: `wss://www.test.com/path?a=1&b=2`\n3. **Normal HTTP/HTTPS:** `http[s]://domain[:port]/[path/to[?query]]`\n    > Example: `https://www.test.com/path?a=1&b=2`\n\n## Rule Syntax\n``` txt\npattern http://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]/[path][?query]`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## HTTP/HTTPS Conversion\n``` txt\nhttp://www.example.com/path1 http://www.test.com/path/xxx\nhttps://www.example.com/path2 http://www.abc.com/path3/yyy\n```\n1. Automatic Path Concatenation:\n    | Original Request | Conversion Result (URL Received by the Server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `http://www.example.com/path1` | `http://www.test.com/path/xxx` |\n    | `http://www.example.com/path1/a/b/c?query` | `http://www.test.com/path/xxx/a/b/c?query` |\n    | `https://www.example.com/path2` | `http://www.abc.com/path3/yyy` |\n    | `https://www.example.com/path2/a/b/c?query` | `http://www.abc.com/path3/yyy/a/b/c?query` |\n2. Disable path concatenation: Use `< >` or `()` to wrap the path.\n    ``` txt\n    www.example.com/path1 http://<www.test.com/path/xxx>\n    # www.example.com/path1 http://(www.test.com/path/xxx)\n    ```\n    | Original request | Conversion result (URL received by the server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `http://www.test.com/path/xxx` |\n\n## WebSocket Conversion\n``` txt\nws://www.example.com/path1 http://www.test.com/path/xxx\nwss://www.example.com/path2 http://www.abc.com/path3/yyy\n```\nThe WebSocket request is replaced with the specified ws request:\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1` | `ws://www.test.com/path/xxx` |\n| `wss://www.example.com/path2/a/b/c?query` | `ws://www.abc.com/path3/yyy/a/b/c?query`|\n\nAlso supports **automatic path concatenation** and **disabling path concatenation**.\n\n## TUNNEL Conversion\n``` txt\ntunnel://www.example.com:443 http://www.test.com:123\ntunnel://www.example2.com:443 http://www.test2.com/path\n```\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443` | `tunnel://www.test.com:123` |\n| `tunnel://www.example2.com:443` | `tunnel://www.test2.com:80`|\n\nAutomatically ignores the path of the matching URL. The default port for HTTP is `80`, and the default port for HTTPS is `443`.\n"
  },
  {
    "path": "docs/en/docs/rules/https-proxy.md",
    "content": "# https-proxy\nThe `https-proxy` directive is used to forward matching requests through a specified HTTPS proxy server.\n\n## Rule Syntax\n``` txt\npattern https-proxy://ipOrDomain[:port] [filters...]\n```\n> `port` is optional. If left blank, the default port `443` will be used.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | IP + optional port or domain name + optional port<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Proxy requests to an HTTPS proxy: `127.0.0.1:443`\nwww.example.com/path https-proxy://127.0.0.1 # Default port 443\n\n# Proxy all requests for the current domain to the HTTPS proxy: `127.0.0.1:8080`\nwww.example.com https-proxy://127.0.0.1:8080\n\n# You can also use a domain name.\nwww.example.com/path https-proxy://test.proxy.com # Default port 443\nwww.example.com https-proxy://test.proxy.com:8080\n```\n\n## Advanced Usage\nBy default, the upstream proxy resolves the requested domain name. However, in some scenarios, you may want to force the proxy to access a specific target IP directly (skipping DNS resolution), for example:\n- Bypassing DNS poisoning\n- Directly accessing a specific backend IP\n- Testing services in different environments\n``` txt\n# Using query parameters\nwww.example.com https-proxy://127.0.0.1:8080?host=1.1.1.1\nwww.example.com https-proxy://127.0.0.1:8080?host=1.1.1.1:8080\n\n# Enabling via directives\nwww.example.com https-proxy://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com https-proxy://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n```\n> `1.1.1.1` is equivalent to `host://1.1.1.1`\n\n\n## Notes  \nThe `https-proxy` protocol only applies to the substituted URL (i.e., the `Final URL` shown in the Overview). If the `Final URL` is empty, it will take effect on the original request URL.  \n\nFor example, with the rule:  \n```  \nwww.example.com/api www.example.com https-proxy://127.0.0.1:1234  \n```  \nWhen a request is made to `https://www.example.com/api/path`, Whistle processes it and changes the URL to `https://www.example.com/path` (this becomes the `Final URL`). Although the intention is to https-proxy this request to `127.0.0.1:1234`, the `https-proxy` rule only matches the original domain `www.example.com/api` before substitution. Since the converted `Final URL` is now `www.example.com/path`, this rule will not be triggered.  \n\nTo ensure the rule also applies to the substituted request, you can break it into two separate rules:  \n```  \nwww.example.com/api www.example.com  \nwww.example.com https-proxy://127.0.0.1:1234  \n```  \n\nThis way, the original request is first rewritten by the first rule, generating a new `Final URL`. Then, the second `https-proxy` rule matches this new URL and successfully proxies the request to `127.0.0.1:1234`.\n"
  },
  {
    "path": "docs/en/docs/rules/https.md",
    "content": "# https\nConvert the following three requests to HTTPS requests (the server will receive the converted HTTPS URL):\n1. **Tunnel proxy:** `tunnel://domain:port`\n    > Example: `tunnel://www.test.com:443`\n2. **WebSocket:** `ws[s]://domain[:port]/[path/to[?query]]`\n    > Example: `wss://www.test.com/path?a=1&b=2`\n3. **Normal HTTP/HTTPS:** `http[s]://domain[:port]/[path/to[?query]]`\n    > Example: `https://www.test.com/path?a=1&b=2`\n\n## Rule Syntax\n``` txt\npattern https://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]/[path][?query]`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## HTTP/HTTPS Conversion\n``` txt\nhttp://www.example.com/path1 https://www.test.com/path/xxx\nhttps://www.example.com/path2 https://www.abc.com/path3/yyy\n```\n1. Automatic Path Concatenation:\n    | Original Request | Conversion Result (URL Received by the Server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `http://www.example.com/path1` | `https://www.test.com/path/xxx` |\n    | `http://www.example.com/path1/a/b/c?query` | `https://www.test.com/path/xxx/a/b/c?query` |\n    | `https://www.example.com/path2` | `https://www.abc.com/path3/yyy` |\n    | `https://www.example.com/path2/a/b/c?query` | `https://www.abc.com/path3/yyy/a/b/c?query` |\n2. Disable path concatenation: Use `< >` or `()` to wrap the path.\n    ``` txt\n    www.example.com/path1 https://<www.test.com/path/xxx>\n    # www.example.com/path1 https://(www.test.com/path/xxx)\n    ```\n    | Original request | Conversion result (URL received by the server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `https://www.test.com/path/xxx` |\n\n## WebSocket Conversion\n``` txt\nws://www.example.com/path1 https://www.test.com/path/xxx\nwss://www.example.com/path2 https://www.abc.com/path3/yyy\n```\nThe WebSocket request is replaced with the specified ws request:\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1` | `wss://www.test.com/path/xxx` |\n| `wss://www.example.com/path2/a/b/c?query` | `wss://www.abc.com/path3/yyy/a/b/c?query`|\n\nAlso supports **automatic path concatenation** and **disabling path concatenation**.\n\n## TUNNEL Conversion\n``` txt\ntunnel://www.example.com:443 https://www.test.com:123\ntunnel://www.example2.com:443 https://www.test2.com/path\n```\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443` | `tunnel://www.test.com:123` |\n| `tunnel://www.example2.com:443` | `tunnel://www.test2.com:443`|\n\n⚠️ Automatically ignores the path of the matching URL. The default port for HTTPS is `443`.\n"
  },
  {
    "path": "docs/en/docs/rules/ignore.md",
    "content": "# ignore\nThis command is used to ignore matching rules for a specific protocol, or to ignore the currently configured matching rules.\n\n## Rule Syntax\n``` txt\npattern ignore://p1|p2|... [filters...]\n# Equivalent to:\npattern ignore://p1 ignore://p2 ... [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Protocol name<br/> `*` indicates all protocols<br/> `-p` excludes `p`<br/> ⚠️ Loading data from files/remote URLs is not supported | [Protocol List](./protocols) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Ignore all rules, retaining only those for the `file` protocol.\nwww.example.com/path ignore://*|-file\n\n# Ignore specific protocols: file, host\nwww.example.com/path2 ignore://file|host\n```\n\n## FAQ\n`ignore` ignores matched rules for a specific protocol. Rules ignored in this way will not match other rules of the same type. To have a request ignore a rule and then continue to match other rules of the same type, use the [skip](./skip) command.\n"
  },
  {
    "path": "docs/en/docs/rules/includeFilter.md",
    "content": "# includeFilter\nBased on matching [pattern](./pattern), further filter requests that meet the specified conditions (requests that meet any of the conditions will be retained).\n\n## Syntax Rules\n``` txt\npattern operation includeFilter://p1 includeFilter://p2 ...\n```\n> Requests must match `pattern` and one of `p1`, `p2`, ... for this to work.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| operation | Operation Instructions | [Operation Instructions Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Only valid for GET or POST requests\nwww.example.com/api/data proxy://127.0.0.1:8080 includeFilter://m:GET includeFilter://m:POST\n```\n\nCan be used with [excludeFilter](./excludeFilter). For detailed usage, see [Filter documentation](./filters).\n"
  },
  {
    "path": "docs/en/docs/rules/inherit.md",
    "content": "# Protocol Inheritance\n[http](./http)/[https](./https)/[ws](./ws)/[wss](./wss)/[tunnel](./tunnel) all restrict the target URL's protocol. Whistle also supports automatic completion based on the request URL's protocol.\n## Rule Syntax\n``` txt\npattern //value [filters...]\n# or\npattern value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]/[path][?query]`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## HTTP/HTTPS Conversion\n``` txt\nhttp://www.example.com/path1 www.test.com/path/xxx\nhttps://www.example.com/path2 www.abc.com/path3/yyy\n```\n1. Automatic path concatenation:\n    | Original request | Conversion result (URL received by the server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `http://www.example.com/path1` | `http://www.test.com/path/xxx` |\n    | `http://www.example.com/path1/a/b/c?query` | `http://www.test.com/path/xxx/a/b/c?query` |\n    | `https://www.example.com/path2` | `https://www.abc.com/path3/yyy` |\n    | `https://www.example.com/path2/a/b/c?query` | `https://www.abc.com/path3/yyy/a/b/c?query` |\n2. Disable path concatenation: Use `< >` or `()` to wrap the path.\n    ``` txt\n    www.example.com/path1 //<www.test.com/path/xxx>\n    # www.example.com/path1 //(www.test.com/path/xxx)\n    ```\n    | Original Request | Conversion Result (URL Received by the Server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[http/https/wss/ws]://www.example.com/path/x/y/z` | `[http/https/wss/ws]://www.test.com/path/xxx` |\n\n## WebSocket Conversion\n``` txt\nws://www.example.com/path1 www.test.com/path/xxx\nwss://www.example.com/path2 www.abc.com/path3/yyy\n```\nThe WebSocket request is replaced with the specified ws request:\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `ws://www.example.com/path1` | `ws://www.test.com/path/xxx` |\n| `wss://www.example.com/path2/a/b/c?query` | `wss://www.abc.com/path3/yyy/a/b/c?query`|\n\nAlso supports **automatic path concatenation** and **disabling path concatenation**.\n\n## TUNNEL Conversion\n``` txt\ntunnel://www.example.com:443 //www.test.com:123\ntunnel://www.example2.com:443 www.test2.com/path\n```\n| Original Request | Conversion Result (URL Received by the Server) |\n| ----------------------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443` | `tunnel://www.test.com:123` |\n| `tunnel://www.example2.com:443` | `tunnel://www.test2.com:443`|\n"
  },
  {
    "path": "docs/en/docs/rules/jsAppend.md",
    "content": "# jsAppend\nInserts the specified content after the existing response body. (This only works if the `content-type` response type contains `javascript` and the status code includes a response body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a response body, such as 204 and 304, are not affected.\n\n## Rule Syntax\n``` txt\npattern jsAppend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path1 jsAppend://(Hello) file://(-test-)\nwww.example.com/path2 jsAppend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsAppend://(Hello) file://(-test-) resType://css\n```\n- Requesting `https://www.example.com/path1/to` results in a response of `-test-<script>Hello</script>`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-Hello`\n- Requesting `https://www.example.com/path3/to` results in a response of `-test-`\n\n#### Embedded/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsAppend://{body.txt} file://(-test-)\nwww.example.com/path2 jsAppend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsAppend://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path1/to` results in a response of `-test-<script>Hello world.</script>`\n- Requesting `https://www.example.com/path2/to` results in a response of `-test-Hello world.`\n- Requesting `https://www.example.com/path3/to` results in a response of `-test-`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 jsAppend:///User/xxx/test.js\nwww.example.com/path2 jsAppend://https://www.xxx.com/xxx/params.js\n# Editing a temporary file\nwww.example.com/path3 jsAppend://temp/blank.js\n````\n\n## Set `<script>` Tag Attributes for Injected Scripts\n\nScripts injected into HTML pages via `jsAppend` are automatically wrapped in `<script>` tags by Whistle. To set additional attributes for these tags—such as `nomodule`, `module`, `defer`, `async`, or `crossorigin`—you can use the `lineProps` parameter for configuration.\n\n```txt\nwww.example.com/path1 jsAppend://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsAppend://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsAppend://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsAppend://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsAppend://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### Attribute Descriptions and Examples\n| Attribute | Purpose | Configuration Example |\n|-----------|---------|-----------------------|\n| `nomodule` | Executes in traditional browsers; this script will run in browsers that do not support ES modules. | `lineProps://nomodule` |\n| `module` | Declares the script as an ES module, enabling modular imports. | `lineProps://module` |\n| `defer` | Loads asynchronously and executes after the document has been parsed. | `lineProps://defer` |\n| `async` | Loads asynchronously and executes immediately after download. | `lineProps://async` |\n| `crossorigin`| Enables Cross-Origin Resource Sharing (CORS) mode. | `lineProps://crossorigin` |\n\n## Associated Protocols\n1. Inject content before the response: [reqAppend](./reqAppend)\n2. Inject content before the JavaScript response: [jsAppend](./jsAppend)\n3. Replace the JavaScript response: [jsBody](./jsBody)\n"
  },
  {
    "path": "docs/en/docs/rules/jsBody.md",
    "content": "# jsBody\nReplaces the existing response body with the specified content. (This only works for responses with `content-type` containing `javascript` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern jsBody://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path1 jsBody://(Hello) file://(-test-)\nwww.example.com/path2 jsBody://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsBody://(Hello) file://(-test-) resType://css\n```\n- Requesting `https://www.example.com/path1/to` results in a response of `<script>Hello</script>`\n- Requesting `https://www.example.com/path2/to` results in a response of `Hello`\n- Requesting `https://www.example.com/path3/to` results in a response of `-test-`\n\n#### Embedded/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsBody://{body.txt} file://(-test-)\nwww.example.com/path2 jsBody://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsBody://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path1/to` results in a response of `<script>Hello world.</script>`\n- Requesting `https://www.example.com/path2/to` results in a response of `Hello world.`\n- Requesting `https://www.example.com/path3/to` results in a response of `-test-`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 jsBody:///User/xxx/test.js\nwww.example.com/path2 jsBody://https://www.xxx.com/xxx/params.js\n# Editing a temporary file\nwww.example.com/path3 jsBody://temp/blank.js\n````\n## Set `<script>` Tag Attributes for Injected Scripts\n\nScripts injected into HTML pages via `jsBody` are automatically wrapped in `<script>` tags by Whistle. To set additional attributes for these tags—such as `nomodule`, `module`, `defer`, `async`, or `crossorigin`—you can use the `lineProps` parameter for configuration.\n\n```txt\nwww.example.com/path1 jsBody://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsBody://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsBody://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsBody://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsBody://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### Attribute Descriptions and Examples\n| Attribute | Purpose | Configuration Example |\n|-----------|---------|-----------------------|\n| `nomodule` | Executes in traditional browsers; this script will run in browsers that do not support ES modules. | `lineProps://nomodule` |\n| `module` | Declares the script as an ES module, enabling modular imports. | `lineProps://module` |\n| `defer` | Loads asynchronously and executes after the document has been parsed. | `lineProps://defer` |\n| `async` | Loads asynchronously and executes immediately after download. | `lineProps://async` |\n| `crossorigin`| Enables Cross-Origin Resource Sharing (CORS) mode. | `lineProps://crossorigin` |\n\n## Associated Protocols\n1. Replace the response content: [resBody](./resBody)\n2. Inject content before the JavaScript response content: [jsBody](./jsBody)\n3. Inject content after the JavaScript response content: [jsBody](./jsBody)\n"
  },
  {
    "path": "docs/en/docs/rules/jsPrepend.md",
    "content": "# jsPrepend\nInserts the specified content before the existing response body. (This only works for responses with `content-type` containing `javascript` and a status code containing a body (e.g., `200`/`500`).)\n> ⚠️ Note: Requests without a body, such as 204 and 304 responses, are not affected.\n\n## Rule Syntax\n``` txt\npattern jsPrepend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path1 jsPrepend://(Hello) file://(-test-)\nwww.example.com/path2 jsPrepend://(Hello) file://(-test-) resType://js\nwww.example.com/path3 jsPrepend://(Hello) file://(-test-) resType://css\n```\n- Request `https://www.example.com/path1/to`. The response content becomes\n    ``` html\n    <!DOCTYPE html>\n    <script>Hello</script>-test-\n    ```\n- Request `https://www.example.com/path2/to`. The response content becomes `Hello-test-`\n- Request The response content of `https://www.example.com/path3/to` becomes `-test-`\n\n#### Embedded/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path1 jsPrepend://{body.txt} file://(-test-)\nwww.example.com/path2 jsPrepend://{body.txt} file://(-test-) resType://js\nwww.example.com/path3 jsPrepend://{body.txt} file://(-test-) resType://css\n````\n- Requesting `https://www.example.com/path1/to`, the response content becomes\n    ``` html\n    <!DOCTYPE html>\n    <script>Hello world.</script> -test-\n    ```\n- Requesting `https://www.example.com/path2/to`, the response content becomes `Hello world.-test-`\n- Request `https://www.example.com/path3/to`. The response content becomes `-test-`.\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 jsPrepend:///User/xxx/test.js\nwww.example.com/path2 jsPrepend://https://www.xxx.com/xxx/params.js\n# Editing a temporary file\nwww.example.com/path3 jsPrepend://temp/blank.js\n````\n\n## Set `<script>` Tag Attributes for Injected Scripts\n\nScripts injected into HTML pages via `jsPrepend` are automatically wrapped in `<script>` tags by Whistle. To set additional attributes for these tags—such as `nomodule`, `module`, `defer`, `async`, or `crossorigin`—you can use the `lineProps` parameter for configuration.\n\n```txt\nwww.example.com/path1 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://nomodule\nwww.example.com/path2 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://module\nwww.example.com/path3 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://defer\nwww.example.com/path4 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://async\nwww.example.com/path5 jsPrepend://https://www.xxx.com/xxx/params.js lineProps://crossorigin\n```\n\n### Attribute Descriptions and Examples\n| Attribute | Purpose | Configuration Example |\n|-----------|---------|-----------------------|\n| `nomodule` | Executes in traditional browsers; this script will run in browsers that do not support ES modules. | `lineProps://nomodule` |\n| `module` | Declares the script as an ES module, enabling modular imports. | `lineProps://module` |\n| `defer` | Loads asynchronously and executes after the document has been parsed. | `lineProps://defer` |\n| `async` | Loads asynchronously and executes immediately after download. | `lineProps://async` |\n| `crossorigin`| Enables Cross-Origin Resource Sharing (CORS) mode. | `lineProps://crossorigin` |\n\n## Associated Protocols\n1. Inject content before the response content: [reqPrepend](./reqPrepend)\n2. Replace JavaScript response content: [jsBody](./jsBody)\n3. Inject content after JavaScript response content: [jsPrepend](./jsPrepend)\n"
  },
  {
    "path": "docs/en/docs/rules/lineProps.md",
    "content": "# lineProps\nEnable features such as proxyHost, proxyTunnel, and safeHtml through rules.\n> 📌 Differences from [enable](./enable):\n>\n> `enable` is a global configuration.\n>\n> `lineProps` only applies to the rule on the line where the configuration is located.\n\n## Rule Syntax\n``` txt\npattern operation lineProps://action1|action2|... [filters...]\n\n# Equivalent to:\npattern operation lineProps://action1 lineProps://action2 ... [filters...]\n```\n> `lineProps` cannot be used as an `operation` alone and only applies to the `operation` on the same line.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| operation | Operation Instructions | [Operation Instruction Documentation](./operation) |\n| action | Specific actions, see the description below | |\n| filters | Optional filters, supporting matching: • Request URL/Method/Header/Content • Response Status Code/Header | [Filter Documentation](./filters) |\n\n- `important`: Like CSS !important, boosts rule priority; only applies to operations of the same protocol\n- `safeHtml`: This is a security protection mechanism. When injecting content into an HTML page using `htmlXxx`/`jsXxx`/`cssXxx`, the first non-whitespace character in the response is checked to see if it is a `{` or a `[` (the opening character of a JSON object). Injection is performed only if it is not. This effectively prevents accidental injection of non-standard HTML responses (such as JSON endpoints).\n- `strictHtml`: This is a security protection mechanism. When injecting content into an HTML page using `htmlXxx`/`jsXxx`/`cssXxx`, the first non-whitespace character in the response is checked to see if it is a `<`. Injection is performed only if it is not. This effectively prevents accidental injection of non-standard HTML responses (such as JSON endpoints).\n- `disableAutoCors`: Disables automatic addition of necessary CORS (Cross-Origin Resource Sharing) headers for [file](./file) protocol substitution requests.\n- `disableUserLogin`: Disables displaying the login dialog when setting [statusCode://401](./statusCode).\n- `enableUserLogin`: Enables displaying the login dialog when setting [statusCode://401](./statusCode). (Shown by default, disables `disable.userLogin`).\n- `internal`: Applies `proxy`, `socks`, and `host` protocol drop rules to Whistle internal requests.\n- `internalOnly`: Applies `proxy`, `socks`, and `host` protocol drop rules to Whistle internal requests only.\n- `internalProxy`: Uses proxy protocols like `proxy` and `socks` to forward requests to another proxy server (such as another Whistle proxy server). When this feature is enabled, HTTPS requests decrypted by the first-tier proxy will be transmitted in plaintext throughout the proxy chain, allowing upstream proxies to directly access the plaintext data.\n- `proxyFirst`: give priority to the [proxy](./proxy) rule (by default, both `host` and `proxy` are matched, and only `host` takes effect)\n- `proxyHost`: Both the [proxy](./proxy) and [host](./host) rules take effect.\n- `proxyHostOnly`: Functions similarly to `proxyHost`, but if no [host](./host) matches, the [proxy](./proxy) rule will be automatically disabled.\n- `proxyTunnel`: Used with `proxyHost`, it allows the upstream proxy to tunnel to the upstream HTTP proxy. See the example below for details.\n- `weakRule`: By default, the `weakRule` rule will be disabled when protocols such as [file](./file) are configured. By setting the `weakRule` property, you can increase the priority of the `proxy` (./proxy) rule, ensuring it still works in the above scenario.\n\n## Configuration Example\n#### Without `lineProps://important`\n``` txt\nwww.example.com/path file:///User/xxx/important1.html\nwww.example.com/path file:///User/xxx/important2.html\n```\nAccessing `https://www.example.com/path` will match `file:///User/xxx/important1.html`\n\n#### Using `lineProps://important`\n``` txt\nwww.example.com/path file:///User/xxx/important1.html\nwww.example.com/path file:///User/xxx/important2.html lineProps://important\n```\nAccessing `https://www.example.com/path` will match `file:///User/xxx/important2.html`\n\n#### Inject text\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1))\nwww.example.com/path jsPrepend://(alert(1))\nwww.example.com/path cssPrepend://(alert(1))\n```\nVisit `https://www.example.com/path` and return the response content:\n``` html\n<!DOCTYPE html>\n<style>alert(1)</style>\nalert(1)\n<script>alert(1)</script>test\n```\n\n#### Use `enable://strictHtml`\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1))\nwww.example.com/path jsPrepend://(alert(1)) enable://strictHtml\nwww.example.com/path cssPrepend://(alert(1))\n```\nVisit `https://www.example.com/path` and return the response content:\n``` html\ntest\n```\n> `enable://strictHtml` is effective for all rules\n\n### Use `lineProps://strictHtml`\n``` txt\nwww.example.com/path file://(test) resType://html\nwww.example.com/path htmlPrepend://(alert(1))\nwww.example.com/path jsPrepend://(alert(1)) lineProps://strictHtml\nwww.example.com/path cssPrepend://(alert(1))\n```\nVisit `https://www.example.com/path` Return response content:\n``` html\n<!DOCTYPE html>\n<style>alert(1)</style>\nalert(1)test\n```\n> `lineProps://strictHtml` only applies to the line where the rule is located\n"
  },
  {
    "path": "docs/en/docs/rules/locationHref.md",
    "content": "# locationHref\nFor scenarios where server-side redirection (`302`/`301`) is unavailable, client-side redirection can be achieved by returning the JavaScript code `window.location.href = targetUrl` in the HTML page. Especially suitable for:\n- App pages loaded from local HTML files\n- Single-Page Applications (SPAs)\n- Hybrid Apps developed with special frameworks\n\n## Rule Syntax\n``` txt\npattern locationHref://targetUrl [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| targetUrl | The redirected URL, which can be a relative path | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Basic Configuration\n``` txt\nwww.example.com/path locationHref://https://www.qq.com\n\nwww.example.com/path2 locationHref://../abc/123\n```\n- Visiting `https://www.example.com/path/to` redirects to `https://www.qq.com` (no automatic path concatenation)\n- Visiting `https://www.example.com/path2/to` redirects to `https://www.example.com/abc/123`\n\n#### Implementing Path Concatenation\n``` txt\n# Wildcard\n^www.example.com/path/*** locationHref://`https://www.example.com/$1`\n```\n- Visiting `https://www.example.com/path/to?query` redirects to `https://www.example.com/to?query`\n\n##### location.replace\nTo implement a page redirect without history history using `location.replace(targetUrl)`, use the following configuration format:\n``` txt\nwww.example.com/path locationHref://replace:https://www.qq.com\n```\n> Redirects to the target URL. The current page will not be saved in the browser history, and users cannot return to the original page using the \"Back\" button.\n\n## Related Protocols\n1. `302` Redirect: [redirect](./redirect)\n"
  },
  {
    "path": "docs/en/docs/rules/log.md",
    "content": "# Log Rule\n\nAutomatically injects JavaScript code into pages to capture JavaScript exceptions and `console.xxx` logs, displaying them in real-time on the Whistle management interface.\n\n## Overview\n- **Exception Monitoring**: Automatically captures JavaScript execution errors and unhandled Promise rejections in pages.\n- **Log Collection**: Intercepts and collects all console outputs such as `console.log()`, `console.warn()`, and `console.error()`.\n- **Visual Log Viewing**: View, filter, and search logs by group in real-time within the Whistle management interface.\n- **Log Preprocessing**: Supports filtering or formatting log content through custom scripts.\n\n## Rule Syntax\n```\npattern log://id [filters...]\n```\n\n### Parameter Description\n| Parameter | Required | Description                                                                 | Details                                                                 |\n|-----------|----------|-----------------------------------------------------------------------------|-------------------------------------------------------------------------|\n| pattern   | Yes      | Expression matching request URLs to specify pages to monitor                 | [Pattern Matching Syntax](./pattern)                                    |\n| id        | Yes      | Log group identifier (plain string) to distinguish and filter logs from different sources in the Whistle interface | Logs within the same group are displayed together, supporting quick view switching by ID |\n| filters   | No       | Filter conditions to further match specific characteristics of requests or responses | [Filter Syntax](./filters) <br> Supports filtering by URL, method, request headers, response status codes, etc. |\n\n## Usage\n\n### Basic Usage\nCapture logs from all pages under a specified domain:\n```\nwww.example.com log://myapp\n```\n\n### Multi-Domain Group Monitoring\nSet different log groups for different domains or paths for categorized viewing:\n```\nke.qq.com log://ke\nnews.qq.com log://news\napi.example.com log://api\n```\n\n## Advanced Configuration: Log Preprocessing\n\nInject custom scripts via the `jsPrepend` rule to preprocess logs before they are sent to Whistle, such as sensitive information masking, format conversion, or conditional filtering.\n\n### Configuration Example\n```\nwww.example.com log:// jsPrepend://{handleLog.js}\n```\n\n### Preprocessing Script Example\n```javascript\n// handleLog.js\n// Custom processing function executed before logs are sent to Whistle\nwindow.onBeforeWhistleLogSend = function(result, level) {\n  // result: Array of raw messages to be output\n  // level: Log level ('log', 'warn', 'error', 'info', 'debug')\n  \n  result.forEach(function(msg, i) {\n    // Example 1: Hide sensitive keywords\n    if (typeof msg === 'string' && msg.includes('password')) {\n      result[i] = '[SENSITIVE DATA HIDDEN]';\n    }\n    \n    // Example 2: Convert specific content to structured objects\n    if (msg === 'abc') {\n      result[i] = {\n        name: 'avenwu',\n        level: level,\n        timestamp: new Date().toISOString()\n      };\n    }\n    \n    // Example 3: Filter out certain types of logs\n    if (msg === 'ignore-this-message') {\n      // Returning an empty array will prevent this log from being sent\n      result.splice(i, 1);\n    }\n  });\n};\n```\n\n## Interface Operation Guide\n\n### Viewing Logs\n1. Open the Whistle management interface (default: `http://127.0.0.1:8899`)\n2. Switch to the **Network / Tools / Console** tab\n3. Select the corresponding log group ID in the top-left corner\n4. View page output logs and error information in real-time\n\n### Log Filtering\n- Use the top filter bar to quickly filter by log level (Log/Warn/Error)\n- Supports keyword search to quickly locate specific logs\n"
  },
  {
    "path": "docs/en/docs/rules/method.md",
    "content": "# method\nModify the request method.\n\n## Rule Syntax\n``` txt\npattern method://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Request method name such as `get`/`post`/`head` (case-insensitive) | |\n| filters | Optional filters, supporting matching: <br/>• Request URL/Method/Header/Content <br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Visit `https://www.example.com/path/to` and see the request method as `POST` on the Whistle packet capture interface.\nwww.example.com/path method://post\n\n# Modify a method with a filter\nwww.example.com/path2 method://patch includeFilter://reqH.content-type=multipart/form-data\n```\n"
  },
  {
    "path": "docs/en/docs/rules/operation.md",
    "content": "# operation\n\nIn Whistle, each rule consists of two parts: **Pattern** (`pattern`) and **Operation** (`operation`). The general syntax for `operation` is:\n\n```txt\nprotocol://[value]\n```\n- **protocol**: Specifies the operation type (such as `file`, `proxy`, `resReplace`, etc.)\n- **value**: Operation content (supports multiple formats, see below)\n\n## Inline Values\n```txt\npattern reqHeaders://x-proxy=Whistle   # Set request headers\npattern statusCode://404               # Modify status code\npattern file://({\"ec\":0})              # Respond with inline content (value inside parentheses: `{\"ec\":0}`)\n```\n\nWhen the operation content (Value) contains spaces, line breaks, or special characters, inline method cannot be used directly. Use the following alternative methods instead:\n\n## Embedded Values\n\n````txt\n``` ua.txt\nMozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\n```\npattern ua://{ua.txt}\n````\n\nEquivalent to:\n\n````txt\n``` headers.json\nuser-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\n```\npattern reqHeaders://{headers.json}\n````\n\n## Values References\n\nWhen operation values (Value) need to be shared by multiple rules, embedding them directly in rules makes them non-reusable. In such cases, these values can be stored in the Values module of the Whistle interface and referenced by key name:\n\n1. Create a key named `result.json` in Values and fill in the operation content\n2. In rules, reference it via `{result.json}`, e.g.: `www.test.com/cgi-bin/test file://{result.json}`\n\n<img src=\"/img//values-demo1.png\" width=\"420\" />\n\n## File/Remote Resources\n```txt\npattern reqHeaders:///User/xxx/filepath             # Load operation content from local file\npattern resHeadrs://https://example.com/config.json # Load JSON object from remote URL\npattern resHeaders://temp/blank.json                # Through editing temporary files\n```\n> ⚠️ Note: Protocols like http/https/ws/wss/tunnel/host/enable/cache... prohibit obtaining content via file paths or remote URLs. See each protocol's documentation for details.\n\n## Temporary Files\nWhen frequent content editing is needed, you can use Whistle's temporary file functionality.\n\n```txt\npattern protocol://temp.json\n```\n\n**Operation Steps**:\n1. In the Rules editor, hold `Command` (Mac) / `Ctrl` (Windows)\n2. Click with mouse on `protocol://temp.json`\n3. Enter the response content in the pop-up editing dialog\n4. Click `Save` to save\n\n## Parentheses Usage\nIn Whistle rules, the value part of `protocol://value` can have three types of indirect references:\n1. `{key}` - Reference embedded values\n2. `remote-url` - Remote resource address\n3. `localfilepath` - Local file path\n\nWhen you need to directly reference the above content itself (rather than what they indirectly reference) as operation content, you can wrap it with parentheses:\n```txt\nprotocol://(value)\n```\n\nExamples:\n1. `reqHeaders:///User/xxx/yyy.txt` - Load operation content from local file `/User/xxx/yyy.txt`\n2. `reqHeaders://(/User/xxx/yyy.txt)` - Treat `/User/xxx/yyy.txt` directly as operation content\n\n## Template Strings\nWhistle provides template string functionality similar to ES6, allowing you to dynamically reference request information and apply it to rule configurations. Supports the following template string types:\n\n##### General Inline Values\n```txt\npattern protocol://`...${version}...`\n```\n\n##### Embedded Values or Values References\n````txt\n``` test.key\n...${reqId}...\n...${version}...\n```\npattern protocol://`{test.key}`\n````\n\n##### Parenthesized Content\n````txt\npattern protocol://`(...${now}...)`\n````\n\n##### String Variables\n\n| Variable Name | Value |\n| --------------------- | ------------------------------------------------------------ |\n| `${now}` | Date.now() |\n| `${random}` | Math.random() |\n| `${randomUUID}` | crypto.randomUUID() |\n| `${randomInt(n)}` or `${randomInt(n1-n2)}` | Get a random positive integer from [0, n] or [n1, n2] (Added in: v2.9.104) |\n| `${reqId}` | ID assigned by Whistle to each request |\n| `${url.protocol}` | url.parse(fullUrl).protocol |\n| `${url.hostname}` | url.parse(fullUrl).hostname |\n| `${url.host}` | url.parse(fullUrl).host |\n| `${url.port}` | url.parse(fullUrl).port |\n| `${url.path}` | url.parse(fullUrl).path |\n| `${url.pathname}` | url.parse(fullUrl).pathname |\n| `${url.search}` | url.parse(fullUrl).search |\n| `${query.xxx}` | Value of request parameter `xxx` |\n| `${url}` | Complete request URL |\n| `${querystring}` | url.parse(fullUrl).search \\|\\| '?' (not empty) |\n| `${searchstring}` | url.parse(fullUrl).search \\|\\| '?' (not empty) |\n| `${method}` | Request method |\n| `${reqHeaders.xxx}` | Value of request header field `xxx` |\n| `${resHeaders.xxx}` | Value of response header field `xxx` |\n| `${version}` | Whistle version number |\n| `${port}` | Whistle port number |\n| `${host}` | Network interface IP that Whistle listens on (empty by default) |\n| `${realPort}` | port displayed in Whistle interface's Online dialog (usually Whistle port number) |\n| `${realHost}` | host displayed in Whistle interface's Online dialog (usually the network interface IP that Whistle listens on) |\n| `${clientIp}` | Client IP |\n| `${clientPort}` | Client port |\n| `${serverIp}` | Server IP |\n| `${serverPort}` | Server port |\n| `${reqCookies.xxx}` | Value of request cookie `xxx` |\n| `${resCookies.xxx}` | Value of response cookie `xxx` |\n| `${statusCode}` | Response status code |\n| `${env.xxx}` | process.env.xxx |\n| `${whistle.plugin-name}` | `value` of `whistle.plugin-name://value` or `plugin-name://value` |\n\n> `${whistle.plugin-name}` may only have values in internal rules of plugins\n\n##### Example\n\n````txt\n``` test.txt\nnow: ${now}\nrandom: ${random}\nrandomUUID: ${randomUUID}\nreqId: ${reqId}\nurl.protocol: ${url.protocol}\nurl.hostname: ${url.hostname}\nurl.host: ${url.host}\nurl.port: ${url.port}\nurl.path: ${url.path}\nurl.pathname: ${url.pathname}\nurl.search; ${url.search}\nquery: ${query.name}\nurl: ${url}\nquerystring: ${querystring}\nsearchstring: ${searchstring}\nmethod: ${method}\nreqHeaders.accept: ${reqHeaders.accept}\nresHeaders.content-type: ${resHeaders.content-type}\nversion: ${version}\nport: ${port}\nhost: ${host}\nrealPort: ${realPort}\nrealHost: ${realHost}\nclientIp: ${clientIp}\nclientPort: ${clientPort}\nserverIp: ${serverIp}\nserverPort: ${serverPort}\nreqCookies.test: ${reqCookies.test}\nresCookies.test: ${resCookies.test}\nstatusCode: ${statusCode}\nenv.USER: ${env.USER}\n```\n\nwww.test.com/index.html file://`{test.txt}`\n````\n\nAccessing `https://www.test.com/index.html?name=avenwu` returns response content:\n\n```txt\nnow: 1752301623295\nrandom: 0.6819241513880432\nrandomUUID: e917b9fc-e2ef-4255-9209-11eb417235c5\nreqId: 1752301623294-339\nurl.protocol: https:\nurl.hostname: www.test.com\nurl.host: www.test.com\nurl.port: \nurl.path: /index.html?name=avenwu\nurl.pathname: /index.html\nurl.search; ?name=avenwu\nquery: avenwu\nurl: https://www.test.com/index.html?name=avenwu\nquerystring: ?name=avenwu\nsearchstring: ?name=avenwu\nmethod: GET\nreqHeaders.accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\nresHeaders.content-type: \nversion: 2.9.100\nport: 8899\nhost: \nrealPort: 8899\nrealHost: \nclientIp: 127.0.0.1\nclientPort: 60582\nserverIp: \nserverPort: \nreqCookies.test: \nresCookies.test: \nstatusCode: \nenv.USER: av\n```\n\n## Data Objects\nOperation content can be not only text or binary content but also JSON objects. Whistle supports the following 3 data object formats:\n\n#### JSON Format\n```js\n{\n  \"key1\": value1,\n  \"key2\": value2,\n  \"keyN\": valueN\n}\n```\n\n#### Line Format\n```txt\nkey1: value1\nkey2:value2\nkeyN: valueN\n```\n> Separated by `colon + space`. If there's no `colon + space`, then separated by the first colon. If there's no colon, then `value` is an empty string\n\n**Multi-level nesting:**\n```txt\na.b.c: 123\nc\\.d\\.e: abc\n```\nEquivalent to:\n```json\n{\n  \"a\": {\n    \"b\": {\n      \"c\": 123\n    }\n  },\n  \"c.d.e\": \"abc\"\n}\n```\n\n#### Inline Format (Request Parameter Format)\n\n```txt\nkey1=value1&key2=value2&keyN=valueN\n```\n> Both `key` and `value` should ideally be `encodeURIComponent`\n\n## Quick Reference Manual for Common Operation Commands {#operation-manual}\n- [Modifying Request Content](#request)\n  - [Modifying Request Method](#method)\n  - [Modifying Request URL](#url)\n  - [Modifying HTTP Version](#http-version)\n  - [Modifying Request Headers](#req-headers)\n  - [Modifying Request Body](#req-body)\n- [Modifying Response Content](#response)\n  - [Modifying Status Code](#status-code)\n  - [Modifying Response Headers](#res-headers)\n  - [Modifying Response Body](#res-body)\n  - [Modifying Trailers](#trailers)\n- [Modifying Connection Process](#connect)\n  - [Modifying DNS](#dns)\n  - [Setting Proxy](#proxy)\n  - [Proxy and DNS Taking Effect Simultaneously](#proxy-host)\n- [Page Debugging Tools](#tools)\n  - [Viewing Page DOM Structure](#weinre)\n  - [Viewing Page Logs and Error Information](#log)\n\n---\n\n## 1. Modifying Request Content {#request}\n\n### 1.1 Modifying Request Method {#method}\n```txt\n# Basic Syntax\npattern method://NewMethod\n\n# Example\nwww.example.com/path method://post\n```\n\n**Supported Data Sources:**\n- Direct specification: `method://get`\n- Embedded value: `method://{keyOfEmbedded}`\n- Values configuration: `method://{keyOfValues}`\n\n**Notes:**\n- Method names are case-insensitive.\n- Not supported from local files or remote URLs.\n\n**Practical Examples:**\n```txt\n# Example 1: Change all request methods to POST\nwww.example.com/path method://post\n\n# Example 2: Change only PUT requests to POST\nwww.example.com/path method://post includeFilter://m:put\n\n# Example 3: Modify method based on request body content\nwww.example.com/api method://put includeFilter://b:cmdname=test\n```\n\n### 1.2 Modifying Request URL {#url}\n#### URL Mapping\n```txt\nwww.example.com/path/to www.test.com/test\n```\n**Mapping Effect:**\n- `https://www.example.com/path/to?query=abc` → `https://www.test.com/test?query=abc`\n- `https://www.example.com/path/to/subpage` → `https://www.test.com/test/subpage`\n- `wss://www.example.com/path/to/api` → `wss://www.test.com/test/api`\n\n#### Modifying Request Parameters\n```txt\n# Add/Replace parameters\npattern urlParams://({\"key\":\"value\"})\n\n# Delete parameter\npattern delete://urlParams.paramName\n\n# Example: Modify parameters and delete a specific parameter\nwww.example.com/api urlParams://({\"cmdname\":\"Test\"}) delete://urlParams.oldParam\n```\n\n#### Modifying Path\n```txt\n# Regular expression replacement\npattern pathReplace://({\"/old/ig\":\"new\"})\n\n# Keyword replacement\npattern pathReplace://({\"old\":\"new\"})\n```\n\n**Supported Data Sources:**\n- Inline JSON\n- Embedded value\n- Values configuration\n- Local file\n- Remote URL\n\n### 1.3 Modifying HTTP Version {#http-version}\n```txt\n# Force using standard HTTPS (disable HTTP/2)\npattern disable://h2\n```\n> By default, attempts to establish a connection using HTTP/2; automatically downgrades to HTTPS if not supported.\n\n### 1.4 Modifying Request Headers {#req-headers}\n```txt\n# Add/Replace request header\npattern reqHeaders://({\"Header-Name\":\"value\"})\n\n# Delete request header\npattern delete://reqHeaders.headerName\n\n# Example\nwww.example.com reqHeaders://({\"X-Custom-Header\":\"test\"})\n```\n\n### 1.5 Modifying Request Body {#req-body}\n#### Merge Modification (JSON/Form)\n```txt\npattern reqMerge://({\"newField\":\"value\"})\n```\n\n#### Text Replacement\n```txt\n# Regular expression replacement\npattern reqReplace://({\"/search/ig\":\"replace\"})\n\n# Keyword replacement\npattern reqReplace://({\"search\":\"replace\"})\n```\n\n#### Complete Replacement\n```txt\npattern reqBody://(NewContent)\n```\n\n#### Deletion Operations\n```txt\n# Delete specific field\npattern delete://reqBody.fieldName\n\n# Delete entire request body\npattern delete://reqBody\n```\n\n**Supported Data Sources:**\n- Inline value\n- Embedded value\n- Values configuration\n- Local file\n- Remote URL\n\n---\n\n## 2. Modifying Response Content {#response}\n\n### 2.1 Modifying Status Code {#status-code}\n```txt\n# Replace existing response status code (request reaches server)\npattern replaceStatus://500\n\n# Respond directly with status code (request not sent to server)\npattern statusCode://500\n```\n\n### 2.2 Modifying Response Headers {#res-headers}\n```txt\n# Add/Replace response header\npattern resHeaders://({\"Header-Name\":\"value\"})\n\n# Delete response header\npattern delete://resHeaders.headerName\n```\n\n### 2.3 Modifying Response Body {#res-body}\n#### Merge Modification (JSON/JSONP)\n```txt\npattern resMerge://({\"newData\":\"value\"})\n```\n\n#### Text Replacement\n```txt\npattern resReplace://({\"/old/ig\":\"new\"})\n```\n\n#### Replace Response Body\n```txt\n# Replace content returned by server\npattern resBody://(NewContent)\n```\n\n#### Respond Directly with Content\n```txt\n# Request not sent to server\npattern file://(Content to return directly) resType://html\n```\n\n#### Deletion Operations\n```txt\n# Delete response body field\npattern delete://resBody.fieldName\n\n# Clear response body\npattern delete://resBody\n```\n\n### 2.4 Modifying Trailers {#trailers}\n> Trailers are additional header fields sent after the chunked transfer response.\n\n```txt\n# Add/Replace Trailers\npattern trailers://({\"Trailer-Name\":\"value\"})\n\n# Delete Trailers\npattern delete://trailers.trailerName\n```\n\n---\n\n## 3. Modifying Connection Process {#connect}\n\n### 3.1 Modifying DNS {#dns}\n```txt\n# Set IP address\npattern 127.0.0.1\n\n# Set IP and port\npattern 127.0.0.1:8080\n\n# CNAME effect (pointing to another host)\npattern host://www.target.com:8080\n```\n\n### 3.2 Setting Proxy {#proxy}\n```txt\n# HTTP proxy\npattern proxy://127.0.0.1:8080\n\n# SOCKS5 proxy\npattern socks://127.0.0.1:1080\n\n# Supports domain name\npattern proxy://proxy.example.com:8080\n```\n\n### 3.3 Proxy and DNS Taking Effect Simultaneously {#proxy-host}\n**Priority Description:**\n- Default: Host configuration takes precedence over proxy.\n- Adjustable: Use `lineProps://proxyHost` to make both take effect simultaneously.\n\n```txt\n# Example 1: Only host configuration takes effect\npattern 127.0.0.1:8080 socks://10.1.1.1:1080\n\n# Example 2: Only proxy configuration takes effect\npattern 127.0.0.1:8080 socks://10.1.1.1:1080 ignore://host\n\n# Example 3: Both host and proxy take effect\npattern 127.0.0.1:8080 socks://10.1.1.1:1080 lineProps://proxyHost\n```\n\n---\n\n## 4. Page Debugging Tools {#tools}\n\n### 4.1 Viewing Page DOM Structure {#weinre}\n```txt\npattern weinre://DebugSessionName\n```\n> Detailed usage reference: [weinre documentation](./weinre)\n\n### 4.2 Viewing Page Logs and Error Information {#log}\n```txt\npattern log://LogSessionName\n```\n> Detailed usage reference: [log documentation](./log)\n\n---\n\n## Data Source Quick Reference Table\n\n| Operation Type | Direct Inline | Embedded Value | Values | Local File | Remote URL |\n|---------------|---------------|----------------|--------|------------|------------|\n| Request Method | ✓ | ✓ | ✓ | ✗ | ✗ |\n| URL Parameters | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Request Headers | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Request Body | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Response Headers | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Response Body | ✓ | ✓ | ✓ | ✓ | ✓ |\n| Trailers | ✓ | ✓ | ✓ | ✓ | ✓ |\n\n**Syntax Examples:**\n```txt\n# Direct inline\nprotocol://({\"key\":\"value\"})\n\n# Embedded value\nprotocol://{embeddedKey}\n\n# Values configuration\nprotocol://{valuesKey}\n\n# Local file\nprotocol:///path/to/file.json\n\n# Remote URL\nprotocol://https://example.com/data.json\n```\n\n---\n\n## Common Filter Conditions\n\n| Filter | Description | Example |\n|--------|-------------|---------|\n| `includeFilter://m:Method` | Filter by request method | `includeFilter://m:put` |\n| `includeFilter://b:Content` | Filter by request body content | `includeFilter://b:cmdname=test` |\n| Regular Expression | Case-sensitive matching | `/Test/` |\n\n> **Tip:** For more protocols and advanced usage, refer to the [Complete Protocol List](./protocols).\n"
  },
  {
    "path": "docs/en/docs/rules/pac.md",
    "content": "# pac\n\nProxy Auto-Config (PAC) is a mechanism that automatically determines request proxy rules using JavaScript scripts, allowing you to dynamically select a proxy or direct connection based on criteria such as URL, domain name, and IP address.\n\n## Rule Syntax\n``` txt\npattern pac://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation content, supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n# Embedded PAC Script\n``` test.pac\nfunction FindProxyForURL(url, host) {\n  // ...\n}\n```\nwww.example.com/path pac://{test.pac}\n\n# Values\nwww.example.com/path1 pac://{test2.pac}\n\n# Local File\nwww.example.com/path3 pac:///User/xxx/test.pac\n\n# Remote PAC Script\n* pac://https://raw.githubusercontent.com/imweb/node-pac/master/test/scripts/normal.pac\n````\n\n## Advanced Usage\nAfter proxying a request to an upstream proxy, by default the upstream proxy will use DNS to obtain the server IP address based on the requested domain name before continuing the request. If you want the upstream proxy to continue the request based on a specific IP address and port, you can do this:\n``` txt\nwww.example.com pac://https://xxx/path/normal.pac 1.1.1.1 enable://proxyHost\nwww.example.com pac:///User/xxx/test.pac 1.1.1.1:8080 enable://proxyHost\n```\n> `1.1.1.1` Equivalent to `host://1.1.1.1`\n\n\n## Notes  \nThe `pac` protocol only applies to the substituted URL (i.e., the `Final URL` shown in the Overview). If the `Final URL` is empty, it will take effect on the original request URL.  \n\nFor example, with the rule:  \n```  \nwww.example.com/api www.example.com pac://https://xxx/path/normal.pac  \n```  \nWhen a request is made to `https://www.example.com/api/path`, Whistle processes it and changes the URL to `https://www.example.com/path` (this becomes the `Final URL`). Although the intention is to apply the PAC script `https://xxx/path/normal.pac` to `https://www.example.com/path`, the `pac` rule only matches the original domain `www.example.com/api` before substitution. Since the converted `Final URL` is now `www.example.com/path`, this rule will not be triggered.  \n\nTo ensure the rule also applies to the substituted request, you can break it into two separate rules:  \n```  \nwww.example.com/api www.example.com  \nwww.example.com pac://https://xxx/path/normal.pac  \n```  \n\nThis way, the original request is first rewritten by the first rule, generating a new `Final URL`. Then, the second `pac` rule matches this new URL.\n"
  },
  {
    "path": "docs/en/docs/rules/pathReplace.md",
    "content": "# pathReplace\nProvides functionality similar to JavaScript's String.replace() method, dynamically modifying the path portion of a URL using regular expressions or string matching.\n\n> URL structure:\n> ``` txt\n> https://www.example.com:8080/path/to/resource?query=string\n> \\___/ \\_____________/\\____/\\____________________________/\n> |                 |           |           |\n> Protocol (scheme) Host (host) Port (path) Path (path)\n> ```\n> \n> The `path` section refers to `path/to/resource?query=string`, which excludes the leading `/`\n\n## Rule Syntax\n``` txt\npattern pathReplace://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following channels: <br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching: • Request URL/Method/Header/Content • Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.example.com/path pathReplace://123=abc\n```\nVisit `https://www.example.com/path/123?test=123&value=123`. The server receives the following URL: `https://www.example.com/path/abc?test=abc&value=abc`\n\n#### Replace Multiple Strings\n\n```` txt\n``` test.json\ntest: name\n123: abc\n```\nwww.example.com/path2 pathReplace://{test.json}\n````\nVisit `https://www.example.com/path2/123?test=123&value=123`. The server receives the following URL: URL: `https://www.example.com/path2/abc?name=abc&value=abc`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 pathReplace:///User/xxx/test.json\nwww.example.com/path2 pathReplace://https://www.xxx.com/xxx/params.json\n# Editing a Temporary File\nwww.example.com/path3 pathReplace://temp/blank.json\n````\n\n## Notes  \nThe following configuration is intended to remove a specific path segment:  \n```txt  \nwww.example.com/api/ pathReplace://(/api/=/)  \n```  \n\n**Expected outcome:**  \nReplace `/api/` in `https://www.example.com/api/xxx` with `/`, resulting in `https://www.example.com/xxx`.  \n\n**Actual issue:**  \nWhistle interprets `/api/` as a regular expression rather than a plain string, causing extra slashes to appear after replacement:  \n`https://www.example.com///xxx`.\n\n> Even if `/api/` is treated as a string, it cannot match `api/xxx/...`. The path matched by pathReplace does not contain the leading `/`\n\n**Solution:**  \n\n```txt  \nwww.example.com pathReplace://(/^api//=)\n```  \n> Regular expressions in Whistle rules do not require escaping `/`.\n>\n> New versions of Whistle can also use `delete://pathname.0` to delete the `api/` path segment in the above URL. For details, see [delete://pathname.xxx](./delete)\n\n## Related Protocols\n1. Modify request parameters: [urlParams](./urlParams)\n2. Delete the path: [delete://pathname.xxx](./delete)\n3. Delete request parameters: [delete://urlParams.xxx](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/pattern.md",
    "content": "`pattern` is the first part of Whistle rules, used to match request URLs. It supports various matching methods including domains, paths, wildcards, and regular expressions.\n\nThrough `pattern`, you can:\n- Precisely match specific domains or paths\n- Use wildcards to match a group of related requests\n- Use regular expressions to implement complex matching logic\n- Support three different types of URL matching\n\n## Request URL Types\n\nWhistle supports three types of request URLs:\n\n| Type | Format | Examples |\n| :--- | :--- | :--- |\n| **Tunnel Proxy** | `tunnel://domain[:port]` | `tunnel://www.test.com:443` |\n| **WebSocket** | `ws[s]://domain[:port]/[path/to[?query]]` | `wss://www.test.com/path?a=1&b=2`<br>`ws://www.example.com:8080/path` |\n| **Regular HTTP/HTTPS** | `http[s]://domain[:port]/[path/to][?query]` | `https://www.test.com/path`<br>`http://www.example.com/path?a=1&b=2` |\n\n## Domain Matching\n\n### Domain Structure\n```txt\n[[schema]://]domain[:port]\n```\n\n**Parameter Description**:\n- `domain`: Domain name or IP address, supports wildcards\n- `port`: Port number (optional), supports wildcards\n- `schema`: Protocol type (optional, such as `http`, `https`, `ws`, `wss`, `tunnel`), supports wildcards\n- `//`: Represents using the current request's protocol (automatically adapts to HTTP/HTTPS)\n\n### Matching Formats\n\n| Type | Format | Examples |\n| :--- | :--- | :--- |\n| **Normal Domain** (supports wildcards) | `domain`<br>`IP`<br>`//domain`<br>`//IP` | `www.example.com`<br>`1.2.3.4`<br>`*.example.com`<br>`//www.example.com`<br>`//1.2.3.4` |\n| **Domain with Port** (port supports wildcards) | `domain:port`<br>`//domain:port` | `www.example.com:8080`<br>`//www.ex*le.com:8*` |\n| **Domain with Protocol** (protocol supports wildcards) | `schema://domain[:port]` | `tunnel://www.*amp*.com`<br>`ws*://**.example.com:443`<br>`http*://www.example.com:8*8` |\n\n### Wildcard Explanation for Domains\n\n#### Domain Wildcards\n- `*`: Equivalent to regex `/[^/?.]*/` (i.e., 0 or any number of non-`.` characters in the domain)\n- `**`: Equivalent to regex `/[^/?]*/` (i.e., 0 or any number of characters in the domain)\n- `***` (and above): Not recommended\n\n**Examples**:\n- `www.example*.com`: Can match `www.example.com`, `www.examplexxx.com:8080`, etc., but cannot match `www.example.x.com`\n- `*.example.com`: Can match `www.example.com`, `www.example.com:8080`, but cannot match `x.www.example.com`\n- `**.example.com`: Can match `x.y.z.www.example.com`, `x.y.www.example.com:8080`, etc., but cannot match `example.com`\n\n#### Port Wildcards\n- `*` (and above): Equivalent to regex `/\\d*/` (i.e., 0 or any number of digits)\n\n**Examples**:\n- `http://www.example.com:8*8`: Matches `http://www.example.com:88`, `http://www.example.com:8888`, etc., but cannot match `http://www.example.com:8080`\n\n#### Protocol Wildcards\n- `*` (and above): Equivalent to regex `/[a-z]*/` (i.e., 0 or any number of characters in the protocol)\n\n**Examples**:\n- `http*://www.example.com`: Matches `http://www.example.com` and `https://www.example.com:8080`\n\n## Path Matching\n\nURL Path Structure:\n```txt\n[[schema:]//]domain[:port]/path?query\n```\n\n**Example**: `https://www.example.com/data/test/result?q=123`\n\n### Matching Formats\n\n#### 1. Path Without Protocol (can match any protocol)\n- `www.example.com[:port]/[path/to[?query]]`\n- `//www.example.com[:port]/[path/to[?query]]`\n\n#### 2. Path With Protocol (matches requests with specified protocol)\n- `ws[s]://www.example.com[:port]/[path/to[?query]]`\n- `http[s]://www.example.com[:port]/[path/to[?query]]`\n\n> **Note**: TUNNEL requests do not have paths.\n\n#### 3. With Wildcards\n- `ws*://*.example.com/path/to`\n- `http*[s]*://www.example*.com:8*/path/to`\n\n## Detailed Explanation of Matching Mechanisms\n\n#### Basic Path Matching  \nMatches specified hosts and paths along with all their subpaths, supporting multiple protocols:\n\n**Supported Protocols**:  \n- `http://` / `https://` (HTTP/HTTPS)  \n- `tunnel://` (Tunnel Proxy)  \n- `ws://` / `wss://` (WebSocket connections)  \n\n**Path Matching Rules**:  \nMatches `www.example.com/path` and all its subpaths:  \n- ✅ `www.example.com/path`  \n- ✅ `www.example.com/path/`  \n- ✅ `www.example.com/path/subfolder`  \n- ✅ `www.example.com/path/file.html`  \n- ✅ `www.example.com/path/subfolder/file?query=1`  \n- ❌ `www.example.com/path-other` (does not start with `/path`)  \n- ❌ `www.example.com/path123` (not an exact prefix of `/path`)  \n\n#### Wildcard Matching Example  \nRule:  \n```txt\nwww.example*.com/path/to www.test.com/test\n```\n\n**Matching Scenarios**:  \n- `https://www.example123.com/path/to?query=abc`  \n  → mapped to `https://www.test.com/test?query=abc`  \n- `https://www.example123.com/path/to/subpage`  \n  → mapped to `https://www.test.com/test/subpage`  \n- `wss://www.example456.com/path/to/api`  \n  → mapped to `wss://www.test.com/test/api`  \n\n**Non-matching Scenarios**:  \n- `https://www.example123.com/path/to123` (path does not end with `/to`)  \n- `https://example123.com/path/to` (missing www prefix)  \n- `https://www.example123.com/path` (incomplete path)  \n\n#### **Exact Matching with Query Parameters**  \n\nRule:  \n```txt\nwww.demo*.com/path/to?name= www.test.com/test\n```\n\n**Rule Explanation**:  \n- Path must exactly match `/path/to`  \n- Must include the `name=` query parameter (case-sensitive)  \n- After matching, the `name=` parameter is removed while other parameters are retained  \n\n**Matching Scenarios**:  \n- `https://www.demo.com/path/to?name=john&age=20`  \n  → mapped to `https://www.test.com/test?age=20`  \n- `https://www.demo.com/path/to?name=&sort=asc`  \n  → mapped to `https://www.test.com/test?sort=asc`  \n\n**Non-matching Scenarios**:  \n- `https://www.demo.com/path/to/extra?name=john` (path not exact)  \n- `https://www.demo.com/path/to?Name=john` (parameter name case mismatch)  \n- `https://www.demo.com/path/to?user=john` (missing name parameter)\n\n## Path Wildcard Matching\n\nSince `*` is a valid URL path character, when it needs to be used as a wildcard, add `^` before the expression to explicitly declare:\n\n```txt\n^[[schema:]//]domain[:port]/pa**th?qu*ery\n```\n\n**Example**: `^http*://**.example.com/data/*/result?q=*23`\n\nWhistle internally converts wildcard paths into corresponding regular expressions with the following conversion rules:\n\n### 1. Protocol, Domain, Port Wildcard Rules\nSame as domain matching above.\n\n### 2. Path Part Wildcard Rules\n\n| Wildcard | Regex Equivalent | Matching Scope | Example |\n| :--- | :--- | :--- | :--- |\n| `*` | `/[^?/]*/` | Single-level path (excluding `/` and `?`) | `^.../*/*.js` → `.../a/b.js` |\n| `**` | `/[^?]*/` | Multi-level path (excluding `?`) | `^.../**file` → `.../a/b/c/test-file` |\n| `***` | `/.*/` | Any character (including `/` and `?`) | `^.../data/***file` → `.../a/b/c?test=file` |\n\n### 3. Query Parameter Wildcard Rules\n\n| Wildcard | Regex Equivalent | Matching Scope | Example |\n| :--- | :--- | :--- | :--- |\n| `*` | `/[^&]*/` | Single parameter value (excluding `&`) | `^...?q=*123` → `...?q=abc123` |\n| `**` | `/.*/` | Any character (including `&`) | `^...?q=**123` → `...?q=abc&test=123` |\n\n> **Memory Tip**: Mainly remember the matching scope of the three wildcards `*`, `**`, `***`.\n\n## Regular Expression Matching\n\nIn addition to simple matching rules, Whistle provides complete regular expression support, with syntax fully compatible with JavaScript regular expressions:\n\n```txt\n/pattern/[flags]\n```\n\n**Parameter Description**:\n- `pattern`: Regular expression body\n- `flags`: Matching mode modifiers (optional), supports:\n  - `i`: Case-insensitive, e.g., `/abc/i` matches \"AbC\"\n  - `u`: Enable Unicode support, e.g., `/\\p{Emoji}/u` matches \"😀\"\n\n**Examples**:\n```txt\n/\\.test\\./          # Matches \".test.\"\n/key=value/i        # Case-insensitive match for \"key=value\"\n/\\/statics\\//ui     # Unicode mode match for \"/statics/\"\n```\n\n## Submatch Value Passing\n\nIn Whistle rule configuration, you can reference submatch content from wildcard or regular expression matches through `$0`, `$1` to `$9`, and pass them to operation values:\n\n```txt\npattern protocol://$0_$1_$2_..._$1\n```\n\n**Parameter Description**:\n- **$0**: Complete match result\n- **$1 - $9**: Content of corresponding capture groups\n\n### Wildcard Matching Value Passing\n```txt\n^http://*.example.com/v0/users/** file:///User/xxx/$1/$2\n```\n\n**Matching Example**:\n- Request URL: `http://www.example.com/v2/users/alice/test.html?q=1`\n- Value Passing Result:\n  - `$1` = `www`\n  - `$2` = `users/alice`\n- Final Replacement: Local file `/User/xxx/www/alice/test.html` content\n\n### Regular Expression Matching Value Passing\n```txt\n/regexp\\/(user|admin)\\/(\\d+)/ reqHeaders://X-Type=$1&X-ID=$2\n```\n\n**Matching Example**:\n- Request URL: `.../regexp/admin/123`\n- Value Passing Result:\n  - `$1` = `admin`\n  - `$2` = `123`\n- Final Effect: Add request headers `X-Type: admin` and `X-ID: 123`\n\n## Special Notes\n\nDomain matching and path matching automatically concatenate paths when mapping to local file/directory paths or new remote URLs, i.e.:\n\n```txt\nhttps://*.example.com/path/to https://www.test.com/test\n\nwww.example.com file:///Usr/test\n```\n\n**Examples**:\n- Accessing `https://abc.example.com/path/to/x/y/z?query` will automatically be replaced with new URL: `https://www.test.com/test/x/y/z?query`\n- Accessing `https://wwww.example.com/path/to/index.html?query` will automatically be replaced with local file: `https://www.test.com/path/to/index.html` (automatically removes `query`)\n\n## Configuration Examples\n\n### Basic Matching\n```txt\n# Exact domain matching\napi.example.com proxy://127.0.0.1:3000\n\n# Port-specific matching\nwww.example.com:8080 file:///local/dev\n\n# Path matching\nwww.example.com/api/users file://{user-data}\n```\n\n### Wildcard Matching\n```txt\n# Match all subdomains\n**.example.com proxy://127.0.0.1:8080\n\n# Match subdomains with specific prefix\ndev-**.example.com file:///(dev-mock)\n\n# Match all HTTP/HTTPS requests\nhttp*://www.example.com  cache://3600\n```\n\n### Regular Expression Matching\n```txt\n# Match user pages with numeric IDs\n/^https?://www\\.example\\.com/user/(\\d+)/ file://(user-$1)\n\n# Case-insensitive match for specific path\n/\\/api\\/v1\\/data/i resBody://({\"version\":\"v1\"})\n\n# Match static resource files\n/\\.(jpg|png|gif|css|js)$/i cache://86400\n```\n\n### Complex Matching\n```txt\n# Combine wildcards and paths\n^https://**.example.com/api/*/v*/users reqHeaders://x-api-version=$3\n\n# Multi-condition matching\nwww.example.com/api file://({\"status\":\"ok\"}) includeFilter://m:GET\nwww.example.com/api file://({\"status\":\"created\"}) includeFilter://m:POST\n```\n\n## Troubleshooting\n\n### Q: Rules Not Matching Requests\n**A:** Check:\n1. Whether the URL format is correct\n2. Whether it contains the correct protocol and port\n3. Whether wildcards or regular expressions are correct\n4. Whether there are higher-priority rules overriding\n\n### Q: Regular Expressions Not Working\n**A:** Check:\n1. Whether the regular expression syntax is correct\n2. Whether special characters need to be escaped\n3. Whether matching mode flags are set correctly\n\n### Q: Submatch Value Passing Error\n**A:** Check:\n1. Whether capture group numbers are correct\n2. Whether wildcard matching correctly captures expected content\n3. Whether regular expression capture groups work as expected\n\n## Extended Reading\n\n- [Rule Syntax Documentation](./rule): Understand the complete rule syntax structure\n- [Operation Instructions Documentation](./operation): Learn how to configure operation instructions\n- [Filters Documentation](./filters): Understand how to precisely control rule activation conditions\n"
  },
  {
    "path": "docs/en/docs/rules/pipe.md",
    "content": "# pipe\nTransfers HTTP/HTTPS/WebSocket/TUNNEL request/response data streams to the plugin for processing.\n\n## Rule Syntax\n``` txt\npattern pipe://plugin-name(pipeValue) [filters...]\n```\n> `(pipeValue)` is optional and can be obtained via `req.originalReq.pipeValue` in the plugin hook.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression that matches the request URL | [Matching Pattern Documentation](./pattern) |\n| plugin-name(pipeValue) | Plugin name + optional parameters | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\ntunnel://wwww.example.com pipe://test\ntunnel://test-tunnel.example.com pipe://test-pipe-tunnel(abc)\n\nwss://test-ws.example.com/path pipe://test-pipe-ws\n\nhttps://www.example.com/path pipe://test-pipe-http(123)\n\n```\n\nFor specific usage, refer to [Plugin Development Documentation](../extensions/dev.md#pipe)\n"
  },
  {
    "path": "docs/en/docs/rules/plugin-vars.md",
    "content": "# `%` Symbol Usage\n\nThe plugin management interface allows you to intuitively configure various parameters. For example, you can enable the [whistle.autosave](https://github.com/whistle-plugins/whistle.autosave) plugin to support the following configurations:\n- Enable storage of captured packet data\n- Configure the directory for storing captured packet data\n\nIn addition to the management interface, you can also use the `%` symbol directly in the rule file for quick configuration.\n\n## Global configuration (applies to all requests)\n``` txt\n%autosave=123\n%autosave.enableAutoSave=true\n%autosave.storageDir=/User/xxx/test/sessions\n```\n\n## Fine-grained configuration (for specific requests)\n``` txt\nwww.test.com/api %autosave=abc\nwww.test.com/api %autosave.enableAutoSave=false [filters...]\nwww.test.com/api %autosave.storageDir= [filters...]\n```\n\n## Retrieving plugin variable values\nIn plugin hooks, you can retrieve the configured variable list using the following code:\n\n``` js\nreq.originalReq.globalPluginVars; // Global variables, such as ['123', 'enableAutoSave=true', 'storageDir=/User/xxx/test/sessions']\nreq.originalReq.pluginVars; // Variables for fine-grained configuration, such as ['abc', 'enableAutoSave=false', 'storageDir=']\n```\n\n## Enable automatic rule prompts\nIf the configuration items via plugin variables are fixed, you can configure optional options in the `whistleConfig` field in the plugin's `package.json`, so that automatic prompts can be added to the rules:\n\n#### Simple Prompt\n``` json\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": true,\n  ...\n}\n```\nAfter completing the above configuration, when you type the `%` character in the Whistle Rules editor, a plugin variable in the following format will be automatically suggested:\n``` txt\n%test-plugin-vars=\n```\n\n#### Anonymous Key Value Hints\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintList\": [\n        \"test1\",\n        \"test2\",\n        \"test3\"\n      ]\n    }\n  },\n  ...\n}\n```\n\n<img src=\"/img/plugin-vars-hint-list.png\" width=\"260\" />\n\nValue and display content separated:\n\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n      \"hintList\": [\n        {\n          \"text\": \"test1\",\n          \"displayText\": \"displayText1\"\n        },\n        {\n          \"text\": \"test2\",\n          \"displayText\": \"displayText2\"\n        },\n        {\n          \"text\": \"test3\",\n          \"displayText\": \"displayText3\",\n          \"help\": \"https://www.example.com/path\"\n        }\n      ]\n    }\n  }\n  ...\n}\n```\n> Both `displayText` and `help` are optional. When configuring `help: Help Link`, when the hint is selected, press `F1` on the keyboard. key automatically opens the help link\n\n<img src=\"/img/plugin-vars-display-list.png\" width=\"220\" />\n\n#### Setting the Key Name\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n    \"hintList\": [\n      {\n        \"text\": \"test1\",\n        \"displayText\": \"displayText1\"\n      },\n      {\n        \"text\": \"test2\",\n        \"displayText\": \"displayText2\"\n      },\n      {\n        \"text\": \"test3\",\n        \"displayText\": \"displayText3\",\n        \"help\": \"https://www.example.com/path\"\n      }\n    ],\n    \"hintSuffix\": [\n      \"=\",\n      \".key1=123\",\n      \".key2\"\n    ]\n  }\n}\n...\n}\n```\n<img src=\"/img/plugin-vars-key-hint.png\" width=\"300\" />\n\nSelecting `%test-plugin-vars=` will automatically display the contents of `hintList`:\n\n<img src=\"/img/plugin-vars-display-list.png\" width=\"220\" />\n\n#### Using the backend interface\n\n``` js\n{\n  \"name\": \"@scope/whistle.test-plugin-vars\",\n  ...\n  \"whistleConfig\": {\n    \"pluginVars\": {\n    \"hintSuffix\": [\n      \"=\",\n      \".key1=123\",\n      \".key2\"\n    ],\n    \"hintUrl\": \"/cgi-bin/plugin-vars\"\n  }\n  }\n  ...\n}\n```\n> `hintList` and `hintUrl` They are mutually exclusive; only one can be used at a time. `hintSuffix` is optional.\n\n<img src=\"/img/plugin-vars-hint-url1.png\" width=\"360\" />\n\n<img src=\"/img/plugin-vars-hint-url2.png\" width=\"360\" />\n\nFor the implementation of `/cgi-bin/plugin-vars`, refer to the [Plugin Development Documentation](../extensions/dev#rules-hint)\n"
  },
  {
    "path": "docs/en/docs/rules/protocols.md",
    "content": "# Protocol List\n\n## Special Rules\n1. [@ (Introduces remote rules or sets client certificates)](./@)\n2. [% (Sets plugin variable values)](./plugin-vars)\n\n## Map Local (Replaces response content with a local file)\n1. [file](./file)\n2. [xfile](./xfile)\n3. [tpl](./tpl)\n4. [xtpl](./xtpl)\n5. [rawfile](./rawfile)\n6. [xrawfile](./xrawfile)\n\n## Map Remote (Returns data from another URL)\n1. [https](./https)\n2. [http](./http)\n3. [wss](./wss)\n4. [ws](./ws)\n5. [tunnel](./tunnel)\n\n## DNS Spoofing (Modifies the requesting IP address)\n1. [host](./host)\n2. [xhost](./xhost)\n3. [proxy (http-proxy)](./proxy)\n4. [xproxy (xhttp-proxy)](./xproxy)\n5. [https-proxy](./https-proxy)\n6. [xhttps-proxy](./xhttps-proxy)\n7. [socks](./socks)\n8. [xsocks](./xsocks)\n9. [pac](./pac)\n\n## Rewrite Request (modify request data)\n1. [urlParams](./urlParams)\n2. [pathReplace](./pathReplace)\n3. [sniCallback](./sniCallback)\n4. [method](./method)\n5. [tlsOptions](./cipher)\n6. [reqHeaders](./reqHeaders)\n7. [forwardedFor](./forwardedFor)\n8. [ua](./ua)\n9. [auth](./auth)\n10. [cache](./cache)\n11. [referer](./referer)\n12. [reqCharset](./reqCharset)\n13. [reqCookies](./reqCookies)\n14. [reqCors](./reqCors)\n15. [reqType](./reqType)\n16. [reqBody](./reqBody)\n17. [reqMerge](./reqMerge)\n18. [reqPrepend](./reqPrepend)\n19. [reqAppend](./reqAppend)\n20. [reqReplace](./reqReplace)\n21. [reqWrite](./reqWrite)\n22. [reqWriteRaw](./reqWriteRaw)\n23. [reqRules](./reqRules)\n24. [reqScript](./reqScript)\n\n## Rewrite Response (modify response data)\n1. [statusCode](./statusCode)\n2. [replaceStatus](./replaceStatus)\n3. [redirect](./redirect)\n4. [locationHref](./locationHref)\n5. [resHeaders](./resHeaders)\n6. [responseFor](./responseFor)\n7. [resCharset](./resCharset)\n8. [resCookies](./resCookies)\n9. [attachment](./attachment)\n10. [resCors](./resCors)\n11. [resType](./resType)\n12. [resBody](./resBody)\n13. [resMerge](./resMerge)\n14. [resPrepend](./resPrepend)\n15. [resAppend](./resAppend)\n16. [resReplace](./resReplace)\n17. [htmlPrepend](./htmlPrepend)\n18. [htmlBody](./htmlBody)\n19. [htmlAppend](./htmlAppend)\n20. [cssPrepend](./cssPrepend)\n21. [cssBody](./cssBody)\n22. [cssAppend](./cssAppend)\n23. [jsPrepend](./jsPrepend)\ntwenty four. [jsBody](./jsBody)\n25. [jsAppend](./jsAppend)\n26. [trailers](./trailers)\n27. [resWrite](./resWrite)\n28. [resWriteRaw](./resWriteRaw)\n29. [resRules](./resRules)\n30. [resScript](./resScript)\n\n## General (Request/Response General Protocol)\n1. [pipe](./pipe)\n2. [delete](./delete)\n3. [headerReplace](./headerReplace)\n\n## Throttle (Traffic Speed Limit)\n1. [reqDelay](./reqDelay)\n2. [resDelay](./resDelay)\n3. [reqSpeed](./reqSpeed)\n4. [resSpeed](./resSpeed)\n\n## Tools\n1. [weinre](./weinre)\n2. [log](./log)\n\n## Settings\n1. [style](./style)\n2. [lineProps](./lineProps)\n3. [enable](./enable)\n4. [disable](./disable)\n\n## Filters\n1. [ignore](./ignore)\n2. [skip](./skip)\n3. [excludeFilter](./excludeFilter)\n4. [includeFilter](./includeFilter)\n"
  },
  {
    "path": "docs/en/docs/rules/proxy.md",
    "content": "# proxy (http-proxy)\nThe `proxy` (or `http-proxy`) directive forwards matching requests through a specified HTTP proxy server. These two directives have identical functionality and can be used interchangeably.\n\n## Rule Syntax\n``` txt\npattern proxy://ipOrDomain[:port] [filters...]\n# Equivalent:\npattern http-proxy://ipOrDomain[:port] [filters...]\n```\n> `port` is optional. If left blank, the default port `80` will be used.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | IP + optional port or domain name + optional port<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Proxy requests to the HTTP proxy: `127.0.0.1:80`\nwww.example.com/path proxy://127.0.0.1 # Default port 80\n\n# Proxy all requests for the current domain to the HTTP proxy: `127.0.0.1:8080`\nwww.example.com proxy://127.0.0.1:8080\n\n# You can also use a domain name.\nwww.example.com/path proxy://test.proxy.com # Default port 80\nwww.example.com proxy://test.proxy.com:8080\n```\n\n## Advanced Usage\nAfter proxying a request to the upstream proxy, by default the upstream proxy will use DNS to obtain the server IP address based on the requested domain name before continuing the request. If you want the upstream proxy to continue the request based on a specific IP address and port, you can do this:\n``` txt\n# Using query parameters\nwww.example.com proxy://127.0.0.1:8080?host=1.1.1.1\nwww.example.com proxy://127.0.0.1:8080?host=1.1.1.1:8080\n\n# Enable via directive\nwww.example.com proxy://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com proxy://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n````\n> `1.1.1.1` is equivalent to `host://1.1.1.1`\n\n## Matching precedence with `host`\n\n#### Default behavior\n\nWhen a request matches both `host` and `proxy` rules:\n- Only the `host` rule takes effect\n- The `proxy` rule is automatically ignored\n\n#### Modify Priority\n| Configuration Method | Syntax | Effect |\n|---------|------|------|\n| **Prioritize proxy** | [`enable://proxyFirst`](./enable) or [`lineProps://proxyFirst`](./lineProps) | Only `proxy` takes effect (overrides `host`) |\n| **Both take effect** | [`enable://proxyHost`](./enable) or [`lineProps://proxyHost`](./lineProps) | Both `proxy` and `host` take effect |\n\n#### Usage Recommendations\n- Use the default behavior in most scenarios\n- Use `proxyFirst` only when special proxy logic is required\n- Use `proxyHost` when dual matching is required\n\n## Notes\n\nThe `host` protocol only applies to the substituted URL (i.e., the `Final URL` shown in the Overview). If the `Final URL` is empty, it will take effect on the original request URL.  \n\nFor example, with the rule:  \n```  \nwww.example.com/api www.example.com 127.0.0.1:1234  \n```  \nA request to `https://www.example.com/api/path` will be processed by Whistle, and the `Final URL` becomes `https://www.example.com/path`. As a result, it will not match `127.0.0.1:1234`.  \n\nIf you want to match the substituted request, you can modify the rules as follows:  \n```  \nwww.example.com/api www.example.com  \nwww.example.com 127.0.0.1:1234  \n```\n"
  },
  {
    "path": "docs/en/docs/rules/rawfile.md",
    "content": "# rawfile\nrawfile is an enhanced version of [file](./file). In addition to supporting all the features of [file](./file), it also allows you to define a complete HTTP response in a file, including:\n- Response status code\n- Response headers\n- Response content\n\n## Rule Syntax\n``` txt\npattern rawfile://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Operation content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching against:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Mock Interface\n1. Local File `/User/xxx/raw.txt`\n    ``` js\n    HTTP/1.1 500 OK\n    Content-Type: application/json\n    X-Custom-Header: value\n\n    {\n      \"status\": \"success\",\n      \"data\": \"your content here\"\n    }\n2. Configure Rules\n    ``` txt\n    https://www.example.com/test/rawfile tpl:///User/xxx/raw.txt\n\n    # Equivalent to\n    # https://www.example.com/test/rawfile file:///User/xxx/test.json replaceStatus://500 resType://json resHeaders://x-custom-header=value\n\n    # Supports remote URLs\n    # pattern rawfile://https://example.com/raw.json\n    ```\n3. Request `https://www.example.com/test/rawfile` Returns:\n    ``` txt\n    // 状态码\n    500\n\n    // 响应头\n    content-type: application/json\n    x-custom-header: value\n    content-length: 56\n\n    // 响应内容\n    {\n      \"status\": \"success\",\n      \"data\": \"your content here\"\n    }\n    ```\n\nFor other functions, see: [file](./file)\n"
  },
  {
    "path": "docs/en/docs/rules/redirect.md",
    "content": "# redirect\nImmediately redirect matching requests (302 Found) to the specified URL without requesting the backend server.\n\n## Rule Syntax\n``` txt\npattern redirect://targetUrl [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| targetUrl | The URL to redirect to, which can be a relative path | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Basic Configuration\n``` txt\nwww.example.com/path redirect://https://www.qq.com\n\nwww.example.com/path2 redirect://../abc/123\n```\n- Visit `https://www.example.com/path/to` and redirect to `https://www.qq.com` (no automatic path concatenation)\n- Visit `https://www.example.com/path2/to` redirects to `https://www.example.com/abc/123`\n\n#### Implementing path concatenation\n``` txt\n# Wildcard\n^www.example.com/path/*** redirect://`https://www.example.com/$1`\n```\n- Visit `https://www.example.com/path/to?query` redirects to `https://www.example.com/to?query`\n\n#### `301` Redirect\n``` txt\nwww.example.com/path/test redirect://https://www.qq.com replaceStatus://301\n```\n\n## Associated Protocols\n1. Pages whose addresses cannot be modified via `302` can use: [locationHref](./locationHref)\n"
  },
  {
    "path": "docs/en/docs/rules/referer.md",
    "content": "# referer\nModify the `referer` field in the request header. Some servers validate the `referer` field in the request header. This protocol can be used to bypass this detection or test backend functionality.\n\n## Rule Syntax\n``` txt\npattern referer://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Full URL link<br/>• Inline/Embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.example.com/path referer://https://www.test.com/x/y/z?xxx\n```\n\n## Associated Protocols\n1. Equivalent to: [reqHeaders://referer=https://xxx](./reqHeaders)\n"
  },
  {
    "path": "docs/en/docs/rules/replaceStatus.md",
    "content": "# replaceStatus\n\nThe `replaceStatus` protocol is used to replace HTTP status codes. It changes the original status code to a specified HTTP status code after the request response is completed. Through the `replaceStatus` protocol, you can:\n- **Modify the status code of server responses**: Replace status codes based on real server responses.\n- **Test handling logic for different status codes**: Test various status code scenarios without modifying backend code.\n- **Error scenario simulation**: Convert normal responses to error status codes to test client fault tolerance.\n- **Redirect testing**: Modify redirect status codes or targets.\n\n## Rule Syntax\n\nThe `replaceStatus` protocol supports multiple configuration methods:\n\n### 1. Inline Value (Direct Specification)\nWrite the status code to replace directly in the rule.\n\n```txt\npattern replaceStatus://code [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/api/old replaceStatus://301\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when you need to return different status codes based on conditions or want to reuse configurations.\n\n````txt\npattern replaceStatus://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n404\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a status code pre-defined in the `Values` panel (central configuration area).\n\n```txt\npattern replaceStatus://{key-of-values} [lineProps...] [filters...]\n```\n\n**Prerequisite**: A key named `key-of-values` with a status code as its value must exist in `Values`.\n\n### 4. File/Remote URL Loading\n**Currently NOT supported** to dynamically load content from local files or remote URLs.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **code** | Yes | The HTTP response status code to replace, such as:<br>• `200` OK<br>• `301` Permanent Redirect<br>• `302` Temporary Redirect<br>• `404` Not Found<br>• `500` Internal Server Error<br>• Supports all standard HTTP status codes. |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Examples\n```txt\n# Replace any response with 404 status code.\nwww.example.com replaceStatus://404\n\n# Replace server errors with normal status codes.\nwww.example.com/api replaceStatus://200 includeFilter://s:500\n\n# Modify redirect status codes.\nwww.example.com/redirect replaceStatus://307\n```\n\n### Simulating Authentication Failure\n```txt\n# Replace normal responses with 401 status code, triggering browser authentication popup.\nwww.example.com/secure-page replaceStatus://401\n\n# Disable the login dialog and directly return 401.\nwww.example.com/secure-page replaceStatus://401 disable://userLogin\n\n# Or use lineProps to achieve the same effect.\nwww.example.com/secure-page replaceStatus://401 lineProps://disableUserLogin\n```\n\n### Conditional Replacement\n```txt\n# Replace only when the original status code is 200 with 500.\nwww.example.com/api replaceStatus://500 includeFilter://s:200\n\n# Replace response status codes only for specific paths.\nwww.example.com/api/admin replaceStatus://403\n```\n\n### Combining with Other Protocols\n````txt\n# Replace status code and modify response content.\nwww.example.com/api/error replaceStatus://500 resBody://{errMsg}\n\n``` errMsg\n{\"message\":\"Custom error message\"}\n```\n\n# Replace with 301 redirect and set new Location header.\nwww.example.com/old-url replaceStatus://301 resHeaders://location=https://www.example.com/new-url\n````\n\n### Using Embedded Values\n````txt\n``` maintenance-status\n503\n```\n\n# Replace all website responses with maintenance status.\nwww.example.com replaceStatus://{maintenance-status}\n````\n\n### Environment-Specific Configuration\n```txt\n# Simulate errors only in testing environment.\ntest.example.com/api replaceStatus://500\n\n# Development environment remains unchanged.\n# dev.example.com/api (no replaceStatus set)\n```\n\n## Common Use Cases\n\n### 1. Error Handling Testing\n```txt\n# Test client handling of server errors.\nwww.example.com/api replaceStatus://500 includeFilter://chance:0.1\n```\n\n### 2. Redirect Behavior Testing\n```txt\n# Test permanent redirect caching.\nwww.example.com/old-page replaceStatus://301\n\n# Test temporary redirect.\nwww.example.com/temp-redirect replaceStatus://302\n```\n\n### 3. Authentication and Authorization Testing\n```txt\n# Test unauthenticated access.\nwww.example.com/protected replaceStatus://401\n\n# Test insufficient permissions.\nwww.example.com/admin replaceStatus://403\n```\n\n### 4. Rate Limiting Testing\n```txt\n# Test too many requests scenarios.\nwww.example.com/api/rate-limited replaceStatus://429 resHeaders://retry-after=60\n```\n\n### 5. Maintenance Mode Testing\n```txt\n# Test maintenance page.\nwww.example.com replaceStatus://503 resBody://(<h1>System Under Maintenance...</h1>)\n```\n\n## Differences from statusCode\n\nThe main distinction between the `replaceStatus` protocol and the [`statusCode`](./statusCode) protocol lies in the processing timing:\n- **`replaceStatus`**: **First requests the backend server**, then replaces the status code after receiving the response.\n- **`statusCode`**: **Does not request the backend server**, immediately returns the specified status code.\n\n## Notes\n\n### 1. Difference from statusCode\n- **Processing timing**: `replaceStatus` takes effect during the response stage, `statusCode` takes effect during the request stage.\n- **Server access**: `replaceStatus` accesses the server, `statusCode` does not.\n- **Use cases**: Use `replaceStatus` when real server data is needed, use `statusCode` for complete simulation.\n\n### 2. Response Content Retention\n- By default, response content remains unchanged when replacing status codes.\n- Response content can be modified by combining with the [`resBody`](./resBody) protocol.\n\n### 3. Authentication Popup Control\n- When replacing with `401` status code, the browser authentication popup is triggered by default.\n- You can disable the popup in the following ways:\n  - Add `disable://userLogin`.\n  - Add `lineProps://disableUserLogin`.\n  - Use the reverse configuration of the [`enable`](./enable) protocol.\n\n### 4. Redirect Handling\n- When replacing with redirect status codes (301, 302, etc.), you usually need to modify the `Location` response header accordingly.\n- Otherwise, the client may not handle the redirect correctly.\n\n### 5. Status Code Compatibility\n- Ensure the replaced status code is compatible with the response content.\n- For example, an HTML page should not be replaced with a 204 (No Content) status code.\n\n## Advanced Usage\n\n### Status Code Conversion Mapping\n```txt\n# Unify all redirects as 302.\nwww.example.com replaceStatus://302 includeFilter://s:301\n```\n\n### Probabilistic Replacement\n```txt\n# Replace responses with errors 10% of the time.\nwww.example.com/api replaceStatus://500 includeFilter://chance:0.1\n```\n\n### Combining Multiple Rules\n```txt\n# Perform different replacements for different original status codes.\nwww.example.com/api replaceStatus://200 includeFilter://s:404\nwww.example.com/api replaceStatus://400 includeFilter://s:403\n```\n\n## Troubleshooting\n\n### Q: Status code replacement is not taking effect.\n**A:** Check:\n1. Whether the rule pattern correctly matches the request URL.\n2. Whether there are other higher-priority rules overriding it.\n3. Whether the filter conditions are met (especially filtering on the original status code).\n\n### Q: Response content does not match the status code.\n**A:** Check:\n1. Whether the response content is also modified.\n2. Whether the response headers are compatible with the new status code.\n3. Whether the client can correctly handle that status code.\n\n### Q: Browser pops up authentication window.\n**A:** For 401 status code:\n1. Check if `disable://userLogin` or `lineProps://disableUserLogin` is added.\n2. Confirm if the rule order is correct.\n\n### Q: Redirect loop.\n**A:** Check:\n1. Whether the correct `Location` header is set.\n2. Whether the redirect target is correct.\n3. Whether multiple redirect rules conflict.\n\n## Related Protocols\n\n1. **Directly return status code**: [statusCode](./statusCode)\n   - Does not request the server, directly returns the specified status code.\n2. **Disable authentication popup**: [disable](./disable) or [lineProps](./lineProps)\n   - Disable the browser login popup triggered by 401 status code.\n3. **Set response content**: [resBody](./resBody)\n   - Add custom content for status code responses.\n4. **Set response headers**: [resHeaders](./resHeaders)\n   - Set necessary response headers for scenarios like redirects.\n\n## Further Reading\n\n- [HTTP Status Codes MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status): Learn the meaning of all HTTP status codes.\n- [Matching Pattern Documentation](./pattern): Learn more about URL matching rules.\n- [Filters Documentation](./filters): Learn more about filter functionalities.\n"
  },
  {
    "path": "docs/en/docs/rules/reqAppend.md",
    "content": "# reqAppend\nInserts the specified content after the existing request body (only valid for requests with a body, such as `POST` and `PUT`).\n> ⚠️ Note: GET, HEAD, and other requests without a body are not affected.\n\n## Rule Syntax\n``` txt\npattern reqAppend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported: <br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching: <br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filters Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path reqAppend://(Hello) reqBody://(-test-) method://post\n```\nRequesting `https://www.example.com/path/to` will result in the request body becoming `-test-Hello`.\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqAppend://{body.txt} reqBody://(-test-) method://post\n````\nRequesting `https://www.example.com/path/to` will result in the request body becoming `-test-Hello world.`.\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqAppend:///User/xxx/test.txt\nwww.example.com/path2 reqAppend://https://www.xxx.com/xxx/params.txt\n# Editing a Temporary File\nwww.example.com/path3 reqAppend://temp/blank.txt\n````\n\n## Associated Protocols\n1. Append content to the request body: [reqPrepend](./reqPrepend)\n2. Inject content before the request body: [reqBody](./reqBody)\n3. Append content to the response body: [resPrepend](./resPrepend)\n"
  },
  {
    "path": "docs/en/docs/rules/reqBody.md",
    "content": "# reqBody\nReplaces the content body of the specified request (only valid for requests with a content body, such as `POST` and `PUT`).\n> ⚠️ Note: GET, HEAD, and other requests without a content body are not affected.\n\n## Rule Syntax\n``` txt\npattern reqBody://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. The following types are supported: <br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching: <br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filters Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path reqBody://(Hello) method://post\n```\nRequesting `https://www.example.com/path/to` will result in the request body becoming `Hello`.\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqBody://{body.txt} method://post\n````\nRequesting `https://www.example.com/path/to` will result in the request body becoming `Hello world.`.\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqBody:///User/xxx/test.txt\nwww.example.com/path2 reqBody://https://www.xxx.com/xxx/params.txt\n# Editing a Temporary File\nwww.example.com/path3 reqBody://temp/blank.txt\n````\n\n## Associated Protocols\n1. Inject content before the request: [reqPrepend](./reqPrepend)\n2. Append content after the request: [reqAppend](./reqAppend)\n3. Replace the response content: [reqBody](./reqBody)\n"
  },
  {
    "path": "docs/en/docs/rules/reqCharset.md",
    "content": "# reqCharset\nModify the `charset` portion of the `content-type` request header to set the encoding of the request content.\n> `content-type` structure:\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## Rule Syntax\n``` txt\npattern reqCharset://encoding [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| encoding | Encoding name, such as `utf8` <br/>• Inline/Embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Set request encoding, request header `content-type` Empty fields are changed to `; charset=utf8`\nwww.example.com/path reqCharset://utf8\n\n# Set the request encoding. The empty `content-type` request header field is changed to `text/html; charset=utf8`\nwww.example.com/path resType://json reqCharset://utf8\n```\n\n## Associated Protocols\n1. Directly modify the request header: [reqHeaders://content-type=xxx](./reqHeaders)\n2. Modify the request content encoding: [reqType://media-type](./reqCharset)\n"
  },
  {
    "path": "docs/en/docs/rules/reqCookies.md",
    "content": "# reqCookies\nModify the request `Cookie` header.\n\n## Rule Syntax\n``` txt\npattern reqCookies://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Cookie object, supported from the following sources:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching against:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\nCookie object structure: `key-value`\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path reqCookies://k1=v1&k2=v2\n````\nAdd two request cookies to the request header: `k1: v1` / `k2: v2`\n\n### Inline Mode\n```` txt\n``` cookies.json\nkey1: value1\nkey2: value2\n```\n# Or\n``` cookies.json\n{\nkey1: 'value1',\nkey2: 'value2'\n}\n```\nwww.example.com/path reqCookies://{cookies.json}\n````\nAdd two request cookies to the request header: `key1: value1` / `key2: value2`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqCookies:///User/xxx/test.json\nwww.example.com/path2 reqCookies://https://www.xxx.com/xxx/params.json\n# By editing a temporary file\nwww.example.com/path3 reqCookies://temp/blank.json\n````\n\n## Related Protocols\n1. Delete request cookies: [delete://reqCookies.xxx](./delete)\n2. Delete all request cookies: [delete://reqHeaders.cookie](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/reqCors.md",
    "content": "# reqCors\nSets the Cross-Origin Resource Sharing (CORS) header for the request to resolve cross-origin request issues.\n\n## Rule Syntax\n``` txt\npattern resCors://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | CORS object, supported from the following sources:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Quick Mode\n``` txt\nwww.example.com/path reqCors://*\n```\nSet the request header `origin: *`\n\n#### Detailed Configuration Mode\n```` txt\n``` cors.json\norigin: *\nmethod: POST\nheaders: x-test\n```\nwww.example.com/path reqCors://{cors.json}\n````\nSetting Request Headers:\n``` txt\norigin: *\naccess-control-request-method: POST\naccess-control-request-headers: x-test\n```\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqCors:///User/xxx/test.json\nwww.example.com/path2 reqCors://https://www.xxx.com/xxx/params.json\n# Editing a Temporary File\nwww.example.com/path3 reqCors://temp/blank.json\n````\n\n## Associated Protocols\n1. Delete request headers: [delete://reqHeaders.orogin](./delete)\n2. Set the response cross-correspondence: [resCors](./resCors)\n"
  },
  {
    "path": "docs/en/docs/rules/reqDelay.md",
    "content": "# reqDelay\n\nSet the request delay time (in milliseconds).\n\n## Rule Syntax\n``` txt\npattern reqDelay://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Time value (in milliseconds)<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Delay 3000 milliseconds (i.e., 3 seconds) for requests\nwww.example.com/path reqDelay://3000\n\n# Abort after 5000 seconds (i.e., 5 seconds) Request\nwww.example.com/path2 reqDelay://5000 enable://abort\n```\n\n## Random Delay\nYou can use [reqScript](./reqScript) to implement a random delay request:\n```` txt\n# Randomly set reqDelay://1000 to reqDelay://6000 milliseconds\n``` delay.js\nrules.push(`* reqDelay://${1000 + Math.ceil(5000 * Math.random())}`);\n\n```\n\nwww.example.com/path reqScript://{delay.js}\n````\n"
  },
  {
    "path": "docs/en/docs/rules/reqHeaders.md",
    "content": "# reqHeaders\nDynamically modify request header information, supporting multiple data sources and batch operations.\n\n## Rule Syntax\n``` txt\npattern reqHeaders://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following channels:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n``` txt\nwww.example.com/path reqHeaders://x-proxy=Whistle\n```\nAccess `https://www.example.com/path/to` You can see the new request headers on Whistle Network or the backend server:\n``` txt\nx-proxy: Whistle\n```\n\n#### Setting Multiple Request Headers\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 reqHeaders://{test.json}\n\n# Equivalent to: www.example.com/path2 reqHeaders://x-test1=1&x-test2=&x-test3=abc\n````\nVisiting `https://www.example.com/path2/to` You can see the new request headers on Whistle Network or the backend server:\n``` txt\nx-test1: 1\nx-test2:\nx-test3: abc\n```\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqHeaders:///User/xxx/test.json\nwww.example.com/path2 reqHeaders://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 reqHeaders://temp/blank.json\n````\n\n## Related Protocols\n1. More flexible way to modify request headers: [headerReplace](./headerReplace)\n2. Deleting request header fields: [delete://reqHeaders.xxx](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/reqMerge.md",
    "content": "# reqMerge\nIntelligently merges specified data objects into the request body, suitable for modifying partial parameters without affecting the remaining content. Supports multiple request formats:\n- Regular form data (`application/x-www-form-urlencoded`)\n- File upload form (`multipart/form-data`)\n- JSON requests (`application/json`)\n\n## Rule Syntax\n``` txt\npattern reqMerge://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following sources:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Inline Method\n``` txt\nwww.example.com/path reqMerge://test=123 reqBody://(name=avenwu) reqType://form method://post\n```\nRequest content received by the server when accessing `https://www.example.com/path/to`:\n``` txt\nname=avenwu&test=123\n```\n\n#### Inline Method\n```` txt\n``` reqMerge.json\na.b.c: 123\nc\\.d\\.e: abc\n```\nwww.example.com/path reqMerge://{reqMerge.json} reqBody://({\"name\":\"avenwu\"}) reqType://json method://post\n````\nAccess `https://www.example.com/path/to` and capture the request content:\n``` js\n{\"name\":\"avenwu\",\"a\":{\"b\":{\"c\":123}},\"c.d.e\":\"abc\"}\n```\n\n#### Local/Remote Resources\n```` txt\nwww.example.com/path1 reqMerge:///User/xxx/test.json\nwww.example.com/path2 reqMerge://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 reqMerge://temp/blank.json\n````\n\n## Note: Request Size Limit\n\nTo ensure processing performance, `reqMerge` enforces a default size limit for request content.\n\n*   **Limit Specification**: The automatic merge processing is only applied to requests with a body size **less than 2MB**. Requests exceeding this size will be skipped.\n*   **How to Override**: If you need to handle larger requests, you can explicitly enable this capability by adding the following directive to your matching rule:\n\n``` txt\npattern enable://reqMergeBigData\n\n# or\nwww.example.com/path1 reqMerge:///User/xxx/test.json lineProps://enableBigData\n```\nOnce enabled, `reqMerge` will attempt to process larger request volumes. Please note that this may increase memory consumption and processing time.\n\n## Associated Protocols\n1. Replace with a keyword or regular expression: [reqReplace](./reqReplace)\n2. Modify the response content object: [resMerge](./resMerge)\n"
  },
  {
    "path": "docs/en/docs/rules/reqPrepend.md",
    "content": "# reqPrepend\nInserts the specified content at the beginning of the existing request body (only valid for requests with a body, such as `POST` and `PUT`).\n> ⚠️ Note: GET, HEAD, and other requests without a body are not affected.\n\n## Rule Syntax\n``` txt\npattern reqPrepend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path reqPrepend://(Hello) reqBody://(-test-) method://post\n```\nRequesting `https://www.example.com/path/to` will result in the request body becoming `Hello-test-`.\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path reqPrepend://{body.txt} reqBody://(-test-) method://post\n````\nRequesting `https://www.example.com/path/to` will result in the request body becoming `Hello world.-test-`.\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqPrepend:///User/xxx/test.txt\nwww.example.com/path2 reqPrepend://https://www.xxx.com/xxx/params.txt\n# Editing a Temporary File\nwww.example.com/path3 reqPrepend://temp/blank.txt\n````\n\n## Associated Protocols\n1. Inject content before the request body: [reqBody](./reqBody)\n2. Append content after the request body: [reqAppend](./reqAppend)\n3. Inject content before the response body: [resPrepend](./resPrepend)\n"
  },
  {
    "path": "docs/en/docs/rules/reqReplace.md",
    "content": "# reqReplace\nReplaces the request body content using a method similar to JavaScript's `String.replace()` (only valid for requests with a body, such as `POST` and `PUT`). Supports multiple text formats:\n- Form data (`application/x-www-form-urlencoded`)\n- JSON (`application/json`)\n- XML (`application/xml`)\n- HTML (`text/html`)\n- Plain text (`text/xxx`)\n\n## Rule Syntax\n``` txt\npattern reqReplace://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Replacement configuration object, supported from the following sources:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Inline Mode\n```` txt\nwww.example.com/path reqBody://(00user-11test-22user-33test) reqReplace://user=abc&/\\d+/g=number reqType://txt method://post\n````\nRequest `https://www.example.com/path/to`. The backend receives the following request:\n``` txt\nnumberabc-numbertest-numberabc-numbertest\n```\n\n### Inline Mode\n```` txt\n``` reqReplace.json\nuser: name\n/\\d+/g: num\n```\n# or (note the escape characters)\n``` reqReplace.json\n{\n  'user': 'name',\n  '/\\\\d+/g': 'num'\n}\n```\nwww.example.com/path reqBody://(00user-11test-22user-33test) reqReplace://{reqReplace.json} reqType://txt method://post\n````\nRequest `https://www.example.com/path/to` The request content received by the backend is:\n``` txt\nnumname-numtest-numname-numtest\n```\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 reqReplace:///User/xxx/test.json\nwww.example.com/path2 reqReplace://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 reqReplace://temp/blank.json\n````\n\n## Association Protocol\n1. Object Merge: [reqMerge](./reqMerge)\n2. Complete Replacement: [reqBody](./reqBody)\n"
  },
  {
    "path": "docs/en/docs/rules/reqRules.md",
    "content": "# reqRules\nBatch multiple rules for matching requests to meet request processing requirements in complex scenarios.\n\n## Rule Syntax\n``` txt\npattern reqRules://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Rule content, supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n``` test.txt\n* file://(<div>hello<div>)\n* resType://text\n```\n\n``` test2.txt\n* resAppend://(test)\n```\n\nwww.example.com/path reqRules://{test.txt} reqRules://{test2.txt}\n````\nVisiting `https://www.example.com/path/to` returns:\n``` txt\n<div>hello<div>test\n```\n## Related Protocols\n1. Request Phase Script Rules: [reqScript](./reqScript)\n2. Response Phase Batch Rules: [resRules](./resRules)\n3. Response Phase Script Rules: [resScript](./resScript)\n4. More Complex Customization Requirements: [Plugin Development](../extensions/dev)\n"
  },
  {
    "path": "docs/en/docs/rules/reqScript.md",
    "content": "# reqScript\nDynamically generate rules during the request phase using JavaScript to implement complex request processing logic. The script can access request context information and dynamically generate matching rules.\n\n## Rule Syntax\n``` txt\npattern reqScript://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | JS script to generate rules. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n``` test.js\nif (method === 'GET') {\nrules.push('* resType://text');\nrules.push('* file://(<div>GET-Request</div>)');\n} else {\nrules.push('* statusCode://403');\n}\n```\nwww.example.com/path reqScript://{test.js}\n````\nAccessing `https://www.example.com/path/to` returns:\n\n#### Available global variables\n\n| Variables/Methods | Description |\n|--------------------|----------------------------------------------------------------------|\n| `url` | Full request URL |\n| `method` | Request method (GET/POST, etc.) |\n| `ip`/`clientIp` | Client IP address |\n| `headers` | Request header object |\n| `body` | Request body (max 16KB) |\n| `rules` | Rule array, add new rules via push |\n| `values` | Temporary value storage object |\n| `render(tpl,data)` | Micro template rendering function |\n| `getValue(key)` | Get the value in `Values` |\n| `parseUrl` | Same as `url.parse` in Node.js |\n| `parseQuery` | Same as `querystring.parse` in Node.js |\n\n## Related Protocols\n1. Request-phase script rules: [reqScript](./reqScript)\n2. Request-phase batch rules: [reqRules](./reqScript)\n3. Response-phase batch rules: [resRules](./resRules)\n4. More complex customization requirements: [Plugin development](../extensions/dev)\n"
  },
  {
    "path": "docs/en/docs/rules/reqSpeed.md",
    "content": "# reqSpeed\nSets the request body transmission speed (unit: kb/s, kilobits per second).\n\n## Rule Syntax\n``` txt\npattern reqSpeed://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Transmission speed (unit: kb/s, kilobits per second)<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Limit the request content transmission speed to 3kb/s\nwww.example.com/path reqSpeed://3\n```\nThis rule has no effect on `CONNECT` (TUNNEL requests), `UPGRADE` (WebSocket requests), and requests with a response status code of `204` and no response body.\n"
  },
  {
    "path": "docs/en/docs/rules/reqType.md",
    "content": "# reqType\nModify the `media-type` and `charset` parts of the `content-type` request header.\n> `content-type` structure:\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## Rule Syntax\n``` txt\npattern reqType://type[;charset] [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| type[;charset] | `type` request type, `charset` encoding <br/>• Inline/embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching: <br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n`reqType` is mainly used to modify the request type The `media-type` part, the `charset` part is optional. If the `charset` part is not set, the original `charset` part of the request (if present) will be retained.\n\n## Configuration Example\n\n#### Shortcut Commands (Strings without `/`)\n- `urlencoded`: `application/x-www-form-urlencoded`\n- `form`: `application/x-www-form-urlencoded`\n- `json`: `application/json`\n- `xml`: `application/xml`\n- `text`: `text/plain`\n- `upload`: `multipart/form-data`\n- `multipart`: `multipart/form-data`\n- Other: [mime](https://github.com/broofa/mime).lookup(type)\n\n``` txt\n# Without encoding, the `content-type` request header becomes `application/x-www-form-urlencoded`\nwww.example.com/path reqType://form\n\n# With encoding, the `content-type` request header becomes `application/json;charset=utf8`\nwww.example.com/path reqType://json;charset=utf8\n```\n#### Complete Type\n``` txt\n# Change the `content-type` in the request header to `text/plain`\nwww.example.com/path reqType://text/plain\n\n# Change the `content-type` in the request header to `text/plain;charset=utf8`\nwww.example.com/path reqType://text/plain;charset=utf8\n```\n\n## Associated Protocols\n1. Directly modify the request header: [reqHeaders://content-type=xxx](./reqHeaders)\n2. Modify the request content encoding: [reqCharset://encoding](./reqCharset)\n"
  },
  {
    "path": "docs/en/docs/rules/reqWrite.md",
    "content": "# reqWrite\nSaves the request body to a specified directory or file. Suitable for scenarios where request data needs to be recorded:\n- Automatically generates a file path based on the request URL\n- Uses a safe write policy (can be forced to overwrite using [enable://forceReqWrite](./enable))\n- Only valid for requests with a body (POST/PUT/PATCH, etc.)\n- Automatically skips requests without a body, such as GET/HEAD\n- Automatically skips if saving fails\n\n## Rule Syntax\n``` txt\npattern reqWrite://fileOrDirPath [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| fileOrDirPath | Directory or file path to store data | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n```txt\nwproxy.org/docs reqWrite:///User/xxx/test/\n```\n##### Path Resolution Rules:\n1. **When Accessing a Specific File**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Save to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Directory Path**\n\n    `https://wproxy.org/docs/`\n\n    → Save to: `/User/xxx/test/index.html` (The target path is `/User/xxx/test/`, and a trailing `/` or `\\` automatically appends `index.html`)\n\n#### Explicit Directory Configuration\n```txt\nwproxy.org/docs/ reqWrite:///User/xxx/test/\n```\n##### Path Resolution Differences:\n1. **When Accessing a Sub-Path**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Still Saved to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Configuration Directory**\n\n    `https://wproxy.org/docs/`\n\n    → Saved Directly to: `/User/xxx/test/` (as a Whole File)\n\n> 💡 Key Differences:\n> - Whether the rule path ends with `/` or `\\` determines how directory requests are saved\n> - Non-directory paths (without a trailing `/` or `\\`) are saved directly to the specified file\n> - Directory paths (with a trailing `/` or `\\`) are automatically completed to `index.html`\n\n#### Specified Files\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ reqWrite:///User/xxx/test/index.html\n```\n> Limit the request URL using regular expression matching\n\n## Associated Protocols\n1. Enable force write: [enable://forceReqWrite](./enable)\n2. Write all request contents: [reqWriteRaw](./reqWriteRaw)\n"
  },
  {
    "path": "docs/en/docs/rules/reqWriteRaw.md",
    "content": "# reqWriteRaw\nSaves the complete request content (including request method, path, protocol, headers, and content) to a specified directory or file. This method is suitable for scenarios where request data needs to be recorded:\n- Automatically generates a file path based on the request URL\n- Uses a safe write policy (can be forced to overwrite using [enable://forceReqWrite](./enable))\n- Only valid for requests with a body (POST/PUT/PATCH, etc.)\n- Automatically skips requests without a body, such as GET/HEAD\n- Automatically skips if saving fails\n\n## Rule Syntax\n``` txt\npattern reqWriteRaw://fileOrDirPath [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| fileOrDirPath | Directory or file path to store data | |\n| filters | Optional filters, supporting matching: • Request URL/Method/Headers/Content • Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n```txt\nwproxy.org/docs reqWriteRaw:///User/xxx/test/\n```\n##### Path Resolution Rules:\n1. **When accessing a specific file**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Save to: `/User/xxx/test/test.html`\n\n2. **When accessing a directory path**\n\n    `https://wproxy.org/docs/`\n\n    → Save to: `/User/xxx/test/index.html` (The target path is `/User/xxx/test/`, and a trailing `/` or `\\` automatically appends `index.html`)\n\n#### Explicit Directory Configuration\n```txt\nwproxy.org/docs/ reqWriteRaw:///User/xxx/test\n```\n##### Path Resolution Differences:\n1. **When Accessing a Sub-Path**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Still Saved to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Configuration Directory**\n\n    `https://wproxy.org/docs/`\n\n    → Saved Directly to: `/User/xxx/test/` (as a Whole File)\n\n> 💡 Key Differences:\n> - Whether the rule path ends with `/` or `\\` determines how directory requests are saved\n> - Non-directory paths (without a trailing `/` or `\\`) are saved directly to the specified file\n> - Directory paths (with a trailing `/` or `\\`) are automatically completed to `index.html`\n\n#### Specified Files\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ reqWriteRaw:///User/xxx/test/index.html\n```\n> Limit the request URL using regular expression matching\n\n## Associated Protocols\n1. Enable force write: [enable://forceReqWrite](./enable)\n2. Write only the request body: [reqWrite](./reqWrite)\n"
  },
  {
    "path": "docs/en/docs/rules/resAppend.md",
    "content": "# resAppend\nInserts the specified content after the existing response body (only valid for status codes with a response body, such as `200`/`500`).\n> ⚠️ Note: Requests without a response body, such as 204 and 304, are not affected.\n\n## Rule Syntax\n``` txt\npattern resAppend://value [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Text or binary content. Supports the following types: <br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching: <br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Inline Method\n``` txt\nwww.example.com/path resAppend://(Hello) file://(-test-)\n```\nRequesting `https://www.example.com/path/to` will result in the response content becoming `-test-Hello`.\n\n#### Inline/Values Method\n```` txt\n``` body.txt\nHello world.\n```\nwww.example.com/path resAppend://{body.txt} file://(-test-)\n````\nRequesting `https://www.example.com/path/to` will result in the response content becoming `-test-Hello world.`.\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 resAppend:///User/xxx/test.txt\nwww.example.com/path2 resAppend://https://www.xxx.com/xxx/params.txt\n# Editing a Temporary File\nwww.example.com/path3 resAppend://temp/blank.txt\n````\n\n## Associated Protocols\n1. Append content to the response: [resPrepend](./resPrepend)\n2. Inject content before the response: [resBody](./resBody)\n3. Append content to the request: [reqPrepend](./reqPrepend)\n"
  },
  {
    "path": "docs/en/docs/rules/resBody.md",
    "content": "# resBody\n\nThe `resBody` protocol is used to replace the response body of a specified request. It only takes effect on status codes that include a response body (such as `200`, `500`, etc.).\n\n> **Effective Condition**: Only takes effect on status codes with a response body.\n> \n> ⚠️ Note: Requests without a response body, such as `204`, `304`, are not affected.\n\n## Rule Syntax\n\n`resBody` supports multiple ways to specify the response content to replace:\n\n### 1. Inline Value (Direct Specification)\nWrite the content to replace directly in the rule, suitable for short text content.\n\n```txt\npattern resBody://(value) [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/api/data resBody://({\"status\":\"modified\"})\n```\n\n### 2. Embedded Value (Using Code Block)\nThis method is recommended when dealing with complex content containing spaces, line breaks, or when you want to reuse a configuration.\n\n````txt\npattern resBody://{custom-key.json} [lineProps...] [filters...]\n\n``` custom-key.json\n{\n  \"status\": \"success\",\n  \"data\": {\n    \"message\": \"This response was modified by resBody\"\n  }\n}\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nWhen the content is large, you can store it in the `Values` configuration area.\n\n```txt\npattern resBody://{key-of-values.html} [lineProps...] [filters...]\n```\n\n**Prerequisite**: A key named `key-of-values.html` with the content to replace as its value must exist in `Values`.\n\n### 4. Loading from a File Path or Remote URL\nLoad the response content to replace from a local file or remote URL.\n\n```txt\n# Load from a local file\npattern resBody:///User/xxx/test.txt\n\n# Load from a remote URL\npattern resBody://https://www.example.com/override-content.txt\n```\n\n### 5. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern resBody://temp/blank.txt\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows).\n2. Click with the mouse on `resBody://temp/blank.txt`.\n3. Enter the replacement content in the pop-up editing dialog.\n4. Click `Save` to save.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | The response content to replace, supporting multiple formats:<br>• Local file path<br>• Remote URL<br>• Inline, embedded, Values references |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Examples\n```txt\n# Replace API response content\nwww.example.com/api/data resBody://({\"status\":\"custom\",\"data\":\"modifiedContent\"})\n```\n\n### Using Embedded Values\n````txt\n``` error-response\n{\n  \"error\": {\n    \"code\": 1001,\n    \"message\": \"Custom error message for testing\"\n  }\n}\n```\n\nwww.example.com/api resBody://{error-response} includeFilter://s:500\n````\n\n### Loading from a File\n```txt\n# Load replacement content from a local file\nwww.example.com/api/config resBody:///Users/username/mock/config.json\n\n# Load replacement content from a remote URL\nwww.example.com/api/data resBody://https://raw.githubusercontent.com/user/repo/main/mock-data.json\n```\n\n### Using Temporary Files\n```txt\nwww.example.com/api/user resBody://temp/blank.json\n```\n> In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows) and click on `resBody://temp/blank.json` with the mouse to edit.\n\n### Using with Filters\n```txt\n# Replace only responses with a specific status code\nwww.example.com/api resBody://({\"error\":\"ServiceUnavailable\"}) includeFilter://s:503\n\n# Determine replacement content based on request method\nwww.example.com/api/users resBody://({\"method\":\"GEToverride\"}) includeFilter://m:GET\nwww.example.com/api/users resBody://({\"method\":\"POSToverride\"}) includeFilter://m:POST\n```\n\n## Advanced Usage\n\n### Dynamic Content Replacement\nUse regular expressions for dynamic content construction:\n```txt\n# Replace the timestamp in the original response with the current time\nwww.example.com/api/time resBody://`({\"timestamp\":${now}})`\n```\n\n### Environment-Specific Replacement\n```txt\n# Development environment: Use mock data\ndev-api.example.com resBody://{\"env\":\"development\",\"data\":\"mock\"}\n```\n\n### Error Scenario Testing\n```txt\n# Simulate various error responses\nwww.example.com/api resBody://{\"error\":\"RateLimitExceeded\"} includeFilter://s:429\nwww.example.com/api resBody://{\"error\":\"InternalServerError\"} includeFilter://s:500\nwww.example.com/api resBody://{\"error\":\"ServiceUnavailable\"} includeFilter://s:503\n```\n\n## Differences from the file Protocol\n\nThe main distinction between the `resBody` protocol and the [`file`](./file) protocol lies in the request processing flow:\n\n### Processing Flow Comparison\n- **`resBody` protocol**: The request **is first sent to the backend server** to obtain the original response, then **replaces** the original response body with the specified content.\n- **`file` protocol**: The request **is not sent to the backend server**; it directly returns the specified file content.\n\n### Use Case Comparison\n- **`resBody`**: Suitable for modifying the return content of real APIs while maintaining the complete request-response flow.\n- **`file`**: Suitable for complete local simulation, independent of real backend services.\n\n## Notes\n\n### Response Type Preservation\n- `resBody` replaces the response body content and does not automatically modify the `Content-Type` response header.\n- If you need to modify the response type, use it in conjunction with the [`resType`](./resType) protocol.\n\n## Common Questions\n\n### Q: The resBody rule is not taking effect.\n**A:** Check:\n1. Whether the response status code has a response body (excluding 204, 304, etc.).\n2. Whether the rule pattern correctly matches the request URL.\n3. Whether the filter conditions are met.\n4. Whether a higher-priority rule is overriding it.\n\n### Q: Can I replace only specific types of responses?\n**A:** Yes, use filters:\n```txt\n# Replace only JSON responses\npattern resBody://content includeFilter://resH:content-type=json\n\n# Replace only responses from specific paths\npattern resBody://content includeFilter://*/api/v1/\n```\n\n## Related Protocols\n\n1. **Inject content before the response body**: [resPrepend](./resPrepend)\n   - Inserts content before the original response body.\n2. **Append content after the response body**: [resAppend](./resAppend)\n   - Appends content after the original response body.\n3. **Replace request content**: [reqBody](./reqBody)\n   - Replaces the request body content sent to the server.\n4. **Directly return local file content**: [file](./file)\n   - Does not request the server; directly returns local file content.\n5. **Modify response headers**: [resHeaders](./resHeaders)\n   - Modifies response header information, such as `Content-Type`.\n\n## Further Reading\n\n- [Matching Pattern Documentation](./pattern): Learn more about URL matching rules.\n- [Operation Commands Documentation](./operation): Learn about multiple ways to load content.\n- [Filters Documentation](./filters): Learn more about filter functionalities.\n"
  },
  {
    "path": "docs/en/docs/rules/resCharset.md",
    "content": "# resCharset\nModify the `charset` portion of the `content-type` response header to set the encoding of the response content.\n> `content-type` structure:\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## Rule Syntax\n``` txt\npattern resCharset://encoding [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| encoding | Encoding name, such as `utf8` <br/>• Inline/Embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Set response encoding and response header `content-type` Empty fields are changed to `; charset=utf8`\nwww.example.com/path resCharset://utf8\n\n# Set the response encoding. The empty `content-type` response header field is changed to `text/html; charset=utf8`\nwww.example.com/path resType://json resCharset://utf8\n```\n\n## Associated Protocols\n1. Directly modify the response header: [resHeaders://content-type=xxx](./reqHeaders)\n2. Modify the response content encoding: [resType://media-type](./resCharset)\n"
  },
  {
    "path": "docs/en/docs/rules/resCookies.md",
    "content": "#resCookies\nModify response `Cookies`.\n\n## Rule Syntax\n``` txt\npattern resCookies://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Cookie object, supported from the following channels:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\nCookie Object Structure\n``` js\n{\n  \"key1\": \"value1\",\n  \"key2\": \"value2\",\n  \"keyN\": {\n          \"value\": \"value1\",\n          \"maxAge\": 60,\n          \"httpOnly\": true,\n          \"path\": \"/\",\n          \"secure\": true,\n          \"domain\": \".example.com\",\n          \"sameSite\": 'None',\n          \"Partitioned\": false\n      }\n}\n```\n## Configuration Example\n\n#### Inline Mode\n\n```` txt\nwww.example.com/path resCookies://k1=v1&k2=v2;path=/&k3=v3;path=/;secure;samesite=none\n```` \n\nAdds two cookies to the response header: `k1=v1`/`k2=v2; path=/`/`k3=v3; path=/; secure; samesite=none`\n\n### Embedded Mode\n\n```` txt\n``` cookies.json\nkey1: value1\nkey2: value2\n```\n# Or\n``` cookies.json\n{\n  key1: 'value1',\n  key2: {\n    value: 'value2',\n    path: '/',\n    secure: true,\n    domain: 'example.com'\n  }\n}\n``` \nwww.example.com/path resCookies://{cookies.json}\n\n````\n\nAdd two response cookies to the response header: `key1=value1`/`key2=value2; path=/; secure; domain=example.com`\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 resCookies:///User/xxx/test.json\nwww.example.com/path2 resCookies://https://www.xxx.com/xxx/params.json\n\n# By editing the temporary file\nwww.example.com/path3 resCookies://temp/blank.json\n\n````\n\n## Global Replacement\n\nTo add `SameSite=Nonoe; Secure` to all (or some) response cookies, you can use [headerReplace](./headerReplace)\n\n> Assuming each response cookie has `path=/;`\n\n```` txt\n``` test.json\nresH.set-cookie:path=/;: SameSite=None; Secure;\n``` \nwww.example.com/path headerReplace://{test.json} resCookies://test=123;path=/;\n\n````\n\n## Association Protocol\n\n1. Delete response cookie: [delete://resCookies.xxx](./delete)\n2. Delete all response header cookies: [delete://resHeaders.set-cookie](./delete)\n3. Replace response header cookies: [headerReplace://resH.set-cookie:pattern=replacement](./headerReplace)\n\n"
  },
  {
    "path": "docs/en/docs/rules/resCors.md",
    "content": "# resCors\nSets Cross-Origin Resource Sharing (CORS) headers during the response phase to resolve cross-origin request issues.\n\n## Rule Syntax\n``` txt\npattern resCors://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | CORS object, supported from the following sources:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching against:<br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\nCORS Object Structure:\n``` text\norigin: *\nmethods: POST\nheaders: x-test\ncredentials: true\nmaxAge: 300000\n```\n\nCorresponding response headers:\n\n``` txt\naccess-control-allow-origin: *\naccess-control-allow-methods: POST\naccess-control-allow-headers: x-test\naccess-control-allow-credentials: true\naccess-control-max-age: 300000\n```\n> When the request method is `OPTIONS`, `access-control-allow-headers` -> `access-control-expose-headers`\n\n## Configuration Example\n#### Quick Mode\n1. Set the response header `access-control-allow-origin: *` (does not support cookies)\n``` txt\nwww.example.com/path resCors://*\n```\n2. Allow cookies\n``` txt\n# `enable` sets `access-control-allow-origin: http://reqOrigin` based on the request header `origin`\n# and settings access-control-allow-credentials: true\nwww.example.com/path2 resCors://enable\n```\n\n#### Detailed Configuration Mode\n```` txt\n``` cors.json\norigin: *\nmethods: POST\nheaders: x-test\ncredentials: true\nmaxAge: 300000\n```\nwww.example.com/path resCors://{cors.json}\n\n# Do not process OPTIONS requests\nwww.example.com/path2 resCors://{cors.json} excludeFilter://options\n````\nSet response headers:\n``` txt\naccess-control-allow-origin: *\naccess-control-allow-methods: POST\naccess-control-allow-headers: x-test\naccess-control-allow-credentials: true\naccess-control-max-age: 300000\n```\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 resCors:///User/xxx/test.json\nwww.example.com/path2 resCors://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 resCors://temp/blank.json\n````\n\n## Associated Protocols\n1. Delete response headers: [delete://resHeaders.orogin](./delete)\n2. Set the request cross: [resCors](./resCors)\n"
  },
  {
    "path": "docs/en/docs/rules/resDelay.md",
    "content": "#resDelay\nSets the delay time (in milliseconds) after the server completes processing.\n\n## Rule Syntax\n``` txt\npattern resDelay://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Time value (unit: milliseconds)<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Delay 3000 milliseconds (i.e., 3 seconds) before responding to the client after the backend returns\nwww.example.com/path resDelay://3000\n\n# Delay 5000 seconds (i.e., 5 seconds) after the backend returns Abort the request after 5 seconds.\nwww.example.com/path2 resDelay://5000 enable://abortRes\n```\n"
  },
  {
    "path": "docs/en/docs/rules/resHeaders.md",
    "content": "# resHeaders\nDynamically modify response header information, supporting multiple data sources and batch operations.\n\n## Rule Syntax\n``` txt\npattern resHeaders://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following channels:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n``` txt\nwww.example.com/path resHeaders://x-proxy=Whistle\n```\nAccess `https://www.example.com/path/to` adds a new response header:\n``` txt\nx-proxy: Whistle\n```\n\n#### Setting Multiple Response Headers\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 resHeaders://{test.json}\n\n# Equivalent to: www.example.com/path2 resHeaders://x-test1=1&x-test2=&x-test3=abc\n````\nVisiting `https://www.example.com/path2/to` on Whistle Network or the backend server will show the new response header:\n``` txt\nx-test1: 1\nx-test2:\nx-test3: abc\n```\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 resHeaders:///User/xxx/test.json\nwww.example.com/path2 resHeaders://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 resHeaders://temp/blank.json\n````\n\n## Related Protocols\n1. More flexible way to modify response headers: [headerReplace](./headerReplace)\n2. Deleting response header fields: [delete://resHeaders.xxx](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/resMerge.md",
    "content": "# resMerge\nIntelligently merges specified data objects into the response body, suitable for modifying partial parameters without affecting the remaining content. Supports the following response types:\n- JSON (response `content-type` contains the keyword `json`)\n- JSONP (response `content-type` is null or contains the keywords `html`/`javascript`)\n\n## Rule Syntax\n``` txt\npattern resMerge://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Operation data object. Supports retrieval from the following sources:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching against:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Inline Method\n``` txt\nwww.example.com/path resMerge://test=123 file://({\"name\":\"avenwu\"})\n```\nVisiting `https://www.example.com/path/to`, the browser receives the following:\n``` js\n{\"name\":\"avenwu\",\"test\":\"123\"}\n```\n\n#### Inline Method\n```` txt\n``` resMerge.json\na.b.c: 123\nc\\.d\\.e: abc\n```\nwww.example.com/path resMerge://{resMerge.json} file://({\"name\":\"avenwu\"})\n````\nVisiting `https://www.example.com/path/to` Content received by the browser:\n``` js\n{\"name\":\"avenwu\",\"a\":{\"b\":{\"c\":123}},\"c.d.e\":\"abc\"}\n```\n\n#### Local/Remote Resources\n```` txt\nwww.example.com/path1 resMerge:///User/xxx/test.json\nwww.example.com/path2 resMerge://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 resMerge://temp/blank.json\n````\n\n## Note: Response Size Limit\n\nTo ensure processing performance, `resMerge` enforces a default size limit for response content.\n\n*   **Limit Specification**: The automatic merge processing is only applied to responses with a body size **less than 2MB**. Responses exceeding this size will be skipped.\n*   **How to Override**: If you need to handle larger responses (e.g., for downloading files or processing large data interfaces), you can explicitly enable this capability by adding the following directive to your matching rule:\n\n``` txt\npattern enable://resMergeBigData\n\n# or\nwww.example.com/path1 resMerge:///User/xxx/test.json lineProps://enableBigData\n```\nOnce enabled, `resMerge` will attempt to process larger response volumes. Please note that this may increase memory consumption and processing time.\n\n## Association Protocol\n1. Replace with a keyword or regular expression: [resReplace](./reqReplace)\n2. Modify the request content object: [reqMerge](./resMerge)\n"
  },
  {
    "path": "docs/en/docs/rules/resPrepend.md",
    "content": "# resPrepend\n\nThe `resPrepend` protocol is used to insert specified content at the beginning of an existing response body. It only takes effect on status codes that include a response body (such as `200`, `500`, etc.). Through the `resPrepend` protocol, you can:\n- Add custom content at the beginning of a response.\n- Insert debugging information or timestamps.\n- Add references to scripts, stylesheets, or other resources.\n- Inject environment markers or version information.\n- Keep the original response content intact, adding extra content only at the beginning.\n\n> **Effective Condition**: Only takes effect on status codes with a response body.\n> \n> ⚠️ Note: Requests without a response body, such as `204`, `304`, are not affected.\n\n## Rule Syntax\n\n`resPrepend` supports multiple ways to specify the content to be inserted:\n\n### 1. Inline Value (Direct Specification)\nWrite the content to insert directly in the rule, suitable for short text (cannot contain spaces or line breaks).\n\n```txt\npattern resPrepend://(value) [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/page resPrepend://(<!--Page Start-->)\n```\n\n### 2. Embedded Value (Using Code Block)\nThis method is recommended when dealing with complex content containing spaces, line breaks, or when you want to reuse a configuration.\n\n````txt\npattern resPrepend://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n<!-- Debug Info -->\n<script>\n  console.log('Page Load Time:', new Date().toISOString());\n</script>\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nWhen the content is large, you can store it in the `Values` configuration area.\n\n```txt\npattern resPrepend://{key-of-values} [lineProps...] [filters...]\n```\n\n**Prerequisite**: A key named `key-of-values` with the content to insert as its value must exist in `Values`.\n\n### 4. Loading from a File Path or Remote URL\nLoad the response content to insert from a local file or remote URL.\n\n```txt\n# Load from a local file\npattern resPrepend:///User/xxx/header.html\n\n# Load from a remote URL\npattern resPrepend://https://cdn.example.com/analytics-script.js\n```\n\n### 5. Loading from a Temporary File\nUse Whistle's temporary file feature when content needs frequent editing.\n\n```txt\npattern resPrepend://temp/blank.txt\n```\n\n**Steps:**\n1. In the Rules editor, hold down `Command` (Mac) / `Ctrl` (Windows).\n2. Click with the mouse on `resPrepend://temp/blank.txt`.\n3. Enter the content to insert in the pop-up editing dialog.\n4. Click `Save` to save.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **value** | Yes | The response content to insert, supporting multiple formats:<br>• Local file path<br>• Remote URL<br>• Inline, embedded, Values references |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Examples\n```txt\n# Add a comment at the beginning of an HTML page\nwww.example.com/index.html resPrepend://(<!--Page Start Time:-->)\n\n# Add a timestamp before a JSON response\napi.example.com/data.json resPrepend://({\"timestamp\":\"2024-01-01T00:00:00Z\"})\n```\n\n### Example Scenario Analysis\n\n#### Scenario 1: Inserting Debug Information\n````txt\n``` debug-header\n<!-- \n  Debug Info:\n  - URL: ${url}\n  - Time: ${now}\n  - User Agent: ${reqHeaders.user-agent}\n-->\n```\n\nwww.example.com resPrepend://`{debug-header}`\n````\n\n#### Scenario 2: Adding Page Header Content\n```` txt\n# Add a common header\n^www.example.com/*.html resPrepend://{custom-html}\n\n``` custom-html\n<div class=\"site-header\">Website Header</div>\n```\n````\n\n#### Scenario 3: Injecting Analytics Script\n```` txt\n# Add Google Analytics script\nwww.example.com resPrepend://{google-analytics}\n``` google-analytics\n<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-XXXXX\"></script>\n<script>\n  window.dataLayer = window.dataLayer || [];\n  function gtag(){dataLayer.push(arguments);}\n  gtag('js', new Date());\n  gtag('config', 'UA-XXXXX');\n</script>\n```\n````\n\n### Using with Other Protocols\n```txt\n# First add header content, then add footer content\nwww.example.com/page resPrepend://(<header>) resAppend://(</footer>)\n\n# Use with the file protocol\nwww.example.com/path resPrepend://(Hello) file://(-test-)\n```\n**Response Result:**\n```\nHello-test-\n```\n\n### Using Embedded Values\n````txt\n``` body.txt\nHello world.\n```\n\nwww.example.com/path resPrepend://{body.txt} file://(-test-)\n````\n**Response Result:**\n```\nHello world.-test-\n```\n\n### Loading from a File or Remote URL\n```txt\n# Load from a local file\nwww.example.com/path1 resPrepend:///User/xxx/test.txt\n\n# Load from a remote URL\nwww.example.com/path2 resPrepend://https://www.xxx.com/xxx/params.txt\n\n# Use a temporary file\nwww.example.com/path3 resPrepend://temp/blank.txt\n```\n\n### Using with Filters\n```` txt\n# Add debug info only for development environment\nwww.example.com resPrepend://(<!--Development Environment-->) includeFilter://reqH:host=/dev\\./\n\n# Add header only for HTML pages\nwww.example.com resPrepend://{header} resType://html\n\n``` header\n<div class=\"header\" />\n```\n\n# Determine whether to add content based on response status code\nwww.example.com/api resPrepend://({\"debug\":true}) includeFilter://s:200\n````\n\n## Advanced Usage\n\n### Dynamic Content Insertion\nUse template strings to implement dynamic content:\n```` txt\n# Insert current timestamp\nwww.example.com/api resPrepend://`({\"timestamp\":\"${now}\"})`\n\n# Insert request information\nwww.example.com/debug resPrepend://`{req-info}`\n\n``` req-info\n<!-- Request Method: ${method}, Path: ${path} -->\n```\n````\n\n### Combining Multiple Insertions\n```` txt\n# Insert multiple content fragments\nwww.example.com/page resPrepend://{header1} resPrepend://{header2}\n\n``` header1\n<div />\n```\n``` header2\n<div id=\"header2\" />\n```\n# Final result: <div id=\"header2\" /><div id=\"header1\" />[Original Content]\n````\n\n### Conditional Insertion\n`````txt\n# Insert content only under specific conditions\nwww.example.com/admin resPrepend://{log.js} includeFilter://reqH:cookie=/admin=true/\n\n```` log.js\n<script>console.log('Admin Page')</script>\n````\n`````\n\n## Common Questions\n\n### Q: The resPrepend rule is not taking effect.\n**A:** Check:\n1. Whether the response status code has a response body (excluding 204, 304, etc.).\n2. Whether the rule pattern correctly matches the request URL.\n3. Whether the filter conditions are met.\n4. Whether other rules are interfering.\n\n### Q: How to ensure content is inserted at a specific position?\n**A:**\n- `resPrepend` always inserts content at the very beginning of the response.\n- If you need to insert at other positions, consider using `resReplace` with regular expression replacement.\n\n### Q: Can I delete parts of the original response?\n**A:** No. `resPrepend` only adds content; it does not delete or modify the original content. If you need to delete content, use the `resBody` protocol.\n\n## Differences from resBody\n\nThe main distinction between the `resPrepend` protocol and the [`resBody`](./resBody) protocol lies in the processing method:\n- **`resPrepend`**: Inserts specified content **before** the original response content, preserving the original.\n- **`resBody`**: **Replaces** the entire response content, without preserving the original.\n\n## Related Protocols\n\n1. **Replace response content**: [resBody](./resBody)\n   - Completely replaces the response content, without preserving the original.\n2. **Append content after the response body**: [resAppend](./resAppend)\n   - Adds content at the end of the response.\n3. **Replace request content**: [reqBody](./reqBody)\n   - Replaces the request body content sent to the server.\n4. **Insert content before the request body**: [reqPrepend](./reqPrepend)\n   - Inserts content before the request body.\n5. **Append content after the request body**: [reqAppend](./reqAppend)\n   - Adds content at the end of the request body.\n\n## Further Reading\n\n- [Matching Pattern Documentation](./pattern): Learn more about URL matching rules.\n- [Operation Commands Documentation](./operation): Learn about multiple ways to load content.\n- [Filters Documentation](./filters): Learn more about filter functionalities.\n"
  },
  {
    "path": "docs/en/docs/rules/resReplace.md",
    "content": "# resReplace\nReplaces the response body content using a method similar to JavaScript's `String.replace()` (only valid for requests with a response body, such as `200` and `500`). Supports multiple text formats:\n- JSON (`application/json`)\n- XML (`application/xml`)\n- HTML (`text/html`)\n- Plain text (`text/xxx`)\n\n## Rule Syntax\n``` txt\npattern resReplace://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Replacement configuration object, supported from the following sources: <br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching: • Request URL/Method/Header/Content • Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Inline Mode\n```` txt\nwww.example.com/path file://(00user-11test-22user-33test) resReplace://user=abc&/\\d+/g=number\n````\nRequest `https://www.example.com/path/to`, browser receives:\n``` txt\nnumberabc-numbertest-numberabc-numbertest\n```\n\n### Inline Mode\n```` txt\n``` resReplace.json\nuser: name\n/\\d+/g: num\n```\n# or (note the escape character)\n``` resReplace.json\n{\n  'user': 'name',\n  '/\\\\d+/g': 'num'\n}\n```\nwww.example.com/path file://(00user-11test-22user-33test) resReplace://{resReplace.json}\n````\nRequest `https://www.example.com/path/to` Content received by the browser:\n``` txt\nnumname-numtest-numname-numtest\n```\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 resReplace:///User/xxx/test.json\nwww.example.com/path2 resReplace://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 resReplace://temp/blank.json\n````\n\n## Relationship Protocol\n1. Object Merge: [resMerge](./resMerge)\n2. Completely replace: [resBody](./resBody)\n"
  },
  {
    "path": "docs/en/docs/rules/resRules.md",
    "content": "# resRules\nBatch set multiple rules for matching requests during the response phase to meet request processing requirements in complex scenarios.\n\n## Rule Syntax\n``` txt\npattern resRules://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Rule content, supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n``` test.txt\n* file://(<div>hello<div>)\n* resType://text\n```\n\n``` test2.txt\n* resAppend://(test)\n```\n\nwww.example.com/path resRules://{test.txt} resRules://{test2.txt}\n````\nAccessing `https://www.example.com/path/to` does not execute `* file://(<div>hello<div>)` (because the rules in resRules are executed during the response phase).\n\n## Related Protocols\n1. Request-Phone Script Rules: [reqScript](./reqScript)\n2. Response-Phone Batch Rules: [reqRules](./resRules)\n3. Response-Phone Script Rules: [resScript](./resScript)\n4. More Complex Customization Requirements: [Plugin Development](../extensions/dev)\n"
  },
  {
    "path": "docs/en/docs/rules/resScript.md",
    "content": "# resScript\nDynamically generate rules during the response phase using JavaScript to implement complex request processing logic. The script can access request context information and dynamically generate matching rules.\n\n## Rule Syntax\n``` txt\npattern resScript://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | JS script to generate rules. Supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values Content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n``` test.js\nif (method === 'GET') {\n    rules.push('* resType://text');\n    rules.push('* file://(<div>GET-Request</div>)');\n} else {\n    rules.push('* statusCode://403');\n}\n```\nwww.example.com/path resScript://{test.js}\n````\nAccessing `https://www.example.com/path/to` does not execute `* file://(<div>hello<div>)` (because the rules generated by resScript are executed during the response phase).\n\n#### Available Global Variables\n\n| Variable/Method | Description |\n|--------------------|-------------------------------------------------------|\n| `url` | Full request URL |\n| `method` | Request method (GET/POST, etc.) |\n| `ip`/`clientIp` | Client IP address |\n| `headers` | Request header object |\n| `body` | Request body (max 16KB) |\n| `rules` | Rule array, add new rules via push |\n| `values` | Temporary value storage object |\n| `render(tpl,data)` | Micro template rendering function |\n| `getValue(key)` | Get the value in `Values` |\n| `parseUrl` | Same as `url.parse` in Node.js |\n| `parseQuery` | Same as `querystring.parse` in Node.js |\n\n## Related Protocols\n1. Request-phase script rule: [reqScript](./reqScript)\n2. Request-phase batch rules: [resRules](./resScript)\n3. Response-phase batch rules: [resRules](./resRules)\n4. More complex customization requirements: [Plugin development](../extensions/dev)\n"
  },
  {
    "path": "docs/en/docs/rules/resSpeed.md",
    "content": "# resSpeed\nSets the response body transmission speed (unit: kb/s, kilobits per second).\n\n## Rule Syntax\n``` txt\npattern resSpeed://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Transmission speed (unit: kb/s, kilobits per second)<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Limit the response content transmission speed to 3kb/s\nwww.example.com/path resSpeed://3\n```\nThis rule is ineffective for requests without a request body, such as those with methods like `GET`, `HEAD`, `CONNECT` (TUNNEL request), and `UPGRADE` (WebSocket request).\n"
  },
  {
    "path": "docs/en/docs/rules/resType.md",
    "content": "#resType\nModify the `media-type` and `charset` components of the `content-type` response header.\n> `content-type` structure:\n> ``` txt\n> <media-type>; charset=<encoding>\n> ```\n\n## Rule Syntax\n``` txt\npattern resType://type[;charset] [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| type[;charset] | `type` Response type, `charset` encoding <br/>• Inline/embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching: <br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n`resType` is mainly used to modify the `media-type` response type. The `charset` part is optional. If the `charset` part is not set, the original `charset` part of the response header (if present) will be retained.\n\n## Configuration Example\n\n#### Shortcut Commands (Strings without `/`)\n> Automatically convert based on [mime](https://github.com/broofa/mime).lookup(type)\n``` txt\n# The `content-type` response header without encoding becomes `text/html`\nwww.example.com/path resType://html\n\n# The `content-type` response header with encoding becomes `application/json;charset=utf8`\nwww.example.com/path resType://json;charset=utf8\n```\n\n#### Full Type\n``` txt\n# The `content-type` response header becomes `text/plain`\nwww.example.com/path resType://text/plain\n\n# The `content-type` response header becomes `text/plain;charset=utf8`\nwww.example.com/path resType://text/plain;charset=utf8\n```\n\n## Associated Protocols\n1. Modify the response headers directly: [resHeaders://content-type=xxx](./reqHeaders)\n2. Modify the response content encoding: [resCharset://encoding](./reqCharset)\n"
  },
  {
    "path": "docs/en/docs/rules/resWrite.md",
    "content": "# resWrite\nSaves the response body to a specified directory or file. Suitable for scenarios where response data needs to be recorded:\n- Automatically generates a file path based on the request URL\n- Uses a safe write policy (can be forced to overwrite using [enable://forceReqWrite](./enable))\n- Valid only for requests with a response body (POST/PUT/PATCH, etc.)\n- Automatically skips requests without a response body, such as GET/HEAD\n- Automatically skips if saving fails\n\n## Rule Syntax\n``` txt\npattern resWrite://fileOrDirPath [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| fileOrDirPath | Directory or file path to store data | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n```txt\nwproxy.org/docs resWrite:///User/xxx/test/\n```\n##### Path Resolution Rules:\n1. **When Accessing a Specific File**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Save to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Directory Path**\n\n    `https://wproxy.org/docs/`\n\n    → Save to: `/User/xxx/test/index.html` (The target path is `/User/xxx/test/`, and a trailing `/` or `\\` automatically appends `index.html`)\n\n#### Explicit Directory Configuration\n```txt\nwproxy.org/docs/ resWrite:///User/xxx/test/\n```\n##### Path Resolution Differences:\n1. **When Accessing a Sub-Path**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Still Saved to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Configuration Directory**\n\n    `https://wproxy.org/docs/`\n\n    → Saved Directly to: `/User/xxx/test/` (as a Whole File)\n\n> 💡 Key Differences:\n> - Whether the rule path ends with `/` or `\\` determines how directory requests are saved\n> - Non-directory paths (without a trailing `/` or `\\`) are saved directly to the specified file\n> - Directory paths (with a trailing `/` or `\\`) are automatically completed to `index.html`\n\n#### Specified Files\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ resWrite:///User/xxx/test/index.html\n```\n> Limit the request URL using regular expression matching\n\n## Associated Protocols\n1. Enable force write: [enable://forceReqWrite](./enable)\n2. Write all response content: [resWriteRaw](./resWriteRaw)\n"
  },
  {
    "path": "docs/en/docs/rules/resWriteRaw.md",
    "content": "# resWriteRaw\nSaves the complete response content (including protocol, status code, status information, response headers, and content) to a specified directory or file. This method is suitable for scenarios where response data needs to be recorded:\n- Automatically generates a file path based on the request URL\n- Uses a safe write policy (can be forced to overwrite using [enable://forceReqWrite](./enable))\n- Only valid for requests with a response body (POST/PUT/PATCH, etc.)\n- Automatically skips requests without a response body, such as GET/HEAD\n- Automatically skips if saving fails\n\n## Rule Syntax\n``` txt\npattern resWriteRaw://fileOrDirPath [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| fileOrDirPath | Directory or file path to store data | |\n| filters | Optional filters, supporting matching: • Request URL/Method/Headers/Content • Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n\n#### Basic Configuration\n```txt\nwproxy.org/docs resWriteRaw:///User/xxx/test/\n```\n##### Path Resolution Rules:\n1. **When accessing a specific file**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Save to: `/User/xxx/test/test.html`\n\n2. **When accessing a directory path**\n\n    `https://wproxy.org/docs/`\n\n    → Save to: `/User/xxx/test/index.html` (The target path is `/User/xxx/test/`, and a trailing `/` or `\\` automatically appends `index.html`)\n\n#### Explicit Directory Configuration\n```txt\nwproxy.org/docs/resWriteRaw:///User/xxx/test/\n```\n##### Path Resolution Differences:\n1. **When Accessing a Sub-Path**\n\n    `https://wproxy.org/docs/test.html`\n\n    → Still Saved to: `/User/xxx/test/test.html`\n\n2. **When Accessing a Configuration Directory**\n\n    `https://wproxy.org/docs/`\n\n    → Saved Directly to: `/User/xxx/test/` (as a Whole File)\n\n> 💡 Key Differences:\n> - Whether the rule path ends with `/` or `\\` determines how directory requests are saved\n> - Non-directory paths (without a trailing `/` or `\\`) are saved directly to the specified file\n> - Directory paths (with a trailing `/` or `\\`) are automatically completed to `index.html`\n\n#### Specified Files\n``` txt\n/^https://wproxy\\.org/docs/(\\?.*)?$ resWriteRaw:///User/xxx/test/index.html\n```\n> Limit the request URL using regular expression matching\n\n## Associated Protocols\n1. Enable force write: [enable://forceReqWrite](./enable)\n2. Write only the response body: [resWrite](./resWrite)\n"
  },
  {
    "path": "docs/en/docs/rules/responseFor.md",
    "content": "# responseFor\nCustomize the `ServerIP` displayed on the Whistle Network dashboard by setting the `x-whistle-response-for` response header field.\n> Equivalent to: [resHeaders://x-whistle-response-for=xxx](./resHeaders)\n\n# Rule Syntax\n``` txt\npattern responseFor://ip [filters...]\n```\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| ip | Custom client IP<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.example.com/path responseFor://1.1.1.1,2.2.2.2\n```\n<img src=\"/img/response-for.png\" width=\"800\" />\n\n## Related Protocols\n1. Custom Response Headers: [resHeaders://x-whistle-response-for=xxx](./resHeaders)\n"
  },
  {
    "path": "docs/en/docs/rules/rule.md",
    "content": "# Rule Syntax\n\nWhistle modifies requests and responses through concise rule configurations, with each rule following the same basic syntax structure.\n\n## Syntax Structure\n\n```txt\npattern operation [lineProps...] [filters...]\n```\n\nEach rule consists of the following four components:\n\n| Component | Required | Description |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression for matching request URLs. Detailed documentation: [pattern](./pattern) |\n| **operation** | Yes | Operation command, formatted as `protocol://value`. Detailed documentation: [operation](./operation) |\n| **lineProps** | No | Additional configurations that apply only to the current rule. Detailed documentation: [lineProps](./lineProps) |\n| **filters** | No | Filter conditions for precise control over when the rule takes effect. Detailed documentation: [filters](./filters) |\n\n## Advanced Rule Configuration\n\n### 1. Combined Configuration\nA single rule can contain multiple operation commands, enabling complex functional combinations.\n\n**Syntax**:\n```txt\npattern operation1 operation2 ... [includeFilter://pattern1 ... excludeFilter://patternN ...]\n```\n\n**Example**:\n```txt\nwww.example.com/* file:///static-files cache://3600 resCors://*\n```\n\n**Explanation**:\n- Multiple operation commands are executed in order.\n- Supports combining any number of operation commands.\n- Filter conditions apply to the entire rule.\n\n### 2. Position Swapping\nWhen `operation` and the first `pattern` are not both in URL or domain format, their positions can be swapped.\n\n**Syntax**:\n```txt\noperation pattern1 pattern2 ... [includeFilter://pattern1 ... excludeFilter://patternN ...]\n```\n\n**Example**:\n```txt\n# Standard writing\nwww.example.com proxy://127.0.0.1:8080\n\n# Position-swapped writing\nproxy://127.0.0.1:8080 www.example.com api.example.com\n```\n\n> **Limitations**: `operation` and the first `pattern` cannot simultaneously be in the following formats:\n> - `https://test.com/path`\n> - `//test.com/path`\n> - `test.com/path`\n> - `test.com`\n\n**Applicable Scenarios**:\n- More concise when applying the same operation to multiple domains.\n- Batch configuration of proxy, redirect, and other rules.\n\n### 3. Multi-line Configuration\nUse code blocks to achieve multi-line configurations, improving readability of complex rules.\n\n**Syntax**:\n````txt\nline`\noperation\npattern1\npattern2\n...\n[includeFilter://pattern1\n...\nexcludeFilter://patternN \n...]\n`\n````\n\n**Example**:\n````txt\nline`\nproxy://127.0.0.1:8080\nwww.example.com\napi.example.com\nstatic.example.com\nincludeFilter://m:GET\nexcludeFilter:///admin/\n`\n````\n\n**Features**:\n- Whistle automatically replaces line breaks within the code block with spaces.\n- Maintains clean code formatting for easy reading and maintenance.\n- Suitable for scenarios involving multiple matching patterns and complex filter conditions.\n\n**Equivalent Conversion**:\nThe above multi-line configuration is equivalent to:\n```txt\nproxy://127.0.0.1:8080 www.example.com api.example.com static.example.com includeFilter://m:GET excludeFilter:///admin/\n```\n\n## Notes\n\n### 1. Rule Priority\n- Rules are executed in top-to-bottom order.\n- Later rules may override the effects of earlier rules.\n- Use `lineProps://important` to increase the priority of important rules.\n\n### 2. Debugging Tips\n1. **Step-by-step verification**: Start with simple rules and gradually add complex conditions.\n2. **Log viewing**: Use the Overview panel in the Whistle Network interface to check rule matching.\n3. **Browser debugging**: Use browser developer tools to inspect actual effects.\n4. **Temporary disabling**: Use `#` to comment out rules temporarily for testing.\n"
  },
  {
    "path": "docs/en/docs/rules/skip.md",
    "content": "# skip\nSkip the specified `pattern` or `operation` rule and continue matching the following rules.\n\n## Syntax Rules\n``` txt\npattern skip://pattern=patternString skip://operation=operationString [filters...]\n```\n> Multiple `skip` options can be configured simultaneously.\n\n| Parameter | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | Regular expressions to ignore: <br/>• pattern=patternString<br/>• operation=operationString<br/> ⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching: <br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.example.com/path file:///User/xxx/index1.html\nwww.example.com/path file:///User/xxx/index2.html\n\nwww.example.com/path2/test file:///User/xxx/test1.html\nwww.example.com/path2 file://</User/xxx/test2.html>\n\nwww.example.com/path skip://operation=file:///User/xxx/index1.html\nwww.example.com/path2/test skip://pattern=www.example.com/path2/test\n```\n\n- Accessing `https://www.example.com/path` returns the content of `/User/xxx/index2.html`\n    > Without the `www.example.com/path skip://operation=file:///User/xxx/index1.html` rule, the content of `/User/xxx/index1.html` would be returned.\n- Accessing `https://www.example.com/path2/test` returns the content of `/User/xxx/test2.html`\n    > Without The `www.example.com/path2/test skip://pattern=www.example.com/path2/test` rule will return the content of `/User/xxx/test1.html`.\n\nSimilar protocol: [ignore](./ignore)\n"
  },
  {
    "path": "docs/en/docs/rules/sniCallback.md",
    "content": "# sniCallback\nDynamically customize the TLS certificate used in HTTPS requests through a plugin mechanism.\n\n## Rule Syntax\n``` txt\npattern sniCallback://plugin-name(sniValue) [filters...]\n```\n> `(sniValue)` is optional and can be obtained via `req.originalReq.sniValue` in the plugin hook.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| plugin-name(sniValue) | Plugin name + optional parameters | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwwww.example.com sniCallback://test\nwwww.example.com sniCallback://test-sni(abc)\n```\n\nAccess SNI information in the plugin through the following properties:\n``` js\nexports.auth = (req) => {\n  const { sniValue, servername } = req.originalReq; // Get configuration parameters and servername\n\n  return {\n    cert: /* Certificate content */,\n    key: /* Private key content */\n  }\n};\n```\n\nFor specific usage, refer to [Plugin Development Documentation](../extensions/dev.md#snicallback)\n"
  },
  {
    "path": "docs/en/docs/rules/socks.md",
    "content": "# socks\nThe `socks` directive is used to forward matching requests through a specified SOCKS5 proxy server.\n\n## Rule Syntax\n``` txt\npattern socks://ipOrDomain[:port] [filters...]\n```\n> `port` is optional. If left blank, the default port `443` will be used.\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| value | IP + optional port or domain name + optional port<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters. Supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## Configuration Example\n``` txt\n# Proxy requests to a SOCKS5 proxy: `127.0.0.1:443`\nwww.example.com/path socks://127.0.0.1 # Default port 443\n\n# Proxy all requests for the current domain to the SOCKS5 proxy: `127.0.0.1:8080`\nwww.example.com socks://127.0.0.1:8080\n\n# You can also use a domain name.\nwww.example.com/path socks://test.proxy.com # Default port 443\nwww.example.com socks://test.proxy.com:8080\n```\n\n## Advanced Usage\nBy default, the upstream proxy resolves the requested domain name. However, in some scenarios, you may want to force the proxy to access a specific target IP directly (skipping DNS resolution), for example:\n- Bypassing DNS poisoning\n- Directly accessing a specific backend IP\n- Testing services in different environments\n``` txt\n# Using query parameters\nwww.example.com socks://127.0.0.1:8080?host=1.1.1.1\nwww.example.com socks://127.0.0.1:8080?host=1.1.1.1:8080\n\n# Enabling via directives\nwww.example.com socks://127.0.0.1:8080 1.1.1.1 enable://proxyHost\nwww.example.com socks://127.0.0.1:8080 1.1.1.1:8080 enable://proxyHost\n````\n> `1.1.1.1` is equivalent to `host://1.1.1.1`\n\n## Notes  \nThe `socks` protocol only applies to the substituted URL (i.e., the `Final URL` shown in the Overview). If the `Final URL` is empty, it will take effect on the original request URL.  \n\nFor example, with the rule:  \n```  \nwww.example.com/api www.example.com proxy://127.0.0.1:1234  \n```  \nWhen a request is made to `https://www.example.com/api/path`, Whistle processes it and changes the URL to `https://www.example.com/path` (this becomes the `Final URL`). Although the intention is to proxy this request to `127.0.0.1:1234`, the `proxy` rule only matches the original domain `www.example.com/api` before substitution. Since the converted `Final URL` is now `www.example.com/path`, this rule will not be triggered.  \n\nTo ensure the rule also applies to the substituted request, you can break it into two separate rules:  \n```  \nwww.example.com/api www.example.com  \nwww.example.com proxy://127.0.0.1:1234  \n```  \n\nThis way, the original request is first rewritten by the first rule, generating a new `Final URL`. Then, the second `proxy` rule matches this new URL and successfully proxies the request to `127.0.0.1:1234`.\n"
  },
  {
    "path": "docs/en/docs/rules/statusCode.md",
    "content": "# statusCode\n\nThe `statusCode` protocol is used to immediately interrupt a request and return a specified HTTP status code, without forwarding the request to the backend server. Through the `statusCode` protocol, you can:\n- **Quickly simulate specific HTTP status codes**: Test various status code responses without a real server.\n- **Intercept and interrupt requests**: Directly return a status code before the request reaches the real server.\n- **Test error scenarios**: Simulate server errors, redirects, authentication failures, etc.\n- **Control request flow**: Decide whether to allow the request to continue based on conditions.\n\n## Rule Syntax\n\nThe `statusCode` protocol supports multiple configuration methods:\n\n### 1. Inline Value (Direct Specification)\nWrite the status code to return directly in the rule.\n\n```txt\npattern statusCode://code [lineProps...] [filters...]\n```\n\n**Example:**\n```txt\nwww.example.com/api/old-endpoint statusCode://410\n```\n\n### 2. Embedded Value (Using Code Block)\nUse this method when you need to return different status codes based on conditions or want to reuse configurations.\n\n````txt\npattern statusCode://{custom-key} [lineProps...] [filters...]\n\n``` custom-key\n404\n```\n````\n\n### 3. Referencing a Value from the Values Panel\nReference a status code pre-defined in the `Values` panel (central configuration area).\n\n```txt\npattern statusCode://{key-of-values} [lineProps...] [filters...]\n```\n\n**Prerequisite**: A key named `key-of-values` with a status code as its value must exist in `Values`.\n\n### 4. File/Remote URL Loading\n**Currently NOT supported** to dynamically load content from local files or remote URLs.\n\n## Parameter Details\n\n| Parameter | Required | Description & Examples |\n| :--- | :--- | :--- |\n| **pattern** | Yes | Expression used to match the request URL.<br>• Supports domains, paths, wildcards, regular expressions.<br>• See [Matching Pattern Documentation](./pattern) for details. |\n| **code** | Yes | HTTP response status code, such as:<br>• `200` OK<br>• `301` Permanent Redirect<br>• `302` Temporary Redirect<br>• `404` Not Found<br>• `500` Internal Server Error<br>• Supports all standard HTTP status codes. |\n| **lineProps** | No | Sets additional properties for the rule.<br>• Example: `lineProps://important` can increase this rule's priority.<br>• See [lineProps Documentation](./lineProps) for details. |\n| **filters** | No | Optional filter conditions for precise control over when the rule takes effect.<br>• Can match request URL, method, headers, body content.<br>• Can match response status code, headers.<br>• See [Filters Documentation](./filters) for details. |\n\n## Configuration Examples\n\n### Basic Examples\n```txt\n# Return 404 status code (Page Not Found)\nwww.example.com/deleted-page statusCode://404\n\n# Return 500 status code (Internal Server Error)\nwww.example.com/api/error statusCode://500\n\n# Return 302 Redirect\nwww.example.com/old-url statusCode://302\n```\n\n### Simulating Authentication Failure\n```txt\n# Authentication required when accessing, browser will pop up login dialog.\nwww.example.com/secure-area statusCode://401\n\n# Disable the login dialog and directly return 401.\nwww.example.com/secure-area statusCode://401 disable://userLogin\n\n# Or use lineProps to achieve the same effect.\nwww.example.com/secure-area statusCode://401 lineProps://disableUserLogin\n```\n\n### Combining with Custom Response Content\n```txt\n# Return 404 status code with custom response content.\nwww.example.com/missing-page statusCode://404 resBody://(<h1>Page Not Found</h1>)\n```\n\n### Using with Filters\n```txt\n# Return 405 (Method Not Allowed) only for specific request methods.\nwww.example.com/api/resource statusCode://405 includeFilter://m:PUT\n\n# Return different status codes based on request path matching.\n/^https?://www\\.example\\.com/user/\\d+/profile/ statusCode://403\n\n# Return status code based on request header conditions.\nwww.example.com/api statusCode://429 includeFilter://reqH:user-agent=/bot/i\n```\n\n### Using Embedded Values\n````txt\n``` maintenance-status\n503\n```\n\nwww.example.com statusCode://{maintenance-status}\n````\n\n### Environment-Specific Configuration\n````txt\n``` testing-403\n403\n```\n\n# Return 403 only in testing environment.\ntest.example.com/api/admin statusCode://{testing-403}\n````\n\n## Common Status Code Scenarios\n\n| Status Code | Scenario Description | Typical Use |\n| :--- | :--- | :--- |\n| **200** | Successful Response | Test normal flow. |\n| **301/302** | Redirect | Test URL redirection logic. |\n| **400** | Bad Request | Test client error handling. |\n| **401** | Unauthorized | Test authentication flow. |\n| **403** | Forbidden | Test access control. |\n| **404** | Not Found | Test resource not found handling. |\n| **429** | Too Many Requests | Test rate limiting logic. |\n| **500** | Internal Server Error | Test server-side exception handling. |\n| **503** | Service Unavailable | Test maintenance page. |\n\n## Notes\n\n### 1. Response Content\n- By default, the response content returned by `statusCode` is empty.\n- Custom response content can be set by combining with the [`resBody`](./resBody) protocol.\n\n### 2. Authentication Popup Control\n- When returning a `401` status code, the browser authentication popup is triggered by default.\n- You can disable the popup in the following ways:\n  - Add `disable://userLogin`.\n  - Add `lineProps://disableUserLogin`.\n  - Use the reverse configuration of the [`enable`](./enable) protocol.\n\n### 3. Redirect Handling\n- When returning redirect status codes such as `301`, `302`, you need to specify the redirect address with the [`location`](./resHeaders) response header:\n  ```txt\n  www.example.com/old statusCode://302 resHeaders://location=\"https://www.example.com/new\"\n  ```\n\n## Advanced Usage\n\n### Dynamic Status Codes\n```txt\n# Return different status codes based on time (maintenance window).\nwww.example.com/api statusCode://503 includeFilter://t:>=00:00&t:<=06:00\n```\n\n### Combining with Other Protocols\n```txt\n# Return 403 with a custom error page.\nwww.example.com/blocked statusCode://403 resBody://<h1>Access Denied</h1> resHeaders://content-type=\"text/html; charset=utf-8\"\n\n# Return 503 and set retry time.\nwww.example.com/down statusCode://503 resHeaders://retry-after=\"3600\"\n```\n\n### Testing Client Fault Tolerance\n```txt\n# Randomly return different error status codes to test client fault tolerance.\nwww.example.com/api/unstable statusCode://500 includeFilter://chance:0.5\nwww.example.com/api/unstable statusCode://429 includeFilter://chance:0.3\nwww.example.com/api/unstable statusCode://200 includeFilter://chance:0.2\n```\n\n## Difference from replaceStatus\n- **`statusCode`**: Takes effect during the request stage, **does not forward to the backend server**.\n- **`replaceStatus`**: Takes effect during the response stage, **first requests the backend server**, then replaces the returned status code.\n\n## Troubleshooting\n\n### Q: Status code rule is not taking effect.\n**A:** Check:\n1. Whether the rule pattern correctly matches the request URL.\n2. Whether there are other higher-priority rules overriding it.\n3. Whether the filter conditions are met.\n\n### Q: Browser pops up authentication window.\n**A:** For 401 status code:\n1. Check if `disable://userLogin` or `lineProps://disableUserLogin` is added.\n2. Confirm if the rule order is correct.\n\n### Q: Client displays a blank page.\n**A:** Check:\n1. Whether custom response content is set.\n2. Whether the `Content-Type` response header is correct.\n3. Whether the response content encoding matches.\n\n### Q: Redirect is not taking effect.\n**A:** Check:\n1. Whether the `Location` response header is set.\n2. Whether the redirect address format is correct.\n3. Whether the browser follows the redirect rules.\n\n## Related Protocols\n\n1. **Replace response status code**: [replaceStatus](./replaceStatus)\n   - The request first reaches the server, then replaces the returned status code.\n2. **Disable authentication popup**: [disable](./disable) or [lineProps](./lineProps)\n   - Disable the browser login popup triggered by 401 status code.\n3. **Set response content**: [resBody](./resBody)\n   - Add custom content for status code responses.\n4. **Set response headers**: [resHeaders](./resHeaders)\n   - Set necessary response headers for scenarios like redirects.\n\n## Further Reading\n\n- [HTTP Status Codes MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status): Learn the meaning of all HTTP status codes.\n- [Matching Pattern Documentation](./pattern): Learn more about URL matching rules.\n- [Filters Documentation](./filters): Learn more about filter functionalities.\n"
  },
  {
    "path": "docs/en/docs/rules/style.md",
    "content": "# style\n\nCustomize the display style of request rows in the Network list, including font color, style, background color, and more.\n\n## Rule Syntax\n``` txt\npattern style://color=@value&fontStyle=value&bgColor=@value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match against the request URL | [Match Pattern Documentation](./pattern) |\n| value | Supported style attributes:<br/>• `color` - Font color (`@hex` or CSS color name, such as `red`)<br/>• `fontStyle` - Font style (such as `italic`, `bold`) See [font-style](https://developer.mozilla.org/en-US/docs/Web/CSS/font-style)<br/>• `bgColor` - Background color (@hex or CSS color name, such as `red`) | |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.test.com style://color=@fff&fontStyle=italic&bgColor=red\n```\n<img src=\"/img/style.png\" width=\"1000\" />\n"
  },
  {
    "path": "docs/en/docs/rules/tpl.md",
    "content": "# tpl\ntpl is an enhanced version of the [file](./file) function, providing simple template engine functionality.\n\n## Rule Syntax\n``` txt\npattern tpl://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation content, supports the following types:<br/>• Directory/File Path<br/>• Remote URL<br/>• Inline/Embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Header/Content<br/>• Response Status Code/Header | [Filter Documentation](./filters) |\n\n## Template Rules\n1. Value format: `{key}` or `{{key}}`\n2. `key`: A property in the request parameters\n\n## Mock JSONP\n1. Local File `/User/xxx/test.js`\n``` js\n{callback}({\n  ec: 0\n});\n```\n2. Rule Configuration\n``` txt\nhttps://www.example.com/test/tpl tpl:///User/xxx/test.js\n\n# Supports remote URLs\n# pattern tpl://https://example.com/template\n```\n3. Request `https://www.example.com/test/tpl?callback=test` Returns:\n``` txt\ntest({\n  ec: 0\n});\n```\n\nFor other functions, refer to [file](./file)\n"
  },
  {
    "path": "docs/en/docs/rules/trailers.md",
    "content": "# trailers\nModify or add trailers (`trailers`) to responses using `Transfer-Encoding: chunked`. Trailers are additional HTTP header fields sent after the response body of a chunked transfer.\nHTTP Tailers Function: https://http.dev/trailer\n\n## Rule Syntax\n``` txt\npattern trailers://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following sources:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n## Configuration Example\n#### Basic Configuration\n``` txt\n# Set the request header `x-proxy: Whistle`\nwww.example.com/path trailers://x-proxy=Whistle\n```\n\n#### Setting Multiple Request Headers\n\n```` txt\n``` test.json\nx-test1: 1\nx-test2:\nx-test3: abc\n```\nwww.example.com/path2 trailers://{test.json}\n\n# Equivalent to: www.example.com/path2 trailers://x-test1=1&x-test2=&x-test3=abc\n````\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 trailers:///User/xxx/test.json\nwww.example.com/path2 trailers://https://www.xxx.com/xxx/params.json\n# Editing a Temporary File\nwww.example.com/path3 trailers://temp/blank.json\n````\n\n## Associated Protocols\n1. Delete request header field: [delete://trailers.xxx](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/tunnel.md",
    "content": "# tunnel\nForward tunnel proxy requests to the new server.\n> Only supports tunnel proxy `tunnel://domain:port`, not WebSocket requests or regular HTTP/HTTPS conversion.\n\n## Rule Syntax\n``` txt\npattern tunnel://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match WebSocket request URLs | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]`<br/>`port` defaults to `443`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## TUNNEL Conversion\n``` txt\ntunnel://www.example.com tunnel://www.test.com:5678\ntunnel://www.example2.com:8080 tunnel://www.test.com\n```\n| Original Request | Transformation Result |\n| ----------------------------- | ----------------------------------------- |\n| `tunnel://www.example.com:443` | `tunnel://www.test.com:5678` |\n| `tunnel://www.example2.com:8080` | `tunnel://www.test.com:443` |\n\nTUNNEL requests do not have automatic path splicing and path splicing is disabled. Other requests matching the `tunnel` protocol result:\n- **WebSocket requests**: Ignore the match\n- **Normal HTTP/HTTPS requests**: Return a `502` error\n"
  },
  {
    "path": "docs/en/docs/rules/ua.md",
    "content": "# ua\nA shortcut protocol for modifying the `User-Agent` field in the request header. This can be used to simulate access from various machines.\n\n## Rule Syntax\n``` txt\npattern ua://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Custom request `User-Agent` string<br/>• Inline/Embedded/Values content<br/>⚠️ Loading data from files/remote URLs is not supported | |\n| filters | Optional filters, supports matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filter Documentation](./filters) |\n\n## Configuration Example\n```` txt\n# Change the original user-agent to `Whistle/2.9.100`\nwww.example.com/path ua://Whistle/2.9.100\n\n# Spaces UA\n``` ua.txt\nTest Whistle/2.9.100\n```\nwww.example.com/path ua://{ua.txt}\n\n# Using reqHeaders\n``` ua.json\nuser-agent: Test Whistle/2.9.100\n```\nwww.example.com/path reqHeaders://{ua.json}\n````\n\n## Associated Protocols\n1. Directly modify the request header: [reqHeaders://User-Agent=value](./reqHeaders)\n"
  },
  {
    "path": "docs/en/docs/rules/urlParams.md",
    "content": "# urlParams\nDynamically modify the query parameters of the request URL, supporting multiple parameter injection methods.\n\n## Rule Syntax\n``` txt\npattern urlParams://value [filters...]\n```\n\n| Parameter | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match request URLs | [Match Pattern Documentation](./pattern) |\n| value | Operation data object, supported from the following channels:<br/>• Directory/file path<br/>• Remote URL<br/>• Inline/embedded/Values content | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supported for matching:<br/>• Request URL/method/header/content<br/>• Response status code/header | [Filter Documentation](./filters) |\n\n## Configuration Example\n``` txt\nwww.example.com/path urlParams://test=123\n```\nAccessing `https://www.example.com/path/to` The server receives URL: `https://www.example.com/path/to?test=123`\n\n<img src=\"/img/url-params.png\" width=\"360\" />\n\n```` txt\n``` test.json\ntest1: 1\ntest2:\ntest3: 3\n```\nwww.example.com/path2 urlParams://{test.json}\n````\nAccessing `https://www.example.com/path2/to` URL received by the server: `https://www.example.com/path2?test1=1&test2=&test3=3`\n\n<img src=\"/img/url-params2.png\" width=\"360\" />\n\n#### Local/Remote Resources\n\n```` txt\nwww.example.com/path1 urlParams:///User/xxx/test.json\nwww.example.com/path2 urlParams://https://www.xxx.com/xxx/params.json\n# Editing a temporary file\nwww.example.com/path3 urlParams://temp/blank.json\n````\n\n## Related Protocols\n1. More flexible way to modify request parameters: [pathReplace](./pathReplace)\n2. Deleting request parameters: [delete://urlParams.xxx](./delete)\n"
  },
  {
    "path": "docs/en/docs/rules/weinre.md",
    "content": "# weinre\nInjects the Weinre (Web Inspector Remote) web debugging tool into the page, allowing developers to debug web pages on mobile devices directly from their computers.\n\n## Rule Syntax\n``` txt\npattern weinre://id [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | An expression to match the request URL | [Match Pattern Documentation](./pattern) |\n| id | Weinre ID, a plain string, used for group filtering | |\n| filters | Optional filters, supporting matching: • Request URL/Method/Header/Content • Response Status Code/Header | [Filter Documentation](./filters) |\n\nFor detailed usage, refer to [UI Menu Weinre Usage](../gui/weinre)\n"
  },
  {
    "path": "docs/en/docs/rules/ws.md",
    "content": "# ws\nConverts a WebSocket request into a new ws request (the server will receive the converted WebSocket URL).\n> Only supports WebSocket requests `ws[s]://domain[:port]/[path][?query]`. Transformation tunneling and plain HTTP/HTTPS are not supported.\n\n## Rule Syntax\n``` txt\npattern ws://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match WebSocket request URLs | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]/[path][?query]`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## WebSocket Transformation\n``` txt\nws://www.example.com/path1 ws://www.test.com/path/xxx\nwss://www.example.com/path2 ws://www.abc.com/path3/yyy\n```\n1. Automatic path concatenation:\n    | Original request | Conversion result (URL received by the server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `ws://www.example.com/path1` | `ws://www.test.com/path/xxx` |\n    | `ws://www.example.com/path1/a/b/c?query` | `ws://www.test.com/path/xxx/a/b/c?query` |\n    | `wss://www.example.com/path2` | `ws://www.abc.com/path3/yyy` |\n    | `wss://www.example.com/path2/a/b/c?query` | `ws://www.abc.com/path3/yyy/a/b/c?query` |\n2. Disable path concatenation: Use `< >` or `()` to wrap the path.\n    ``` txt\n    www.example.com/path1 ws://<www.test.com/path/xxx>\n    # www.example.com/path1 ws://(www.test.com/path/xxx)\n    ```\n    | Original Request | Conversion Result (URL Received by the Server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[wss/ws]://www.example.com/path/x/y/z` | `ws://www.test.com/path/xxx` |\n\nOnly supports forwarding WebSocket requests; other requests match the `ws` protocol. Result:\n- **Tunnel Proxy**: Ignore matches\n- **Normal HTTP/HTTPS Request**: Returns `502`\n"
  },
  {
    "path": "docs/en/docs/rules/wss.md",
    "content": "# wss\nConverts a WebSocket request into a new wss request (the server will receive the converted WebSocket URL).\n> Only supports WebSocket requests `ws[s]://domain[:port]/[path][?query]`. Transformation tunneling proxies and regular HTTP/HTTPS are not supported.\n\n## Rule Syntax\n``` txt\npattern wss://value [filters...]\n```\n\n| Parameters | Description | Detailed Documentation |\n| ------- | ------------------------------------------------------------ | ------------------------- |\n| pattern | Expression to match WebSocket request URLs | [Match Pattern Documentation](./pattern) |\n| value | Target URL format: `domain[:port]/[path][?query]`<br/>⚠️ Loading data from files/remote URLs is not supported | [Operation Instruction Documentation](./operation) |\n| filters | Optional filters, supporting matching:<br/>• Request URL/Method/Headers/Content<br/>• Response Status Code/Headers | [Filters Documentation](./filters) |\n\n## WebSocket Transformation\n``` txt\nws://www.example.com/path1 wss://www.test.com/path/xxx\nwss://www.example.com/path2 wss://www.abc.com/path3/yyy\n```\n1. Automatic path concatenation:\n    | Original request | Conversion result (URL received by the server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `ws://www.example.com/path1` | `wss://www.test.com/path/xxx` |\n    | `ws://www.example.com/path1/a/b/c?query` | `wss://www.test.com/path/xxx/a/b/c?query` |\n    | `wss://www.example.com/path2` | `wss://www.abc.com/path3/yyy` |\n    | `wss://www.example.com/path2/a/b/c?query` | `wss://www.abc.com/path3/yyy/a/b/c?query` |\n2. Disable path concatenation: Use `< >` or `()` to wrap the path.\n    ``` txt\n    www.example.com/path1 wss://<www.test.com/path/xxx>\n    # www.example.com/path1 wss://(www.test.com/path/xxx)\n    ```\n    | Original Request | Conversion Result (URL Received by the Server) |\n    | ----------------------------------------- | ----------------------------------------- |\n    | `[wss/ws]://www.example.com/path/x/y/z` | `wss://www.test.com/path/xxx` |\n\nOnly supports forwarding WebSocket requests; other requests match the `wss` protocol. Result:\n- **Tunnel Proxy**: Ignore matches\n- **Normal HTTP/HTTPS Request**: Returns `502`\n"
  },
  {
    "path": "docs/en/docs/rules/xfile.md",
    "content": "# xfile\n\nxfile is a pass-through version of the [file](./file) rule, differing primarily in how it handles file non-existence:\n- ✅ If the file exists: Returns the local file contents (same behavior as the [file](./file) rule)\n- ❌ If the file does not exist: Continues with the normal network request (whereas the [file](./file) rule returns a `404` error).\n\n## Rule Syntax\n``` txt\npattern xfile://value [filters...]\n```\n\nFor detailed usage, see: [file](./file)\n"
  },
  {
    "path": "docs/en/docs/rules/xhost.md",
    "content": "# xhost\n\nxhost is a pass-through version of the [host](./host) rule. The main difference lies in how it handles request failures:\n- ✅ Request normal: Behaves the same as the [host](./host) rule\n- ❌ Request error: Ignore the matching rule and continue the normal network request (while [host](./host) aborts the request).\n\n## Rule Syntax\n``` txt\npattern xhost://value [filters...]\n```\n\nFor detailed usage, see: [host](./host)\n"
  },
  {
    "path": "docs/en/docs/rules/xhttps-proxy.md",
    "content": "# xhttps-proxy\n\nxhttps-proxy is a pass-through version of the [https-proxy](./https-proxy) rule. The main difference lies in how it handles connection failures to the target proxy:\n- ✅ When a connection is established successfully: Behaves the same as the [https-proxy](./https-proxy) rule\n- ❌ When a connection fails: Ignore the matching rule and continue the normal network request (while [https-proxy](./https-proxy) aborts the request).\n\n## Rule Syntax\n``` txt\npattern xhttps-proxy://value [filters...]\n```\n\nFor detailed usage, see [https-proxy](./https-proxy)\n"
  },
  {
    "path": "docs/en/docs/rules/xproxy.md",
    "content": "# xproxy (xhttp-proxy)\n\nxproxy (xhttp-proxy) is a pass-through version of the [proxy](./proxy) rule. The main difference lies in how it handles connection failures to the target proxy:\n- ✅ When a connection is established successfully: Behaves the same as the [proxy](./proxy) rule\n- ❌ When a connection fails: Ignore the matching rule and continue the normal network request (while [proxy](./proxy) aborts the request).\n\n## Rule Syntax\n``` txt\npattern xproxy://value [filters...]\n```\n\nFor detailed usage, see: [proxy](./proxy)\n"
  },
  {
    "path": "docs/en/docs/rules/xrawfile.md",
    "content": "# xrawfile\n\nxrawfile is a pass-through version of the [rawfile](./rawfile) rule, differing primarily in how it handles file non-existence:\n- ✅ If the file exists: Returns the local file contents (same behavior as the [rawfile](./rawfile) rule)\n- ❌ If the file does not exist: Continues with the normal network request (whereas the [rawfile](./rawfile) rule returns a `404` error).\n\n## Rule Syntax\n``` txt\npattern xrawfile://value [filters...]\n```\n\nFor detailed usage, see: [rawfile](./rawfile)\n"
  },
  {
    "path": "docs/en/docs/rules/xsocks.md",
    "content": "# xsocks\n\nxsocks is a pass-through version of the [socks](./socks) rule. The main difference lies in how it handles connection failures to the target proxy:\n- ✅ When the connection is established successfully: Behaves the same as the [socks](./socks) rule\n- ❌ When the connection fails: Ignore the matching rule and continue the normal network request (while [socks](./socks) aborts the request).\n\n## Rule Syntax\n``` txt\npattern xsocks://value [filters...]\n```\n\nFor detailed usage, see [socks](./socks)\n"
  },
  {
    "path": "docs/en/docs/rules/xtpl.md",
    "content": "# xtpl\n\nxtpl is a fall-through version of the [tpl](./tpl) rule. The main difference lies in how it handles file non-existence:\n- ✅ If the file exists: Returns the local file contents (same behavior as the [tpl](./tpl) rule)\n- ❌ If the file does not exist: Continues with the normal network request (whereas the [tpl](./tpl) rule returns a `404` error).\n\n## Rule Syntax\n``` txt\npattern xtpl://value [filters...]\n```\n\nFor detailed usage, see: [tpl](./tpl)\n"
  },
  {
    "path": "docs/en/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: \"Whistle\"\n  text: \"Cross-platform network debugging proxy\"\n  tagline: Provides professional-level packet capture debugging solutions, experience simple operations like blowing a whistle\n  image: /img/whistle.png\n  actions:\n    - theme: brand\n      text: Whistle client version\n      link: https://github.com/avwo/whistle-client\n    - theme: alt\n      text: Whistle command line version\n      link: https://github.com/avwo/whistle\n\nfeatures:\n  - title: Powerful\n    details: Supports HTTP/HTTPS/SOCKS/Reverse proxy, can intercept and modify mainstream network protocol traffic, built-in debugging tools such as Weinre\n  - title: Simple operation\n    details: Modify requests/responses by configuring rules, and provide a one-stop interface including viewing packet capture, configuring rules, managing plug-ins and debugging tools\n  - title: Extensible\n    details: Functions can be extended through plug-ins, or used as NPM module dependencies\n  - title: Cross-platform\n    details: Supports macOS/Windows/Linux mainstream desktop systems and non-header servers\n---\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: \"Whistle\"\n  text: \"专业级网络调试代理\"\n  tagline: 操作跟吹口哨（Whistle）一样简单\n  image: /img/whistle.png\n  actions:\n    - theme: brand\n      text: Whistle 客户端版本\n      link: https://github.com/avwo/whistle-client\n    - theme: alt\n      text: Whistle 命令行版本\n      link: https://github.com/avwo/whistle\n\nfeatures:\n  - title: 功能强大\n    details: 支持 HTTP/HTTPS/SOCKS/反向代理，可拦截修改主流网络协议流量，内置 Weinre 等调试工具\n  - title: 操作简单\n    details: 通过配置规则修改请求/响应，提供包含查看抓包、配置规则、管理插件及调试工具的一站式界面\n  - title: 可扩展\n    details: 可通过插件扩展规则及界面功能，或作为标准 NPM 模块依赖集成\n  - title: 跨平台\n    details: 支持 macOS/Windows/Linux 桌面系统、无界面服务器系统及 Docker 等容器化平台\n---\n"
  },
  {
    "path": "index.d.ts",
    "content": "/// <reference types=\"node\" />\n\nimport { IncomingMessage, ServerResponse, Server } from 'http';\nimport EventEmitter from 'events';\n\nexport type WhistleBody = string | false;\n\nexport interface WhistleFrame {\n reqId: string;\n frameId: string;\n base64?: string;\n bin?: '' | Buffer;\n text?: string;\n mask?: boolean;\n compressed?: boolean;\n length?: number;\n opcode?: number;\n isClient?: boolean;\n err?: string;\n closed?: true;\n code?: string | number;\n [propName: string]: any;\n}\n\nexport interface WhistleSession {\n id: string;\n url: string;\n useH2?: boolean;\n isHttps?: boolean;\n startTime: number;\n dnsTime?: number;\n requestTime: number;\n responseTime: number;\n endTime?: number;\n req: {\n   method?: string;\n   httpVersion?: string;\n   ip?: string;\n   port?: string | number;\n   rawHeaderNames?: object;\n   headers: object;\n   size?: number;\n   body?: WhistleBody;\n   base64?: WhistleBody;\n   rawHeaders?: object;\n   [propName: string]: any;\n };\n res: {\n   ip?: string;\n   port?: string | number;\n   rawHeaderNames?: object;\n   statusCode?: number | string;\n   statusMessage?: string;\n   headers?: object;\n   size?: number;\n   body?: WhistleBody;\n   base64?: WhistleBody;\n   rawHeaders?: object;\n   [propName: string]: any;\n };\n rules: object;\n rulesHeaders?: object;\n frames?: WhistleFrame[];\n [propName: string]: any;\n}\n\nexport type WhistleSecureFilter = ((item: WhistleSession, clientIp?: string, filter?: string) => WhistleSession) | string;\n\nexport interface WhistleOptions {\n  whistleName?: string;\n  config?: string;\n  cluster?: number | string;\n  server?: EventEmitter | Server;\n  debugMode?: boolean;\n  mode?: string;\n  realPort?: number;\n  realHost?: string;\n  port?: number | string;\n  uiport?: number | string;\n  socksPort?: number | string;\n  httpPort?: number | string;\n  httpsPort?: number | string;\n  host?: string;\n  authKey?: string;\n  guestAuthKey?: string;\n  reqCacheSize?: number;\n  frameCacheSize?: number;\n  allowMultipleChoice?: boolean;\n  timeout?: number;\n  username?: string;\n  password?: string;\n  guestName?: string;\n  guestPassword?: string;\n  disableAllRules?: boolean;\n  disableAllPlugins?: boolean;\n  replaceExistRule?: boolean;\n  replaceExistValue?: boolean;\n  allowPluginList?: boolean;\n  blockPluginList?: boolean;\n  webUIPath?: string;\n  certDir?: string;\n  middleware?: string;\n  uiMiddleware?: string;\n  cmdName?: string;\n  account?: string;\n  dnsServer?: string;\n  projectPluginsPath?: string | string[];\n  accountPluginsPath?: string | string[];\n  customPluginsPath?: string | string[];\n  notUninstallPluginPath?: string | string[];\n  pluginsPath?: string | string[];\n  addonsPath?: string | string[];\n  specialAuth?: string;\n  specialPath?: string;\n  inspect?: boolean;\n  inspectBrk?: boolean;\n  secureFilter?: WhistleSecureFilter;\n  encrypted?: boolean;\n  sockets?: number;\n  dataDirname?: string;\n  storage?: string;\n  baseDir?: string;\n  noGlobalPlugins?: boolean;\n  pluginsDataMap?: object;\n  globalData?: object;\n  localUIHost?: string;\n  extra?: string;\n  rules?: object;\n  values?: object;\n  shadowRules?: string;\n  dnsCache?: number;\n  allowDisableShadowRules?: boolean;\n  disableInstaller?: boolean;\n  customHandler?: (req: IncomingMessage, res: ServerResponse, next?: Function) => void;\n  pluginHost?: string;\n  copy?: string;\n  uiExt?: {\n    required?: boolean;\n    htmlPrepend?: string;\n    htmlAppend?: string;\n    jsPrepend?: string;\n    jsAppend?: string;\n  };\n  [propName: string]: any;\n}\n\nexport interface WhistleAuth {\n username?: string;\n password?: string;\n guestName?: string;\n guestPassword?: string;\n guest?: {\n   username?: string;\n   password?: string;\n };\n}\n\nexport interface WhistleRuntimeInfo {\n memUsage: NodeJS.MemoryUsage;\n uptime: number;\n cpuPercent: string;\n startupTime: number;\n updateTime: number;\n httpRequests: number;\n allHttpRequests: number;\n wsRequests: number;\n allWsRequests: number;\n tunnelRequests: number;\n totalHttpRequests: number;\n totalWsRequests: number;\n totalTunnelRequests: number;\n totalAllHttpRequests: number;\n totalAllWsRequests: number;\n httpQps: number;\n tunnelQps: number;\n wsQps: number;\n totalQps: number;\n maxQps: number;\n maxAllQps: number;\n maxRss: number;\n maxCpu: number;\n}\n\nexport type WhistleLogFn = (msg: Object, ...restMsg: Object[]) => void;\n\nexport interface WhistleResult {\n  logger: {\n    log: (msg: Object, level?: string) => void;\n    fatal: WhistleLogFn;\n    error: WhistleLogFn;\n    warn: WhistleLogFn;\n    info: WhistleLogFn;\n    debug: WhistleLogFn;\n  };\n  getWhistlePath(): string;\n  setAuth(auth: WhistleAuth): void;\n  setUIHost(host: string | string[]): void;\n  setPluginUIHost(pluginName: string, host: string | string[]): void;\n  getRuntimeInfo(): WhistleRuntimeInfo;\n  getShadowRules(): string;\n  setShadowRules(shadowRules: string): void;\n  [propName: string]: any;\n}\n\nexport type WhistleCallback = (result?: WhistleResult) => void;\n\nexport function getWhistlePath(): string;\n\nexport default function(options?: WhistleOptions | WhistleCallback, callback?: WhistleCallback): WhistleResult;\n"
  },
  {
    "path": "index.js",
    "content": "require('./lib/util/patch');\nvar net = require('net');\nvar tls = require('tls');\nvar extend = require('extend');\nvar path = require('path');\nvar cluster = require('cluster');\nvar os = require('os');\nvar assert = require('assert');\nvar common = require('./lib/util/common');\n\nvar ver = process.version.substring(1).split('.');\nvar PROD_RE = /(^|\\|)prod(uction)?($|\\|)/;\nvar noop = function () {};\nvar state = {};\nvar INTERVAL = 1000;\nvar TIMEOUT = 10000;\nvar MASTER_TIMEOUT = 12000;\n\nif (ver[0] >= 7 && ver[1] >= 7) {\n  var connect = net.Socket.prototype.connect;\n  if (typeof connect === 'function') {\n    //fix: Node v7.7.0+引入的 `\"listener\" argument must be a function` 问题\n    net.Socket.prototype.connect = function (options, cb) {\n      if (options && typeof options === 'object' && typeof cb !== 'function') {\n        return connect.call(this, options, null);\n      }\n      return connect.apply(this, arguments);\n    };\n  }\n}\n\nvar env = process.env || '';\nenv.WHISTLE_ROOT = __dirname;\nif (typeof tls.checkServerIdentity == 'function') {\n  var checkServerIdentity = tls.checkServerIdentity;\n  tls.checkServerIdentity = function () {\n    try {\n      return checkServerIdentity.apply(this, arguments);\n    } catch (err) {\n      return err;\n    }\n  };\n}\nif (env.WHISTLE_PLUGIN_EXEC_PATH) {\n  env.PFORK_EXEC_PATH = env.WHISTLE_PLUGIN_EXEC_PATH;\n}\n\nfunction isPipeName(s) {\n  return typeof s === 'string' && toNumber(s) === false;\n}\n\nfunction toNumber(x) {\n  return (x = Number(x)) >= 0 ? x : false;\n}\n\nif (!net._normalizeConnectArgs) {\n  //Returns an array [options] or [options, cb]\n  //It is the same as the argument of Socket.prototype.connect().\n  net._normalizeConnectArgs = function (args) {\n    var options = {};\n\n    if (args[0] !== null && typeof args[0] === 'object') {\n      // connect(options, [cb])\n      options = args[0];\n    } else if (isPipeName(args[0])) {\n      // connect(path, [cb]);\n      options.path = args[0];\n    } else {\n      // connect(port, [host], [cb])\n      options.port = args[0];\n      if (typeof args[1] === 'string') {\n        options.host = args[1];\n      }\n    }\n\n    var cb = args[args.length - 1];\n    return typeof cb === 'function' ? [options, cb] : [options];\n  };\n}\n\nfunction loadConfig(options) {\n  var config = options.config;\n  if (config) {\n    delete options.config;\n    return require(path.resolve(config));\n  }\n}\n\nfunction likePromise(p) {\n  return p && typeof p.then === 'function' && typeof p.catch === 'function';\n}\n\nfunction killWorker(worker) {\n  try {\n    worker.removeAllListeners();\n    worker.on('error', noop);\n    worker.kill('SIGTERM');\n  } catch (err) {}\n}\n\nfunction forkWorker(index) {\n  var worker = cluster.fork({ workerIndex: index });\n  var reforked;\n  var refork = () => {\n    if (!state[index]) {\n      setTimeout(function () {\n        process.exit(1);\n      }, INTERVAL);\n      return;\n    }\n    if (reforked) {\n      return;\n    }\n    reforked = true;\n    killWorker(worker);\n    clearInterval(worker.timer);\n    clearTimeout(worker.activeTimer);\n    setTimeout(function () {\n      forkWorker(index);\n    }, 600);\n  };\n  worker.once('disconnect', refork);\n  worker.once('exit', refork);\n  worker.on('error', noop);\n  worker.on('message', (msg) => {\n    if (msg !== '1') {\n      return;\n    }\n    state[index] = true;\n    if (!worker.timer) {\n      worker.timer = setInterval(() => {\n        try {\n          worker.send('1', noop);\n        } catch (e) {\n          clearInterval(worker.timer);\n        }\n      }, INTERVAL);\n    } else {\n      clearTimeout(worker.activeTimer);\n    }\n    worker.activeTimer = setTimeout(refork, MASTER_TIMEOUT);\n  });\n}\n\nmodule.exports = function (options, callback) {\n  if (typeof options === 'function') {\n    callback = options;\n    options = null;\n  }\n  options = options || {};\n  var startWhistle = function () {\n    var server = options.server;\n    if (server) {\n      if (typeof server.address === 'function') {\n        var info = server.address();\n        if (info && info.port) {\n          options.port = info.port;\n          options.host = info.address;\n        }\n      }\n      assert(options.port > 0, 'options.port of the custom server is required');\n      if (!options.storage && options.storage !== false) {\n        options.storage = '__custom_server_5b6af7b9884e1165__' + options.port;\n      }\n    }\n    var workerIndex = env.workerIndex;\n    if (options && options.cluster && workerIndex >= 0) {\n      options.storage =\n        '.' +\n        (options.storage || '') +\n        '__cluster_worker.' +\n        workerIndex +\n        '_5b6af7b9884e1165__';\n    }\n    var conf = require('./lib/config').extend(options);\n    if (!conf.cluster) {\n      return require('./lib')(callback, server);\n    }\n    var timer;\n    var activeTimeout = function () {\n      clearTimeout(timer);\n      timer = setTimeout(function () {\n        process.exit(1);\n      }, TIMEOUT);\n    };\n    process.once('SIGTERM', function () {\n      process.exit(0);\n    });\n\n    require('./lib')(function () {\n      activeTimeout();\n      process.on('message', activeTimeout);\n      process.send('1', noop);\n      setInterval(() => {\n        try {\n          process.send('1', noop);\n        } catch (e) {}\n      }, INTERVAL);\n    });\n  };\n  if (options) {\n    if (options.cluster && cluster.isMaster) {\n      if (/^\\d+$/.test(options.cluster)) {\n        options.cluster = Math.min(parseInt(options.cluster, 10), 999);\n      } else if (options.cluster) {\n        options.cluster = Math.min(os.cpus().length, 999);\n      }\n      if (options.cluster > 0) {\n        assert(!options.server, 'cannot exist options.server in cluster mode');\n        for (var i = 0; i < options.cluster; i++) {\n          forkWorker(i);\n        }\n        return;\n      }\n    }\n    if (options.debugMode) {\n      if (PROD_RE.test(options.mode)) {\n        options.debugMode = false;\n      } else {\n        env.PFORK_MODE = 'bind';\n      }\n    }\n    var config = loadConfig(options);\n    if (typeof config === 'function') {\n      var handleCallback = function (opts) {\n        opts && extend(options, opts);\n        return startWhistle();\n      };\n      if (config.length < 2) {\n        config = config(options);\n        if (likePromise(config)) {\n          return config.then(handleCallback).catch(function (err) {\n            process.nextTick(function () {\n              throw err;\n            });\n          });\n        }\n      } else {\n        config(options, handleCallback);\n      }\n    }\n    config && extend(options, config);\n  }\n  return startWhistle();\n};\n\nmodule.exports.getWhistlePath = common.getWhistlePath;\n"
  },
  {
    "path": "lib/config.js",
    "content": "var path = require('path');\nvar http = require('http');\nvar fs = require('fs');\nvar net = require('net');\nvar dns = require('dns');\nvar qs = require('querystring');\nvar LRU = require('lru-cache');\nvar httpAgent = http.Agent;\nvar httpsAgent = require('https').Agent;\nvar parseUrl = require('./util/parse-url-safe');\nvar fse = require('fs-extra2');\nvar extend = require('extend');\nvar tunnel = require('hagent').agent;\nvar socks = require('sockx');\nvar json5 = require('json5');\nvar pkgConf = require('../package.json');\nvar config = extend(exports, pkgConf);\nvar common = require('./util/common');\n\nvar customUIHost;\nvar customHostPluginMap = {};\nvar customPluginNameHost = {};\nvar WHISTLE_PLUGIN_RE = /^(?:whistle\\.)?([a-z\\d_\\-]+)$/;\nvar CMD_RE = /^[\\w]{1,12}(?:\\s+-g)?$/;\nvar httpsAgents = new LRU({ max: 360 });\nvar socksAgents = new LRU({ max: 100 });\nvar version = process.version.substring(1).split('.');\nvar disableAgent = version[0] > 10;\nvar useVerbatim = version[0] >= 17;\nvar hasIPv6First = version[0] >= 22 && version[1] >= 1;\nvar hasDnsOrder = typeof dns.setDefaultResultOrder === 'function';\nvar uid = Date.now().toString(16) + (process.pid * 10000 + Math.floor(Math.random() * 10000)).toString(16);\nvar IPV4_RE = /^([\\d.]+)(:\\d{1,5})?$/;\nvar IPV6_RE = /^\\[([\\w:]+)\\](:\\d{1,5})?$/;\nvar noop = function () {};\nvar DATA_KEY_RE = /^(clientip|clientid|tunneldata)([=.])([\\w.-]+)$/i;\nvar DNS_ORDERS = ['ipv4First', 'ipv4first', 'ipv6first', 'ipv6First', 'verbatim'];\nvar LOCAL_UI_HOST_LIST = [\n  'local.whistlejs.com',\n  'local.wproxy.org',\n  'rootca.pro'\n];\nvar variableProperties = [\n  'encrypted',\n  'sockets',\n  'dataDirname',\n  'storage',\n  'baseDir',\n  'noGlobalPlugins',\n  'pluginsDataMap',\n  'globalData',\n  'username',\n  'password',\n  'debugMode',\n  'localUIHost',\n  'extra',\n  'rules',\n  'values',\n  'dnsCache',\n  'allowDisableShadowRules',\n  'disableInstaller'\n];\nif (hasDnsOrder) {\n  if (hasIPv6First) {\n    config.verbatim = 2;\n  } else {\n    config.verbatim = 1;\n  }\n}\n\nconfig.appName = common.upperFirst(config.name);\nconfig.defaultDnsOrder = useVerbatim ? 1 : 2;\nconfig.uid = uid;\nconfig.rejectUnauthorized = false;\nconfig.enableH2 = version[0] > 12; // 支持 HTTP2 要求的最低 Node 版本\nconfig.ASSESTS_PATH = path.join(__dirname, '../assets');\nconfig.WHISTLE_POLICY_HEADER = 'x-whistle-policy';\nconfig.CLIENT_IP_HEADER = common.CLIENT_IP_HEADER;\nconfig.HTTPS_FIELD = common.HTTPS_FIELD;\nconfig.CLIENT_INFO_HEADER = 'x-whistle-client-info-' + uid;\nconfig.SHOW_LOGIN_BOX = 'x-whistle-show-login-box-' + uid;\nconfig.WEBUI_PATH = '/.whistle-path.5b6af7b9884e1165/';\nconfig.PREVIEW_PATH_RE = /\\?\\?\\?WHISTLE_PREVIEW_CHARSET=([A-Z\\d_-]+)\\?\\?\\?$/;\nconfig.PLUGIN_HOOK_NAME_HEADER = 'x-whistle-plugin-hook-name_';\nconfig.CLIENT_ID_HEADER = common.CLIENT_ID_HEADER;\nconfig.CLIENT_PORT_HEADER = common.CLIENT_PORT_HEADER;\nconfig.ALPN_PROTOCOL_HEADER = common.ALPN_PROTOCOL_HEADER;\nconfig.DISABLE_RULES_HEADER = 'x-whistle-proxy-rules-' + uid;\nconfig.PROXY_ID_HEADER = 'x-whistle-proxy-id-' + uid;\nconfig.WEBUI_HEAD = 'x-forwarded-from-whistle-' + uid;\nconfig.RES_RULES_HEAD = 'x-whistle-res-rules-' + uid;\nconfig.COMPOSER_CLIENT_ID_HEADER = 'x-whistle-client-id-' + uid;\nconfig.FROM_COM_HEADER = 'x-whistle-composer-' + uid;\nconfig.PLUGIN_HOOKS = {\n  SNI: 'sni-' + uid,\n  AUTH: 'auth-' + uid,\n  HTTP: 'http-' + uid,\n  UI: 'ui-' + uid,\n  TUNNEL: 'tunnel-' + uid,\n  TUNNEL_RULES: 'tunnel-rules-' + uid,\n  REQ_STATS: 'req-stats-' + uid,\n  RES_STATS: 'res-stats-' + uid,\n  REQ_RULES: 'req-rules-' + uid,\n  RES_RULES: 'res-rules-' + uid,\n  REQ_READ: 'req-read-' + uid,\n  REQ_WRITE: 'req-write-' + uid,\n  RES_READ: 'res-read-' + uid,\n  RES_WRITE: 'res-write-' + uid,\n  WS_REQ_READ: 'ws-req-read-' + uid,\n  WS_REQ_WRITE: 'ws-req-write-' + uid,\n  WS_RES_READ: 'ws-res-read-' + uid,\n  WS_RES_WRITE: 'ws-res-write-' + uid,\n  TUNNEL_REQ_READ: 'tunnel-req-read-' + uid,\n  TUNNEL_REQ_WRITE: 'tunnel-req-write-' + uid,\n  TUNNEL_RES_READ: 'tunnel-res-read-' + uid,\n  TUNNEL_RES_WRITE: 'tunnel-res-write-' + uid\n};\n\nvar KEEP_ALIVE_MSECS = 10000;\nvar CONN_TIMEOUT = common.CONN_TIMEOUT;\nvar DNS_ORDERS_OPTS = ['', 'verbatim', 'ipv4first', hasIPv6First ? 'ipv6first' : ''];\nvar DOMAIN_STAR_RE = /([*~]+)(\\\\.)?/g;\n\nconfig.CONN_TIMEOUT = CONN_TIMEOUT;\n\nvar getWhistlePath = common.getWhistlePath;\nvar getHostPort = common.getHostPort;\n\nvar ALL_WHISTLES_PATH = path.join(getWhistlePath(), 'all_whistles');\n\n\nfunction domainToRegExp(_, star, dot) {\n  var len = star.length;\n  var result = len > 1 ? '([^/?]*)' : '([^/?.]*)';\n  if (dot) {\n    result += '\\\\.';\n    if (len > 2) {\n      result = '(?:' + result + ')?';\n    }\n  }\n  return result;\n}\n\nfunction getDataDir(dirname) {\n  var dir = path.join(getWhistlePath(), dirname || '.' + config.name);\n  fse.ensureDirSync(dir);\n  return dir;\n}\n\nconfig.baseDir = getDataDir();\nconfig.TEMP_FILES_PATH = path.join(getWhistlePath(), 'temp_files');\nconfig.CUSTOM_PLUGIN_PATH = path.join(getWhistlePath(), 'custom_plugins');\nconfig.CUSTOM_CERTS_DIR = path.resolve(getWhistlePath(), 'custom_certs');\nconfig.SAVED_SESSIONS_PATH = path.resolve(getWhistlePath(), 'saved_sessions');\nconfig.DEV_PLUGINS_PATH = path.resolve(getWhistlePath(), 'dev_plugins');\n\ntry {\n  fse.ensureDirSync(config.TEMP_FILES_PATH);\n  var blankFile = path.join(config.TEMP_FILES_PATH, 'blank');\n  if (!fs.existsSync(blankFile)) {\n    fs.writeFileSync(blankFile, '');\n  }\n} catch (e) {}\ntry {\n  fse.ensureDirSync(config.CUSTOM_CERTS_DIR);\n} catch (e) {}\ntry {\n  fse.ensureDirSync(config.SAVED_SESSIONS_PATH);\n} catch (e) {}\n\nexports.getWhistlePath = getWhistlePath;\nexports.getDataDir = getDataDir;\nvar async_id_symbol;\nif (version[0] < 16) {\n  try {\n    async_id_symbol = process.binding('async_wrap').async_id_symbol;\n  } catch (e) {}\n}\nvar emptyHandle = {\n  asyncReset: noop,\n  getAsyncId: noop\n};\n\nfunction createAgent(agentConfig, https) {\n  var agent = new (https ? httpsAgent : httpAgent)(agentConfig);\n  if (async_id_symbol) {\n    var addRequest = agent.addRequest;\n    agent.addRequest = function (req, options) {\n      // fix: https://github.com/nodejs/node/issues/13539\n      var freeSockets = this.freeSockets[this.getName(options)];\n      if (freeSockets && freeSockets.length) {\n        var socket = freeSockets[0];\n        var handle = socket._handle;\n        if (!handle) {\n          socket._handle = emptyHandle;\n        } else if (typeof handle.asyncReset !== 'function') {\n          handle.asyncReset = noop;\n        }\n        var originalRef = socket.ref;\n        socket.ref = function () {\n          socket.ref = originalRef;\n          if (socket._handle === emptyHandle) {\n            delete socket._handle;\n          } else if (socket._handle.asyncReset === noop) {\n            delete socket._handle.asyncReset;\n          }\n          socket.ref();\n        };\n      }\n      var onSocket = req.onSocket;\n      req.onSocket = function (socket) {\n        try {\n          socket[async_id_symbol] = socket._handle.getAsyncId();\n        } catch (e) {}\n        onSocket.apply(this, arguments);\n      };\n      addRequest.apply(this, arguments);\n    };\n  }\n  return agent;\n}\n\nfunction capitalize(str) {\n  return (str && str[0].toUpperCase()) + str.substring(1);\n}\n\nfunction getHttpsAgent(options, reqOpts) {\n  var key = getCacheKey(options);\n  var agent = httpsAgents.get(key);\n  if (reqOpts) {\n    var headers = options.headers || {};\n    var proxyHeaders = {};\n    Object.keys(headers).forEach(function (key) {\n      var rawKey = key.split('-').map(capitalize).join('-');\n      proxyHeaders[rawKey] = headers[key];\n    });\n    reqOpts._tunnelProxyHeaders = proxyHeaders;\n  }\n  if (!agent) {\n    options.proxyAuth = options.auth;\n    options.rejectUnauthorized = config.rejectUnauthorized;\n    options.proxy = {\n      host: options.proxyHost,\n      port: options.proxyPort,\n      enableIntercept: options.enableIntercept,\n      keepStreamResume: options.keepStreamResume,\n      proxyTunnelPath: options.proxyTunnelPath\n    };\n    var agentName = options.isHttps ? 'httpsOverHttp' : 'httpOverHttp';\n    if (options.proxyServername) {\n      options.proxy.servername = options.proxyServername;\n      agentName += 's';\n    }\n    agent = new tunnel[agentName](options);\n    httpsAgents.set(key, agent);\n    agent.on('free', preventThrowOutError);\n  }\n  return agent;\n}\n\nexports.getHttpsAgent = getHttpsAgent;\n\nfunction getCacheKey(options, isSocks) {\n  var auth = options.auth || options.proxyAuth;\n  if (!auth) {\n    var headers = options.headers || '';\n    auth = headers['proxy-authorization'] || '';\n  }\n  var ip = (!isSocks && options.clientIp) || '';\n  return [\n    options.proxyServername ? 'https-proxy' : '',\n    options.isHttps ? 'https' : 'http',\n    options.proxyHost,\n    options.proxyPort,\n    auth,\n    ip,\n    options.proxyTunnelPath || '',\n    (options.isHttps && options.cacheKey) || ''\n  ].join(':');\n}\n\nfunction getAuths(_url) {\n  var options = typeof _url == 'string' ? parseUrl(_url) : _url;\n  if (!options || !options.auth) {\n    return [socks.auth.None()];\n  }\n\n  var auths = [];\n  options.auth.split('|').forEach(function (auth) {\n    auth = auth.trim();\n    if (auth) {\n      var index = auth.indexOf(':');\n      auths.push({\n        username: index == -1 ? auth : auth.substring(0, index),\n        password: index == -1 ? '' : auth.substring(index + 1)\n      });\n    }\n  });\n\n  return auths.length\n    ? auths.map(function (auth) {\n      return socks.auth.UserPassword(auth.username, auth.password);\n    })\n    : [socks.auth.None()];\n}\n\nexports.getAuths = getAuths;\n\nfunction setDefaultResultOrder(order) {\n  if (!hasDnsOrder) {\n    return;\n  }\n  order = DNS_ORDERS_OPTS.indexOf(order) === -1 ? DNS_ORDERS_OPTS[order] : order;\n  if (!order) {\n    return;\n  }\n  config.ipv4First = false;\n  config.ipv6First = false;\n  if (order[3] === '6') {\n    config.ipv6First = true;\n  } else if (order[3] === '4') {\n    config.ipv4First = true;\n  }\n  try {\n    if (dns.getDefaultResultOrder() !== order) {\n      dns.setDefaultResultOrder(order);\n    }\n    config.dnsOrder = DNS_ORDERS_OPTS.indexOf(order);\n    return true;\n  } catch (e) {}\n}\n\nexports.setDefaultResultOrder = setDefaultResultOrder;\n\nexports.setAuth = function (auth) {\n  if (auth) {\n    config.username = getString(auth.username);\n    var password = (config.password = getString(auth.password));\n    config.passwordHash = password && common.createHash(password);\n    setGuestAuth(auth);\n  }\n  return config;\n};\n\nexports.getPluginData = function (name) {\n  var pluginsDataMap = name && config.pluginsDataMap;\n  if (!pluginsDataMap) {\n    return;\n  }\n  name = name.substring(name.lastIndexOf('.') + 1);\n  return pluginsDataMap[name];\n};\n\nexports.setGuestAuth = function (auth) {\n  auth && setGuestAuth(auth);\n};\n\nfunction parseHost(host) {\n  if (Array.isArray(host)) {\n    return host.filter(function (h) {\n      return h && typeof h === 'string';\n    });\n  }\n  host = typeof host === 'string' ? host.trim() : '';\n  return host && host.split('|').map(getHostname);\n}\n\nexports.setUIHost = function (host) {\n  customUIHost = parseHost(host);\n};\n\nexports.setPluginUIHost = function (pluginName, host) {\n  if (!pluginName || !WHISTLE_PLUGIN_RE.test(pluginName)) {\n    return;\n  }\n  pluginName = RegExp.$1;\n  host = parseHost(host);\n  if (host) {\n    customPluginNameHost[pluginName] = host;\n    host.forEach(function (h) {\n      delete customHostPluginMap[h];\n    });\n  } else {\n    delete customPluginNameHost[pluginName];\n  }\n  customHostPluginMap = {};\n  Object.keys(customPluginNameHost).forEach(function (name) {\n    var list = customPluginNameHost[name];\n    list &&\n      list.forEach(function (h) {\n        customHostPluginMap[h] = name;\n      });\n  });\n};\n\nexports.connect = function (options, cb) {\n  return common.connectInner(options, cb, config);\n};\n\nfunction preventThrowOutError(socket) {\n  if (socket.listeners('error').indexOf(freeSocketErrorListener) === -1) {\n    socket.once('error', freeSocketErrorListener);\n  }\n}\n\nfunction freeSocketErrorListener() {\n  var socket = this;\n  socket.destroy();\n  socket.emit('agentRemove');\n  socket.removeListener('error', freeSocketErrorListener);\n}\n\nfunction resolvePath(file) {\n  if (!file || !(file = file.trim())) {\n    return file;\n  }\n\n  return /^[\\w-]+$/.test(file) ? file : path.resolve(file);\n}\n\nfunction getHostname(_url) {\n  if (typeof _url != 'string') {\n    return '';\n  }\n  if (_url.indexOf('/') != -1) {\n    return parseUrl(_url).hostname;\n  }\n  var index = _url.indexOf(':');\n  return index == -1 ? _url : _url.substring(0, index);\n}\n\nfunction getPaths(paths, isCustom) {\n  if (typeof paths === 'string') {\n    paths = paths.trim().split(/\\s*[|,;]\\s*/);\n  } else if (!Array.isArray(paths)) {\n    return;\n  }\n  var result = [];\n  paths.forEach(function (path) {\n    if (isCustom && ['self', 'buildin', 'buildIn', 'build-in'].indexOf(path) !== -1) {\n      result.push(config.CUSTOM_PLUGIN_PATH);\n    } else if (path && typeof path === 'string') {\n      result.push(path);\n    }\n  });\n  return result.length ? result : null;\n}\n\nfunction getSecureFilter(newConf) {\n  var secureFilter = newConf.secureFilter;\n  if (typeof secureFilter === 'function') {\n    return secureFilter;\n  }\n  if (!secureFilter || typeof secureFilter !== 'string') {\n    return;\n  }\n  try {\n    return require(secureFilter);\n  } catch (e) {}\n}\n\nfunction getString(str) {\n  return str && typeof str === 'string' ? str : '';\n}\n\nfunction readFileText(filepath) {\n  var text = common.readFileTextSync(filepath);\n  return text && text.trim();\n}\n\nfunction readFileBuffer(filepath, required) {\n  return common.readFileBufferSync(filepath, required === false);\n}\n\nfunction isPort(port) {\n  return /^\\d+$/.test(port) && port <= 65535 && port > 0;\n}\n\nfunction setGuestAuth(auth) {\n  if (auth.guestName === '-' && !auth.guestPassword) {\n    config.guest = {};\n  } else if (auth.guestName && auth.guestPassword) {\n    config.guest = {\n      username: auth.guestName,\n      password: auth.guestPassword\n    };\n  } else if ('guest' in auth) {\n    config.guest = auth.guest;\n  }\n}\n\nfunction parseString(str) {\n  str = str && typeof str === 'string' && str.trim();\n  if (!str) {\n    return '';\n  }\n  str = readFileText(str) || str;\n  if (!/\\s/.test(str) && str.indexOf('%') !== -1) {\n    try {\n      str = decodeURIComponent(str);\n    } catch (e) {}\n  }\n  if (/^\\{[\\s\\S]+\\}$/.test(str) || /^\\[[\\s\\S]+\\]$/.test(str)) {\n    try {\n      return json5.parse(str);\n    } catch (e) {}\n  }\n  return str;\n}\n\nfunction getPluginList(list) {\n  if (typeof list == 'string') {\n    list = list.trim().split(/\\s*[,|]\\s*/);\n  }\n  var result;\n  if (Array.isArray(list)) {\n    list.forEach(function (name) {\n      if (WHISTLE_PLUGIN_RE.test(name)) {\n        result = result || [];\n        result.push(RegExp.$1);\n      }\n    });\n  }\n  return result;\n}\n\nfunction readUIExt(uiExt) {\n  if (!uiExt) {\n    return;\n  }\n  var htmlPrepend = uiExt.htmlPrepend && readFileBuffer(uiExt.htmlPrepend, uiExt.required);\n  var htmlAppend = uiExt.htmlAppend && readFileBuffer(uiExt.htmlAppend, uiExt.required);\n  var jsPrepend = uiExt.jsPrepend && readFileBuffer(uiExt.jsPrepend, uiExt.required);\n  var jsAppend = uiExt.jsAppend && readFileBuffer(uiExt.jsAppend, uiExt.required);\n  if (htmlPrepend ||htmlAppend || jsPrepend || jsAppend) {\n    if (htmlPrepend) {\n      htmlPrepend = Buffer.concat([Buffer.from('<!DOCTYPE html>\\n'), htmlPrepend]);\n    }\n    return {\n      htmlPrepend: htmlPrepend,\n      htmlAppend: htmlAppend,\n      jsPrepend: jsPrepend,\n      jsAppend: jsAppend\n    };\n  }\n}\n\nexports.extend = function (newConf) {\n  config.pluginHostMap = {};\n  config.uiport = config.port;\n  config.ipv6Only = false;\n  var rcConf = common.readWhistleRc(newConf);\n  newConf = extend({}, rcConf, newConf);\n  if (rcConf) {\n    if (rcConf.clearPreOptions) {\n      newConf.clearPreOptions = true;\n    }\n    if (rcConf.noGlobalPlugins) {\n      newConf.noGlobalPlugins = true;\n    }\n  }\n  if (process.env.WHISTLE_MODE) {\n    newConf.mode = process.env.WHISTLE_MODE + '|' + (newConf.mode || '');\n  }\n  var useAgent;\n  var newAgentConf;\n\n  if (newConf.specialPath && typeof newConf.specialPath === 'string') {\n    config.SPEC_PATH = newConf.specialPath;\n  }\n  if (newConf.whistleName && common.isWhistleName(newConf.whistleName)) {\n    config.whistleName = newConf.whistleName;\n  }\n  config.uiExt = readUIExt(newConf.uiExt);\n  config.specialAuth = newConf.specialAuth;\n  config.uiMiddleware = newConf.uiMiddlewares || newConf.uiMiddleware;\n  newAgentConf = newConf.agentConfig;\n  if (newConf.cmdName && CMD_RE.test(newConf.cmdName)) {\n    config.cmdName = newConf.cmdName;\n  }\n  if (newConf.account && typeof newConf.account === 'string') {\n    config.account = newConf.account;\n  }\n  config.allowPluginList = getPluginList(newConf.allowPluginList);\n  config.blockPluginList = getPluginList(newConf.blockPluginList);\n\n  if (newConf.webUIPath && /^[\\w.-]+$/.test(newConf.webUIPath)) {\n    config.WEBUI_PATH = '/.' + newConf.webUIPath + config.WEBUI_PATH.substring(1);\n  }\n  if (newConf.cluster) {\n    config.headless = true;\n    config.workerIndex = process.env.workerIndex;\n    if (typeof newConf.mode !== 'string') {\n      newConf.mode = '';\n    }\n  }\n  var dnsServer = newConf.dnsServer;\n  var resolve6;\n  var dnsOptional;\n  if (common.isString(dnsServer)) {\n    dnsServer = dnsServer.trim();\n    if (common.isUrl(dnsServer)) {\n      config.dnsOverHttps = dnsServer;\n    } else {\n      dnsServer = dnsServer.split(/[|,&]/);\n    }\n  }\n\n  var allowOrigin = newConf.allowOrigin;\n  if (common.isString(allowOrigin)) {\n    allowOrigin = allowOrigin.trim().toLowerCase().split(/\\s*[|,&]\\s*/);\n    if (allowOrigin.indexOf('*') === -1) {\n      allowOrigin = allowOrigin.map(function(h) {\n        if (h.indexOf('*') === -1) {\n          return h;\n        }\n        h = common.escapeRegExp(h);\n        h = h.replace(DOMAIN_STAR_RE, domainToRegExp);\n        try {\n          return new RegExp('^' + h + '$');\n        } catch (e) {}\n      }).filter(function(h) {\n        return h;\n      });\n      if (allowOrigin.length) {\n        config.allowOrigin = allowOrigin;\n      }\n    } else {\n      config.allowAllOrigin = true;\n    }\n  }\n\n  if (dns.getServers && Array.isArray(dnsServer)) {\n    var newServers = [];\n    dnsServer.forEach(function (ip) {\n      ip = typeof ip === 'string' && ip.trim();\n      if (/^ipv6$/i.test(ip)) {\n        resolve6 = true;\n      } else if (ip === 'optional' || ip === 'default' || ip === 'fallback') {\n        dnsOptional = true;\n      } else if (net.isIP(ip)) {\n        newServers.push(ip);\n      } else if (IPV4_RE.test(ip) || IPV6_RE.test(ip)) {\n        var dnsIp = RegExp.$1;\n        var dnsPort = RegExp.$2;\n        if (net.isIP(dnsIp) && (!dnsPort || isPort(dnsPort.substring(1)))) {\n          newServers.push(ip);\n        }\n      }\n    });\n    if (newServers.length) {\n      dns.setServers(newServers);\n      config.resolve6 = resolve6;\n      config.dnsOptional = dnsOptional;\n      config.dnsServer = newServers.join();\n    }\n  }\n\n  if (newConf.inspect || newConf.inspectBrk) {\n    config.inspectMode = true;\n    process.env.PFORK_MODE = 'inline';\n  }\n  variableProperties.forEach(function (name) {\n    config[name] = newConf[name] || pkgConf[name];\n  });\n  var shadowRules = parseString(newConf.shadowRules);\n  var resolveShadowValues = function (obj) {\n    if (\n        (!obj.rules || typeof obj.rules === 'string') &&\n        obj.values &&\n        typeof obj.values !== 'string'\n      ) {\n      config.shadowRules = obj.rules || obj.rules.trim();\n      if (Object.keys(obj.values).length) {\n        config.shadowValues = obj.values;\n      }\n    } else {\n      config.rules = extend({}, newConf.rules, obj);\n    }\n  };\n  if (typeof shadowRules === 'string') {\n    config.shadowRules = shadowRules.trim();\n  } else if (Array.isArray(shadowRules)) {\n    if (typeof shadowRules[0] === 'string') {\n      config.shadowRules = shadowRules[0].trim();\n      if (shadowRules[1]) {\n        config.rules = extend({}, newConf.rules, shadowRules[1]);\n      }\n      if (shadowRules[2]) {\n        config.values = extend({}, newConf.values, shadowRules[2]);\n      }\n    } else if (shadowRules[0]) {\n      resolveShadowValues(shadowRules[0]);\n      if (shadowRules[1]) {\n        config.values = extend({}, newConf.values, shadowRules[1]);\n      }\n    } else if (shadowRules[1]) {\n      config.values = extend({}, newConf.values, shadowRules[1]);\n    }\n  } else if (shadowRules) {\n    resolveShadowValues(shadowRules);\n  }\n  var extra = newConf.extra;\n  if (common.isString(extra)) {\n    extra = /^\\s*\\{[\\w\\W]*\\}\\s*$/.test(extra) ? extra : readFileText(extra);\n    try {\n      extra = extra && JSON.parse(extra);\n      if (extra && typeof extra === 'object') {\n        config.pluginsDataMap = extend({}, config.pluginsDataMap, extra);\n      }\n    } catch (e) {}\n    extra = null;\n  }\n  var customHandler = newConf.customHandler || newConf.customHandle;\n  if (typeof customHandler === 'function') {\n    config.customHandler = customHandler;\n  }\n  if (isPort(newConf.realPort) && config.realPort != config.port) {\n    config.realPort = newConf.realPort;\n  }\n  var realHost = newConf.realHost;\n  if (typeof realHost === 'string' && (!realHost || /^[\\w.-]+$/.test(newConf.realHost))) {\n    config.realHost = newConf.realHost;\n  }\n\n  var socksPort = getHostPort(newConf.socksPort);\n  if (socksPort) {\n    config.socksPort = socksPort.port;\n    config.socksHost = socksPort.host;\n  }\n  var httpPort = getHostPort(newConf.httpPort);\n  if (httpPort) {\n    config.httpPort = httpPort.port;\n    config.httpHost = httpPort.host;\n  }\n  var httpsPort = getHostPort(newConf.httpsPort);\n  if (httpsPort) {\n    config.httpsPort = httpsPort.port;\n    config.httpsHost = httpsPort.host;\n  }\n\n  if (common.isString(newConf.host)) {\n    config.defaultHost = newConf.host;\n  }\n\n  if (typeof newConf.authKey === 'string') {\n    config.authKey = newConf.authKey;\n  }\n  if (typeof newConf.guestAuthKey === 'string') {\n    config.guestAuthKey = newConf.guestAuthKey;\n  }\n  if (newConf.reqCacheSize > 0) {\n    config.reqCacheSize = newConf.reqCacheSize;\n  }\n  if (newConf.frameCacheSize > 0) {\n    config.frameCacheSize = newConf.frameCacheSize;\n  }\n  config.allowMultipleChoice = newConf.allowMultipleChoice;\n  if (common.isString(newConf.mode)) {\n    var mode = newConf.mode.trim().split(/\\s*[|,&]\\s*/);\n    var useResolve;\n    var dnsFallback;\n    if (mode.indexOf('admin') !== -1) {\n      var admin = 'proxyServer|master|x-forwarded-proto';\n      admin = config.debugMode ? admin : `${admin}|strict|rules|disableUpdateTips|proxifier|notAllowedDisablePlugins`;\n      mode = admin.split('|').concat(mode);\n    }\n    if (mode.indexOf('multiple') !== -1) {\n      mode = 'multiEnv|disableUpdateTips|keepXFF|x-forwarded-proto'.split('|').concat(mode);\n    }\n    mode.forEach(function (m) {\n      m = m.trim();\n      if (\n          /^(pureProxy|debug|ipv6Only|master|client|diagnose|disableAuthUI|captureData|headless|strict|proxyServer|encrypted|noGzip|disableUpdateTips|proxifier2?)$/.test(m)\n        ) {\n        config[m] = true;\n      } else if(m === 'ipv6only') {\n        config.ipv6Only = true;\n      } else if (m === 'keepProxyUI') {\n        config.keepProxyUI = true;\n      } else if (m === 'agent') {\n        useAgent = true;\n      } else if (m === 'disableUIAuth') {\n        config.disableAuthUI = true;\n      } else if (m === 'showPluginReq') {\n        config.showPluginReq = config.debugMode;\n      } else if (m === 'disableCustomCerts') {\n        config.disableCustomCerts = true;\n      } else if (m === 'nohost' || m === 'multienv' || m === 'multiEnv') {\n        config[m] = true;\n        config.multiEnv = true;\n      } else if (DNS_ORDERS.indexOf(m) !== -1) {\n        setDefaultResultOrder(m.toLowerCase());\n      } else if (m === 'proxyOnly') {\n        config.pureProxy = true;\n      } else if (m === 'useMultipleRules' || m === 'enableMultipleRules') {\n        config.allowMultipleChoice = true;\n      } else if (m === 'disableMultipleRules') {\n        config.allowMultipleChoice = false;\n      } else if (\n        m === 'notAllowDisableRules' ||\n        m === 'notAllowedDisableRules'\n      ) {\n        config.notAllowedDisableRules = true;\n      } else if (m === 'disableBackOption' || m === 'disabledBackOption') {\n        config.disabledBackOption = true;\n      } else if (\n        m === 'disableMultipleOption' ||\n        m === 'disabledMultipleOption'\n      ) {\n        config.disabledMultipleOption = true;\n      } else if (\n        m === 'disableRulesOptions' ||\n        m === 'disabledRulesOptions'\n      ) {\n        config.disabledRulesOptions = true;\n        config.disabledBackOption = true;\n        config.disabledMultipleOption = true;\n        config.notAllowedDisableRules = true;\n      } else if (\n        m === 'notAllowDisablePlugins' ||\n        m === 'notAllowedDisablePlugins'\n      ) {\n        config.notAllowedDisablePlugins = true;\n      } else if (\n        m === 'notAllowEnableHTTPS' ||\n        m === 'notAllowedEnableHTTPS'\n      ) {\n        config.notAllowedEnableHTTPS = true;\n      } else if (m === 'socks') {\n        config.socksMode = true;\n      } else if (m === 'network') {\n        config.networkMode = true;\n        config.captureData = true;\n      } else if (m === 'shadowRules') {\n        config.shadowRulesMode = true;\n      } else if (m === 'shadowRulesOnly') {\n        config.shadowRulesMode = true;\n        config.disableWebUI = true;\n      } else if (m === 'plugins') {\n        config.pluginsMode = true;\n      } else if (m === 'pluginsOnly') {\n        config.pluginsOnlyMode = true;\n      } else if (m === 'rules') {\n        config.rulesMode = true;\n      } else if (m === 'rulesOnly') {\n        config.rulesOnlyMode = true;\n      } else if (m === 'httpProxy') {\n        config.pureProxy = true;\n      } else if (\n        m === 'keepXFF' ||\n        m === common.CLIENT_IP_HEADER ||\n        m === 'forwardedFor'\n      ) {\n        config.keepXFF = true;\n      } else if (m === 'x-forwarded-host') {\n        config.enableFwdHost = true;\n      } else if (m === 'x-forwarded-proto') {\n        config.enableFwdProto = true;\n      } else if (m === 'INADDR_ANY') {\n        config.INADDR_ANY = true;\n      } else if (m === 'buildIn' || m === 'build-in') {\n        process.env.PFORK_EXEC_PATH = process.execPath;\n      } else if (m === 'safe' || m === 'rejectUnauthorized') {\n        config.rejectUnauthorized = true;\n      } else if (m === 'enableRequestHeaderRules') {\n        config.enableRequestHeaderRules = true;\n      } else if (m === 'persistentCapture') {\n        config.isEnableCapture = true;\n        config.persistentCapture = true;\n      } else if (['capture', 'intercept', 'enable-capture', 'enableHttps', 'enableHTTPS', 'enableCapture'].indexOf(m) !== -1) {\n        config.isEnableCapture = true;\n      } else if (['disable-capture', 'disableCapture'].indexOf(m) !== -1) {\n        config.isEnableCapture = false;\n      } else if (['http2',  'enable-http2', 'enableHttp2', 'enable-h2', 'enableH2'].indexOf(m) !== -1) {\n        config.isEnableHttp2 = true;\n      } else if (\n        ['disable-http2', 'disableHttp2', 'disable-h2', 'disableH2'].indexOf(m) !== -1\n      ) {\n        config.isEnableHttp2 = false;\n      } else if (m === 'hideLeftBar' || m === 'hideLeftMenu') {\n        config.hideLeftMenu = true;\n      } else if (DATA_KEY_RE.test(m)) {\n        var type = RegExp.$1;\n        var override = RegExp.$2 === '=';\n        var key = RegExp.$3.toLowerCase();\n        switch (type) {\n        case 'clientip':\n          config.overCipKey = override;\n          config.cipKey = key;\n          break;\n        case 'clientid':\n          config.overCidKey = override;\n          config.cidKey = key;\n          break;\n        default:\n          config.overTdKey = override;\n          config.tdKey = key;\n          break;\n        }\n      } else if (['dnsResolve', 'dnsResolve4', 'dnsResolve6'].indexOf(m) !== -1) {\n        useResolve = true;\n        config[m] = true;\n      } else if (m === 'dnsFallback') {\n        dnsFallback = true;\n      }\n    });\n\n    if (useResolve && dnsFallback) {\n      config.dnsFallback = true;\n    }\n    if (config.headless) {\n      if (!config.pluginsMode) {\n        config.noGlobalPlugins = true;\n      }\n      config.pluginsOnlyMode = true;\n      config.disableWebUI = true;\n      delete config.rulesOnlyMode;\n    } else if (config.shadowRulesMode) {\n      config.networkMode = true;\n      delete config.rulesOnlyMode;\n      delete config.pluginsOnlyMode;\n    }\n    if (config.rulesOnlyMode) {\n      delete config.pluginsOnlyMode;\n      delete config.networkMode;\n      delete config.pluginsMode;\n      config.rulesMode = true;\n    } else if (config.pluginsOnlyMode) {\n      delete config.networkMode;\n      config.rulesMode = true;\n      config.pluginsMode = true;\n    } else if (config.networkMode) {\n      delete config.rulesMode;\n      delete config.pluginsMode;\n    } else if (config.rulesMode) {\n      delete config.pluginsMode;\n      delete config.networkMode;\n    }\n  }\n  if (/^\\d+$/.test(newConf.timeout)) {\n    config.timeout = +newConf.timeout;\n  }\n\n  setGuestAuth(newConf);\n  config.disableAllRules = newConf.disableAllRules;\n  config.disableAllPlugins = newConf.disableAllPlugins;\n  if (newConf.replaceExistRule === false) {\n    config.replaceExistRule = false;\n  } else {\n    config.replaceExistRule = newConf.replaceRules;\n  }\n  if (newConf.replaceExistValue === false) {\n    config.replaceExistValue = false;\n  } else {\n    config.replaceExistValue = newConf.replaceValues;\n  }\n  if (common.isString(newConf.certDir)) {\n    config.certDir = path.resolve(newConf.certDir);\n  }\n  var mw = newConf.middlewares || newConf.middleware;\n  if (typeof mw == 'string') {\n    config.middlewares = mw.trim().split(/\\s*,\\s*/);\n  }\n  var secureFilter = getSecureFilter(newConf);\n  if (typeof secureFilter === 'function') {\n    config.secureFilter = secureFilter;\n  }\n  if (typeof newConf.installPlugins === 'function') {\n    config.installPlugins = newConf.installPlugins;\n    config.hasInstaller = true;\n  }\n  if (typeof newConf.handleWebReq === 'function') {\n    config.handleWebReq = newConf.handleWebReq;\n  }\n  if (typeof newConf.handleUpdate === 'function') {\n    config.handleUpdate = newConf.handleUpdate;\n    config.hasUpdater = true;\n  }\n  config.notUninstallPluginPaths = getPaths(newConf.notUninstallPluginPath || newConf.notUninstallPluginPaths);\n  config.pluginPaths = getPaths(newConf.pluginPaths || newConf.pluginsPath || newConf.pluginPath);\n  config.prePluginsPath = config.projectPluginPaths = getPaths(newConf.projectPluginPaths || newConf.projectPluginsPath || newConf.projectPluginPath);\n  config.accountPluginsPath = getPaths(newConf.accountPluginPaths || newConf.accountPluginsPath || newConf.accountPluginPath);\n  config.customPluginPaths = getPaths(newConf.customPluginPaths || newConf.customPluginsPath || newConf.customPluginPath, true);\n  config.addon = getPaths(newConf.addonsPath || newConf.addonPath || newConf.addon);\n  if (config.accountPluginsPath) {\n    config.customPluginPaths = config.accountPluginsPath.concat(config.customPluginPaths || []);\n  }\n  if (config.customPluginPaths) {\n    config.prePluginsPath = (config.prePluginsPath || []).concat(config.customPluginPaths);\n  }\n\n  var pluginHost = newConf.pluginHost;\n  if (typeof pluginHost === 'string' && (pluginHost = pluginHost.trim())) {\n    pluginHost = qs.parse(pluginHost);\n    Object.keys(pluginHost).forEach(function (name) {\n      var host = pluginHost[name];\n      if (typeof host === 'string' && (host = host.trim())) {\n        host = host.toLowerCase().split(/\\s*[|,&]\\s*/);\n        host.forEach(function (h) {\n          config.pluginHostMap[h] = name;\n        });\n      }\n    });\n  }\n\n  var port = getHostPort(newConf.port);\n  if (port) {\n    config.host = port.host;\n    config.uiport = config.port = port.port;\n  }\n  if (config.disableWebUI) {\n    config.notAllowedDisablePlugins = true;\n  } else {\n    var uiPort = getHostPort(newConf.uiport);\n    if (uiPort) {\n      var curHost = config.host || config.defaultHost;\n      if (curHost && uiPort.host && curHost !== uiPort.host) {\n        config.customUIPort = true;\n      } else {\n        config.customUIPort = uiPort.port != config.port;\n      }\n      config.uiport = uiPort.port;\n      config.uihost = uiPort.host;\n    }\n  }\n\n\n  if (!config.rulesMode) {\n    config.captureData = true;\n  }\n\n  if (!config.authKey) {\n    config.authKey = 'auto_' + (Date.now() + Math.random().toFixed(6));\n  }\n  config.middlewares = Array.isArray(config.middlewares)\n    ? config.middlewares.map(resolvePath)\n    : [];\n  var uiHostList = (config.uiHostList = []);\n  if (typeof config.localUIHost === 'string') {\n    var localHostList = config.localUIHost.toLowerCase().split(/\\s*[|,&]\\s*/);\n    localHostList.forEach(function (hostname) {\n      hostname = getHostname(hostname);\n      if (hostname && LOCAL_UI_HOST_LIST.indexOf(hostname) == -1) {\n        uiHostList.push(hostname);\n      }\n    });\n    LOCAL_UI_HOST_LIST.push.apply(LOCAL_UI_HOST_LIST, uiHostList);\n    // 兼容老版本\n    config.customLocalUIHost = uiHostList[0];\n  }\n\n  config.setLocalUIHost = function (hostname) {\n    config.customLocalUIHost = getHostname(hostname);\n  };\n\n  config.localUIHost = 'local.whistlejs.com';\n\n  var isWebUIHost = function (host) {\n    if (\n      host === 'local.wproxy.org' ||\n      uiHostList.indexOf(host) !== -1 ||\n      (customUIHost && customUIHost.indexOf(host) !== -1)\n    ) {\n      return true;\n    }\n    if (config.pureProxy) {\n      return false;\n    }\n    return LOCAL_UI_HOST_LIST.indexOf(host) !== -1;\n  };\n  var isLocalUIUrl = function (url) {\n    var host = getHostname(url);\n    return (\n      isWebUIHost(host) ||\n      !!config.pluginHostMap[host] ||\n      !!customHostPluginMap[host]\n    );\n  };\n  config.isLocalUIUrl = isLocalUIUrl;\n  exports.isWebUIHost = isWebUIHost;\n  exports.getPluginNameByHost = function (host) {\n    host = getHostname(host);\n    if (isWebUIHost(host)) {\n      return;\n    }\n    return config.pluginHostMap[host] || customHostPluginMap[host];\n  };\n\n  config.sockets = Math.max(parseInt(config.sockets, 10) || 0, 1);\n  if (config.strict) {\n    config.sockets = Math.max(config.sockets, 360);\n  }\n  var agentConfig = newAgentConf || {\n    maxSockets: config.sockets,\n    keepAlive: true,\n    keepAliveMsecs: KEEP_ALIVE_MSECS,\n    maxFreeSockets: 0\n  };\n  // node 11及以上版本缓存连接有问题，先禁掉\n  disableAgent = !newAgentConf && !useAgent && (disableAgent || config.debug);\n  config.httpAgent = disableAgent ? false : createAgent(agentConfig);\n  config.httpsAgent = disableAgent ? false : createAgent(agentConfig, true);\n  config.getSocksAgent = function (options) {\n    var key = getCacheKey(options, true);\n    var agent = socksAgents.get(key);\n    if (agent) {\n      return agent;\n    }\n    extend(options, agentConfig);\n    options.proxyPort = parseInt(options.proxyPort, 10) || 1080;\n    options.rejectUnauthorized = config.rejectUnauthorized;\n    options.localDNS = false;\n    options.auths = getAuths(options);\n    agent = options.isHttps\n      ? new socks.HttpsAgent(options)\n      : new socks.HttpAgent(options);\n    socksAgents.set(key, agent);\n    agent.on('free', preventThrowOutError);\n    return agent;\n  };\n  var baseDir = config.baseDir\n    ? path.resolve(common.getHomePath(config.baseDir), config.dataDirname)\n    : (config.whistleName ? path.join(ALL_WHISTLES_PATH, config.whistleName) : getDataDir(config.dataDirname));\n  var customDirs = path.join(baseDir, 'custom_dirs');\n  config.baseDir = baseDir;\n  config.storage = config.storage && encodeURIComponent(config.storage);\n  if (config.storage) {\n    baseDir = path.join(customDirs, config.storage);\n  }\n  if (config.whistleName) {\n    config.CUSTOM_PLUGIN_PATH = path.join(baseDir, 'custom_plugins');\n    config.CUSTOM_CERTS_DIR = path.resolve(baseDir, 'custom_certs');\n    config.SAVED_SESSIONS_PATH = path.resolve(baseDir, 'saved_sessions');\n    fse.ensureDirSync(config.CUSTOM_PLUGIN_PATH);\n    fse.ensureDirSync(config.CUSTOM_CERTS_DIR);\n    fse.ensureDirSync(config.SAVED_SESSIONS_PATH);\n  }\n  fse.ensureDirSync(baseDir);\n  config.baseDirHash = common.createHash(baseDir);\n  if (config.password) {\n    config.passwordHash = common.createHash(config.password);\n  }\n  config.rulesDir = path.join(baseDir, 'rules');\n  config.valuesDir = path.join(baseDir, 'values');\n  config.propertiesDir = path.join(baseDir, 'properties');\n  if (config.storage && newConf.copy) {\n    var copyDir =\n      typeof newConf.copy == 'string' && encodeURIComponent(newConf.copy);\n    if (copyDir !== config.storage) {\n      var dataDir = copyDir ? path.join(customDirs, copyDir) : config.baseDir;\n      var rulesDir = path.join(dataDir, 'rules');\n      var valuesDir = path.join(dataDir, 'values');\n      var propsDir = path.join(dataDir, 'properties');\n      fse.ensureDirSync(rulesDir);\n      fse.ensureDirSync(valuesDir);\n      fse.ensureDirSync(propsDir);\n      fse.copySync(rulesDir, config.rulesDir);\n      fse.copySync(valuesDir, config.valuesDir);\n      fse.copySync(propsDir, config.propertiesDir);\n    }\n  }\n  var clientIdFile = path.join(config.baseDir, '.clientid');\n  var clientId = readFileText(clientIdFile);\n  if (!clientId) {\n    clientId = [\n      Date.now(),\n      Math.random(),\n      Math.random(),\n      Math.random(),\n      Math.random(),\n      Math.random()\n    ].join();\n    fs.writeFileSync(clientIdFile, clientId);\n  }\n  if (config.whistleName) {\n    clientId += '/' + config.whistleName;\n  }\n  config.clientId = clientId;\n  config.clientIdNo = common.getStatSync(clientIdFile).ino + '';\n  config.setModified = function (clientId, isRules) {\n    if (isRules) {\n      config.mrulesClientId = clientId || '';\n      config.mrulesTime = Date.now();\n    } else {\n      config.mvaluesClientId = clientId || '';\n      config.mvaluesTime = Date.now();\n    }\n  };\n  return config;\n};\n"
  },
  {
    "path": "lib/handlers/error-handler.js",
    "content": "var util = require('../util');\n\nmodule.exports = function (err, req, res, _) {\n  res.response(util.wrapGatewayError(util.getErrorStack(err)));\n};\n"
  },
  {
    "path": "lib/handlers/file-proxy.js",
    "content": "var fs = require('fs');\nvar extend = require('extend');\nvar util = require('../util');\nvar mime = require('mime');\nvar qs = require('querystring');\nvar rulesMgr = require('../rules');\nvar protoMgr = require('../rules/protocols');\nvar pluginMgr = require('../plugins');\n\nvar CRLF_RE = /\\r\\n|\\r|\\n/g;\nvar RAW_FILE_RE = /rawfile/;\nvar HEADERS_SEP_RE = /(\\r?\\n(?:\\r\\n|\\r|\\n)|\\r\\r\\n?)/;\nvar MAX_HEADERS_SIZE = 256 * 1024;\nvar TPL_RE = /(?:dust|tpl|jsonp):$/;\nvar VAR_RE = /\\{\\S+\\}/;\nvar QUERY_VAR_RE = /\\$?(?:\\{\\{([\\w$-]+)\\}\\}|\\{([\\w$-]+)\\})/g;\nvar UTF8_OPTIONS = { encoding: 'utf8' };\nvar ENABLE_OPTIONS = { enable: true };\nvar SLASH_RE = /\\\\+/g;\nvar QUOTE_RE = /\"/g;\nvar JS_RE = /^js:/i;\nvar HTML_RE = /^html:/i;\nvar REPLACE_RE = /^replace:/i;\nvar BLANK_RE = /\\s/g;\nvar HASH_RE = /#.*$/;\nvar CROSS_RE = /^x(s)?/;\nvar HTTP_RE = /^http:/;\nvar NOT_A_FILE_ERR = new Error('Not a file');\nvar INVALID_PATH = '(Path contains parent directory notation \\'..\\')';\n\nfunction urlToStr(url) {\n  return url.replace(SLASH_RE, '').replace(QUOTE_RE, '\\\\$&').replace(BLANK_RE, ' ');\n}\n\nfunction isRawFileProtocol(protocol) {\n  return RAW_FILE_RE.test(protocol);\n}\n\nfunction readFiles(files, callback) {\n  var file = files.shift();\n  var execCallback = function (err, stat) {\n    if (!err && stat && stat.isFile()) {\n      callback(null, file, stat.size);\n    } else if (file && file.originalKey) {\n      pluginMgr.requestBin(file, function(buffer, err, res) {\n        file = file.originalKey;\n        callback(err, file, buffer ? buffer.length : 0, buffer, res);\n      });\n    } else if (files.length) {\n      readFiles(files, callback);\n    } else {\n      callback(err || NOT_A_FILE_ERR, file == null ? INVALID_PATH : file);\n    }\n  };\n\n  !file || typeof file != 'string'\n    ? execCallback()\n    : util.getStat(file, execCallback);\n}\n\nfunction parseRes(str, rawHeaderNames, fromValue) {\n  if (!str) {\n    return {\n      statusCode: 200,\n      headers: {}\n    };\n  }\n  var headers = str.split(CRLF_RE);\n  var statusLine = headers.shift().trim().split(/\\s+/g);\n  headers = util.parseHeaders(headers, rawHeaderNames);\n  if (fromValue) {\n    delete headers['content-encoding'];\n  }\n  return {\n    statusCode: statusLine[1] || 200,\n    headers: headers\n  };\n}\n\nfunction addLength(reader, length) {\n  reader.headers['content-length'] = length;\n}\n\nfunction getRawResByValue(body) {\n  var headers;\n  if (HEADERS_SEP_RE.test(body)) {\n    var crlfStr = RegExp.$1;\n    var index = body.indexOf(crlfStr);\n    headers = body.substring(0, index);\n    body = body.substring(index + crlfStr.length);\n  }\n  var rawHeaderNames = {};\n  var reader = parseRes(headers, rawHeaderNames, true);\n  reader.rawHeaderNames = rawHeaderNames;\n  reader.body = body;\n  addLength(reader, body ? Buffer.byteLength(body) : 0);\n  return reader;\n}\n\nfunction getRawResByPath(protocol, path, req, size, callback, body) {\n  var isRawFile = isRawFileProtocol(protocol);\n  var range = isRawFile ? undefined : util.parseRange(req, req.localFileSize);\n  var reader;\n  if (body == null) {\n    reader = fs.createReadStream(path, range);\n  } else {\n    reader = util.createTransform();\n    if (!body || !range) {\n      reader.end(body);\n    } else {\n      reader.end(body.slice(range.start, range.end + 1));\n    }\n  }\n  var rawHeaderNames = (reader.rawHeaderNames = rawHeaderNames);\n  if (isRawFile) {\n    var buffer;\n    var response = function (err, crlf) {\n      reader.removeAllListeners();\n      reader.on('error', util.noop);\n      var stream = reader;\n      reader = util.createTransform();\n      if (err) {\n        var stack = util.getErrorStack(err);\n        reader.statusCode = 500;\n        reader.push(stack);\n        reader.push(null);\n        size = Buffer.byteLength(stack);\n      } else {\n        if (crlf) {\n          crlf = util.toBuffer(crlf);\n          var index = buffer.indexOf(crlf);\n          if (index != -1) {\n            extend(\n              reader,\n              parseRes(buffer.slice(0, index) + '', rawHeaderNames)\n            );\n            var headerLen = index + crlf.length;\n            size -= headerLen;\n            buffer = buffer.slice(headerLen);\n          }\n        }\n        buffer && reader.push(buffer);\n        stream.on('error', function (err) {\n          reader.emit('error', err);\n        });\n        stream.pipe(reader);\n      }\n      callback(reader, null, size);\n    };\n\n    reader.on('data', function (data) {\n      buffer = buffer ? Buffer.concat([buffer, data]) : data;\n      if (HEADERS_SEP_RE.test(buffer + '')) {\n        response(null, RegExp.$1);\n      } else if (buffer.length > MAX_HEADERS_SIZE) {\n        response();\n      }\n    });\n    reader.on('error', response);\n    reader.on('end', response);\n  } else {\n    callback(reader, range, size);\n  }\n}\n\nfunction addRangeHeaders(res, range, size) {\n  if (!range) {\n    return;\n  }\n  var headers = res.headers;\n  headers['content-range'] =\n    'bytes ' + (range.start + '-' + range.end) + '/' + size;\n  headers['accept-ranges'] = 'bytes';\n  headers['content-length'] = range.end - range.start + 1;\n  res.statusCode = 206;\n}\n\nfunction isAutoCors(req, rule) {\n  if (rule.lineProps.disableAutoCors || rule.lineProps.disabledAutoCors) {\n    return false;\n  }\n  return !req.disable.autoCors && req.headers.origin;\n}\n\nfunction sendResponse(rule, res, reader, req) {\n  if (isAutoCors(req, rule)) {\n    util.setResCors(reader, ENABLE_OPTIONS, req);\n  }\n  rule.isLoc = 1;\n  res.response(reader);\n}\n\nfunction handleLocHref(req, rule, handleRes) {\n  var body = util.getMatcherValue(rule) || '';\n  var isJs = JS_RE.test(body);\n  var isHtml = HTML_RE.test(body);\n  var isReplace = REPLACE_RE.test(body);\n  if (isJs) {\n    body = body.substring(3);\n  } else if (isHtml) {\n    body = body.substring(5);\n  } else if (isReplace) {\n    body = body.substring(8);\n  } else {\n    isJs = req.headers['sec-fetch-dest'] === 'script';\n  }\n  var type = isJs ? 'application/javascript' : 'text/html';\n  if (body) {\n    body = urlToStr(body);\n    if (util.compareUrl(body.replace(HASH_RE, ''), req.fullUrl)) {\n      return false;\n    }\n    if (isReplace) {\n      body = 'window.location.replace(\"' + body + '\");';\n    } else {\n      body = 'window.location.href = \"' + body + '\";';\n    }\n    if (!isJs) {\n      body = '<script>' + body + '</script>';\n    }\n  }\n\n  handleRes({\n    statusCode: 200,\n    body: body,\n    headers: {\n      'content-type': type + '; charset=utf-8'\n    }\n  });\n  return true;\n}\n\nmodule.exports = function (req, res, next) {\n  var options = req.options;\n  var config = this.config;\n  var protocol = options && options.protocol;\n  if (!protoMgr.isFileProxy(protocol)) {\n    return next();\n  }\n  var rules = req.rules;\n  var rule = rules.rule;\n  if (protoMgr.isLocHref(protocol)) {\n    if (handleLocHref(req, rule, render)) {\n      return;\n    }\n    req.isWebProtocol = true;\n    req.options = util.parseUrl(req.fullUrl);\n    return next();\n  }\n  if (isAutoCors(req, rule) && req.method === 'OPTIONS') {\n    var optsRes = util.wrapResponse({ statusCode: 200  }, rule.matcher);\n    return sendResponse(rule, res, optsRes, req);\n  }\n  var isTpl = TPL_RE.test(protocol);\n  var defaultType = mime.lookup(\n    util.getPureUrl(req.fullUrl),\n    'text/html'\n  );\n  delete rules.proxy;\n  delete rules.host;\n  if (rule.value) {\n    var body = util.removeProtocol(rule.value, true);\n    var isRawFile = isRawFileProtocol(protocol);\n    var reader = isRawFile\n      ? getRawResByValue(body)\n      : {\n        statusCode: 200,\n        body: body,\n        headers: {\n          'content-type':\n              (rule.key ? mime.lookup(rule.key, defaultType) : defaultType) +\n              '; charset=utf-8'\n        }\n      };\n\n    if (isTpl) {\n      reader.realUrl = rule.matcher;\n      render(reader);\n    } else {\n      if (!isRawFile) {\n        var size = Buffer.byteLength(body);\n        var range = util.parseRange(req, size);\n        if (range) {\n          body = Buffer.from(body);\n          reader.body = body.slice(range.start, range.end + 1);\n          addRangeHeaders(reader, range, size);\n        } else {\n          addLength(reader, size);\n        }\n      }\n      reader = util.wrapResponse(reader, rule.matcher);\n      sendResponse(rule, res, reader, req);\n    }\n    return;\n  }\n\n  readFiles(util.getRuleFiles(rule, req), function (err, path, size, buffer, svrRes) {\n    if (err) {\n      if (CROSS_RE.test(protocol)) {\n        var fullUrl = RegExp.$1 ? req.fullUrl.replace(HTTP_RE, 'https:') : req.fullUrl;\n        extend(options, util.parseUrl(fullUrl));\n        return next();\n      }\n      var is502 = err.code > 0 && err.code != 404;\n      var notFound = util.wrapResponse({\n        statusCode: is502 ? 502 : 404,\n        body: is502 ? util.encodeHtml(err.message) : 'Not found ' + (rule.key ? 'key' : 'file') +\n          ' <strong>' + util.encodeHtml(rule.key || path) + '</strong>',\n        headers: { 'content-type': 'text/html; charset=utf-8' }\n      }, rule.matcher);\n      return sendResponse(rule, res, notFound, req);\n    }\n    var type = buffer != null && svrRes && svrRes.headers && svrRes.headers['x-whistle-response-type'];\n    type = type || mime.lookup(rule._suffix || path, defaultType);\n    var headers = {\n      server: config.appName,\n      'content-type': type + (util.isText(type) ? '; charset=utf-8' : '')\n    };\n\n    if (isTpl) {\n      var reader = {\n        statusCode: 200,\n        realUrl: path,\n        headers: headers\n      };\n      if (buffer != null) {\n        reader.body = buffer + '';\n        render(reader);\n      } else {\n        fs.readFile(path, UTF8_OPTIONS, function (err, data) {\n          if (err) {\n            return util.emitError(req, err);\n          }\n          reader.body = data;\n          render(reader);\n        });\n      }\n    } else {\n      req.localFileSize = size;\n      getRawResByPath(\n        protocol,\n        path,\n        req,\n        size,\n        function (reader, range, realSize) {\n          reader.realUrl = path;\n          reader.statusCode = reader.statusCode || 200;\n          reader.headers = reader.headers || headers;\n          addRangeHeaders(reader, range, size);\n          !range && addLength(reader, realSize);\n          sendResponse(rule, res, reader, req);\n        },\n        buffer\n      );\n    }\n  });\n\n  function render(reader) {\n    if (reader.body) {\n      if (VAR_RE.test(reader.body)) {\n        var data = util.getQueryString(req.fullUrl);\n        data = data && qs.parse(data);\n        if (data) {\n          reader.body = reader.body.replace(QUERY_VAR_RE, function (all, matched1, matched2) {\n            if (all[0] === '$') {\n              return all;\n            }\n            var value = data[matched1 || matched2];\n            return value === undefined ? all : util.getQueryValue(value);\n          }\n          );\n        }\n        reader.body = rulesMgr.resolveTplVar(reader.body, req);\n      }\n      addLength(reader, Buffer.byteLength(reader.body));\n    } else {\n      addLength(reader, 0);\n    }\n    reader = util.wrapResponse(reader, reader.realUrl);\n    sendResponse(rule, res, reader, req);\n  }\n};\n"
  },
  {
    "path": "lib/handlers/http-proxy.js",
    "content": "var util = require('../util');\nvar pluginMgr = require('../plugins');\nvar protoMgr = require('../rules/protocols');\n\nvar handleReq = function (req, next) {\n  if (req.isWebProtocol) {\n    req.request(req.options);\n  } else {\n    next(new Error('Unsupported protocol ' + req.options.protocol));\n  }\n};\n\nmodule.exports = function (req, res, next) {\n  var protocol = req.options && req.options.protocol;\n  req.isWebProtocol = protoMgr.isWebProtocol(protocol);\n  var plugin = !req.isWebProtocol && pluginMgr.getPlugin(protocol);\n  if (!plugin) {\n    return handleReq(req, next);\n  }\n\n  pluginMgr.loadPlugin(req.isPluginReq && !req._isPureInternalReq ? null : plugin, function (err, ports) {\n    if (err) {\n      res.response(util.wrapGatewayError(err));\n      return;\n    }\n\n    if (!ports.port) {\n      req.isWebProtocol = true;\n      req.options = util.parseUrl(req.fullUrl);\n      return handleReq(req, next);\n    }\n    req.customParser = util.getParserStatus(req);\n    if (req.customParser) {\n      req.initCustomParser();\n    }\n    var fullUrl = req.options._realUrl || req.fullUrl;\n    var options = util.parseUrl(fullUrl);\n    pluginMgr.addSessionInfo(req, req.rules);\n    options.protocol = 'http:';\n    options.host = '127.0.0.1';\n    options.port = ports.port;\n    options.href = util.changePort(fullUrl, ports.port);\n    options.localDNS = true;\n    options.isPlugin = true;\n    req.request(options);\n  });\n};\n"
  },
  {
    "path": "lib/handlers/index.js",
    "content": "module.exports = [\n  './file-proxy',\n  './http-proxy',\n  './error-handler'\n].map(function (mod) {\n  return require(mod);\n});\n"
  },
  {
    "path": "lib/https/ca.js",
    "content": "var forge = require('node-forge');\nvar fs = require('fs');\nvar net = require('net');\nvar path = require('path');\nvar crypto = require('crypto');\nvar LRU = require('lru-cache');\nvar hagent = require('hagent');\nvar extend = require('extend');\nvar h2 = require('./h2');\nvar createSecureContext =\n  require('tls').createSecureContext || crypto.createCredentials;\nvar util = require('../util');\nvar config = require('../config');\nvar common = require('../util/common');\nvar rulesUtil = require('../rules/util');\n\nvar pki = forge.pki;\nvar CUR_VERSION = process.version;\nvar requiredVersion = parseInt(CUR_VERSION.slice(1), 10) >= 6;\nvar HTTPS_DIR = mkdir(path.join(config.getDataDir(), 'certs'));\nvar ROOT_NEW_KEY_FILE = path.join(HTTPS_DIR, 'root_new.key');\nvar ROOT_NEW_CRT_FILE = path.join(HTTPS_DIR, 'root_new.crt');\nvar CUSTOM_CERTS_DIR = config.disableCustomCerts\n  ? null\n  : config.CUSTOM_CERTS_DIR;\nvar useNewKey =\n  fs.existsSync(ROOT_NEW_KEY_FILE) && fs.existsSync(ROOT_NEW_CRT_FILE);\nvar ROOT_KEY_FILE = useNewKey\n  ? ROOT_NEW_KEY_FILE\n  : path.join(HTTPS_DIR, 'root.key');\nvar ROOT_CRT_FILE = useNewKey\n  ? ROOT_NEW_CRT_FILE\n  : path.join(HTTPS_DIR, 'root.crt');\nvar customCertDir = config.certDir;\nvar customPairs = {};\nvar customCertsInfo = {};\nvar customCertsFiles = {};\nvar allCustomCerts = {};\nvar customCertCount = 0;\nvar cachePairs = new LRU({ max: 5120 });\nvar certsCache = new LRU({ max: 256 });\nvar remoteCerts = new LRU({ max: 1280 });\nvar ILEGAL_CHAR_RE = /[^a-z\\d-]/i;\n// https://cloud.tencent.com/developer/ask/sof/58155\nvar RANDOM_SERIAL = '/' + Date.now() + '/' + process.pid + '/' + Math.floor(Math.random() * 100);\nvar ONE_DAY = 1000 * 60 * 60 * 24;\nvar MIN_DATE = ONE_DAY * 20;\nvar CLEAR_CERTS_INTERVAL = ONE_DAY * 20;\nvar MAX_INNTERFAL = 16;\nvar PORT_RE = /:\\d*$/;\nvar pureCertFiles = {};\nvar customRoot;\nvar ROOT_KEY, ROOT_CRT;\nvar rootKey, rootCrt;\nvar ILLEGAL_PATH_RE = /[/\\\\]/;\n\nexports.remoteCerts = remoteCerts;\nexports.createSecureContext = createSecureContext;\nexports.CUSTOM_CERTS_DIR = CUSTOM_CERTS_DIR;\n\nfunction isRootCa(name) {\n  return name !== 'root';\n}\n\nfunction checkFilename(name) {\n  return name && !ILLEGAL_PATH_RE.test(name) && isRootCa(name);\n}\n\nfunction resetAllCerts() {\n  cachePairs.reset();\n  certsCache.reset();\n}\n\n// When delay is larger than 2147483647 or less than 1, the delay will be set to 1. Non-integer delays are truncated to an integer.\nvar intervalCount = 0;\nvar timer = setInterval(function () {\n  if (++intervalCount >= MAX_INNTERFAL) {\n    intervalCount = 0;\n    resetAllCerts();\n  }\n}, CLEAR_CERTS_INTERVAL);\n\nif (timer && typeof timer.unref === 'function') {\n  timer.unref();\n}\n\nif (!useNewKey && requiredVersion && !checkCertificate()) {\n  try {\n    fs.unlinkSync(ROOT_KEY_FILE);\n    fs.unlinkSync(ROOT_CRT_FILE);\n  } catch (e) {}\n}\n\nfunction mkdir(path) {\n  !fs.existsSync(path) && fs.mkdirSync(path);\n  return path;\n}\n\nfunction isExpiredCert(crt) {\n  var now = Date.now();\n  try {\n    var startDate = new Date(crt.notBefore);\n    var endDate = new Date(crt.notAfter);\n    return startDate.getTime() >= now || endDate.getTime() <= now;\n  } catch (e) {}\n}\n\nfunction checkCertificate() {\n  try {\n    var crt = pki.certificateFromPem(fs.readFileSync(ROOT_CRT_FILE));\n    if (crt.publicKey.n.toString(2).length < 2048 || isExpiredCert(crt.validity)) {\n      return false;\n    }\n    return /^whistle\\.\\d+$/.test(getCommonName(crt));\n  } catch (e) {}\n  return true;\n}\n\nfunction getCommonName(crt) {\n  var attrs = crt.issuer && crt.issuer.attributes;\n  if (Array.isArray(attrs)) {\n    for (var i = 0, len = attrs.length; i < len; i++) {\n      var attr = attrs[i];\n      if (attr && attr.name === 'commonName') {\n        return attr.value;\n      }\n    }\n  }\n  return '';\n}\n\nfunction getDomain(hostname) {\n  if (getCacheCert(hostname) || net.isIP(hostname)) {\n    return hostname;\n  }\n  var list = hostname.split('.');\n  var prefix = list[0];\n  list[0] = '*';\n  var wildDomain = list.join('.');\n  if (getCacheCert(wildDomain)) {\n    return wildDomain;\n  }\n  var len = list.length;\n  if (len < 3) {\n    return hostname;\n  }\n  if (\n    len > 3 ||\n    ILEGAL_CHAR_RE.test(prefix) ||\n    list[1].length > 3 ||\n    list[2] === 'com' ||\n    list[2] === 'net' ||\n    list[1] === 'url'\n  ) {\n    // For tencent cdn\n    return wildDomain;\n  }\n\n  return hostname;\n}\n\nexports.getDomain = getDomain;\nexports.existsCustomCert = function (hostname) {\n  if (!customCertCount) {\n    return false;\n  }\n  hostname = hostname.replace(PORT_RE, '');\n  var cert = customPairs[hostname];\n  if (cert) {\n    return true;\n  }\n  hostname = hostname.split('.');\n  hostname[0] = '*';\n  return customPairs[hostname.join('.')];\n};\n\nexports.hasCustomCerts = function () {\n  return customCertCount;\n};\n\nfunction getCacheCert(hostname) {\n  return (\n    customPairs[hostname] ||\n    cachePairs.get(hostname) ||\n    certsCache.get(hostname)\n  );\n}\n\nfunction createSelfCert(hostname) {\n  // https://blog.csdn.net/OnlyLove_/article/details/125815813\n  var serialNumber =\n    crypto\n      .createHash('sha1')\n      .update(hostname + RANDOM_SERIAL, 'binary')\n      .digest('hex');\n  serialNumber = (parseInt(serialNumber[0], 16) % 8) + serialNumber.substring(1);\n  var cert = createCert(\n    pki.setRsaPublicKey(ROOT_KEY.n, ROOT_KEY.e),\n    serialNumber,\n    true\n  );\n  cert.setSubject([\n    {\n      name: 'commonName',\n      value: hostname\n    }\n  ]);\n  cert.setIssuer(ROOT_CRT.subject.attributes);\n  cert.setExtensions([\n    {\n      name: 'subjectAltName',\n      altNames: [\n        net.isIP(hostname)\n          ? {\n            type: 7,\n            ip: hostname\n          }\n          : {\n            type: 2,\n            value: hostname\n          }\n      ]\n    }\n  ]);\n  cert.sign(ROOT_KEY, forge.md.sha256.create());\n  return {\n    key: pki.privateKeyToPem(ROOT_KEY),\n    cert: pki.certificateToPem(cert)\n  };\n}\n\nexports.createCertificate = function (hostname) {\n  hostname = getDomain(hostname);\n  var cert = cachePairs.get(hostname); // 确保使用自己生成的证书，防止把用户证书下载出去\n  if (!cert) {\n    cert = createSelfCert(hostname);\n    certsCache.set(hostname, cert);\n  }\n  return cert;\n};\n\nfunction parseCert(cert) {\n  var pem = pki.certificateFromPem(cert.cert);\n  var altNames = getAltNames(pem.extensions);\n  if (!altNames || !altNames.length) {\n    return;\n  }\n  return { cert: cert, altNames: altNames, validity: pem.validity };\n}\n\nfunction parseAllCustomCerts() {\n  var pairs = {};\n  var certsInfo = {};\n  var certFiles = {};\n  var keys = Object.keys(allCustomCerts).sort(function (key1, key2) {\n    return util.compare(\n      allCustomCerts[key1].cert.mtime,\n      allCustomCerts[key2].cert.mtime\n    );\n  });\n  keys.forEach(function (filename) {\n    var info = allCustomCerts[filename];\n    var cert = info.cert;\n    var mtime = cert.mtime;\n    var validity = info.validity;\n    var altNames = info.altNames;\n    var dnsName = [];\n    var disabled = (isRootCa(filename) && rulesUtil.isDisabledCertFile(filename)) || undefined;\n    altNames.forEach(function (item) {\n      if ((item.type === 2 || item.type === 7) && !pairs[item.value]) {\n        if (!disabled) {\n          var preCert = customPairs[item.value];\n          if (preCert && preCert.key === cert.key && preCert.cert === cert.cert) {\n            if (preCert.mtime < mtime) {\n              preCert.mtime = mtime;\n            }\n            pairs[item.value] = preCert;\n          } else {\n            pairs[item.value] = cert;\n          }\n        }\n        dnsName.push(item.value);\n        certsInfo[item.value] = extend(\n          { filename: filename, type: cert.type, mtime: mtime, domain: item.value, disabled: disabled },\n          validity\n        );\n      }\n    });\n    if (dnsName.length) {\n      certFiles[filename] = extend(\n        { mtime: mtime, type: cert.type, dir: cert.dir, dnsName: dnsName.join(', '), disabled: disabled },\n        validity\n      );\n    }\n  });\n  certFiles.root = certFiles.root || customCertsFiles.root;\n  customPairs = pairs;\n  customCertsInfo = certsInfo;\n  customCertsFiles = certFiles;\n  customCertCount = Object.keys(customPairs).length;\n  checkExpired();\n}\n\nfunction loadCustomCerts(certDir, isCustom) {\n  if (!certDir) {\n    return;\n  }\n  var certs = {};\n  pureCertFiles = {};\n  try {\n    fs.readdirSync(certDir).forEach(function (name) {\n      if (!/^(.+)\\.(crt|cer|pem|key)$/.test(name)) {\n        return;\n      }\n      var filename = RegExp.$1;\n      var type = RegExp.$2;\n      var cert = (certs[filename] = certs[filename] || {});\n      var isCert = type !== 'key';\n      try {\n        var filePath = path.join(certDir, name);\n        var mtime = common.getStatSync(filePath).mtime.getTime();\n        if (isCert && cert.type && (cert.mtime == null || cert.mtime >= mtime)) {\n          return;\n        }\n        cert.dir = certDir;\n        if (isCert) {\n          cert.mtime = mtime;\n          cert.type = type;\n          type = 'cert';\n        }\n        cert[type] = fs.readFileSync(filePath, { encoding: 'utf8' });\n      } catch (e) {}\n    });\n  } catch (e) {}\n  var rootCA = certs.root;\n  delete certs.root;\n  if (rootCA && rootCA.key && rootCA.cert && !customRoot) {\n    customRoot = rootCA;\n    ROOT_KEY_FILE = path.join(certDir, 'root.key');\n    ROOT_CRT_FILE = path.join(certDir, 'root.' + rootCA.type);\n  }\n  Object.keys(certs).filter(function (key) {\n    var cert = certs[key];\n    if (cert && cert.mtime != null && cert.key && cert.cert) {\n      pureCertFiles[key] = {\n        type: cert.type,\n        name: key,\n        key: cert.key,\n        cert: cert.cert\n      };\n      try {\n        cert = parseCert(cert);\n        if (cert) {\n          allCustomCerts[isCustom ? 'z/' + key : key] = cert;\n        }\n      } catch (e) {}\n    }\n  });\n}\n\nfunction getAltNames(exts) {\n  for (var i = 0, len = exts.length; i < len; i++) {\n    var item = exts[i];\n    if (item.name === 'subjectAltName') {\n      return Array.isArray(item.altNames) && item.altNames.filter(util.noop);\n    }\n  }\n}\n\nfunction resetRootCA() {\n  var cert = createCACert();\n  var key = pki.privateKeyToPem(cert.key).toString();\n  var crt = pki.certificateToPem(cert.cert).toString();\n  fs.writeFileSync(ROOT_KEY_FILE, key);\n  fs.writeFileSync(ROOT_CRT_FILE, crt);\n  rootKey = key;\n  rootCrt = crt;\n  ROOT_KEY = cert.key;\n  ROOT_CRT = cert.cert;\n}\n\nfunction createRootCASafe() {\n  if (isExpiredCert(ROOT_CRT.validity)) {\n    try {\n      resetRootCA();\n      resetAllCerts();\n    } catch (e) {}\n  }\n}\n\nfunction createRootCA() {\n  allCustomCerts = {};\n  loadCustomCerts(customCertDir, true);\n  loadCustomCerts(CUSTOM_CERTS_DIR);\n  parseAllCustomCerts();\n  if (ROOT_KEY && ROOT_CRT) {\n    return;\n  }\n  try {\n    ROOT_KEY = fs.readFileSync(ROOT_KEY_FILE);\n    ROOT_CRT = fs.readFileSync(ROOT_CRT_FILE);\n    rootKey = ROOT_KEY.toString();\n    rootCrt = ROOT_CRT.toString();\n  } catch (e) {\n    ROOT_KEY = ROOT_CRT = null;\n  }\n\n  if (ROOT_KEY && ROOT_CRT && ROOT_KEY.length && ROOT_CRT.length) {\n    ROOT_KEY = pki.privateKeyFromPem(ROOT_KEY);\n    ROOT_CRT = pki.certificateFromPem(ROOT_CRT);\n    if (customRoot) {\n      customCertsFiles.root = extend(\n        {\n          mtime: customRoot.mtime,\n          dir: customRoot.dir,\n          type: customRoot.type,\n          dnsName: ''\n        },\n        ROOT_CRT.validity\n      );\n    }\n    try {\n      var altNames = getAltNames(ROOT_CRT.extensions);\n      var dnsName = [];\n      altNames.forEach(function (item) {\n        if (\n          (item.type === 2 || item.type === 7) &&\n          dnsName.indexOf(item.value) === -1\n        ) {\n          dnsName.push(item.value);\n        }\n      });\n      customCertsFiles.root.dnsName = dnsName.join(', ');\n    } catch (e) {}\n  } else {\n    resetRootCA();\n  }\n}\n\nfunction createCACert(opts) {\n  opts = opts || {};\n  var keys = pki.rsa.generateKeyPair(requiredVersion ? 2048 : 1024);\n  var cert = createCert(keys.publicKey);\n  var now = Date.now() + common.padLeft(Math.floor(Math.random() * 1000), 3);\n  var attrs = [\n    {\n      name: 'commonName',\n      value: opts.commonname || opts.commonName || 'whistle.' + now\n    },\n    {\n      name: 'countryName',\n      value: opts.countryname || opts.countryName || 'CN'\n    },\n    {\n      shortName: 'ST',\n      value: opts.st || opts.ST || 'ZJ'\n    },\n    {\n      name: 'localityName',\n      value: opts.localityname || opts.localityName || 'HZ'\n    },\n    {\n      name: 'organizationName',\n      value: opts.organizationname || opts.organizationName || now + '.wproxy.org'\n    },\n    {\n      shortName: 'OU',\n      value: opts.ou || opts.OU || 'wproxy.org'\n    }\n  ];\n\n  cert.setSubject(attrs);\n  cert.setIssuer(attrs);\n  cert.setExtensions([\n    {\n      name: 'basicConstraints',\n      cA: true\n    },\n    {\n      name: 'keyUsage',\n      keyCertSign: true,\n      digitalSignature: true,\n      nonRepudiation: true,\n      keyEncipherment: true,\n      dataEncipherment: true\n    },\n    {\n      name: 'extKeyUsage',\n      serverAuth: true,\n      clientAuth: true,\n      codeSigning: true,\n      emailProtection: true,\n      timeStamping: true\n    },\n    {\n      name: 'nsCertType',\n      client: true,\n      server: true,\n      email: true,\n      objsign: true,\n      sslCA: true,\n      emailCA: true,\n      objCA: true\n    }\n  ]);\n\n  cert.sign(keys.privateKey, forge.md.sha256.create());\n\n  return {\n    key: keys.privateKey,\n    cert: cert\n  };\n}\n\nexports.createRootCA = function(opts) {\n  var cert = createCACert(opts);\n  cert.key = pki.privateKeyToPem(cert.key).toString();\n  cert.cert = pki.certificateToPem(cert.cert).toString();\n  return cert;\n};\n\nfunction createCert(publicKey, serialNumber, isShortPeriod) {\n  var cert = pki.createCertificate();\n  cert.publicKey = publicKey;\n  cert.serialNumber = serialNumber || '01';\n  var curDate = new Date();\n  var curYear = curDate.getFullYear();\n  if (isShortPeriod) {\n    cert.validity.notBefore = new Date(curDate.getTime() - MIN_DATE);\n  } else {\n    cert.validity.notBefore = new Date();\n    cert.validity.notBefore.setFullYear(curYear - 1);\n  }\n  // https://www.ssls.com/blog/apples-new-ssl-lifetime-limitation-and-what-it-means-for-you/\n  // https://chromium.googlesource.com/chromium/src/+/refs/heads/master/net/cert/cert_verify_proc.cc#900\n  cert.validity.notAfter = new Date();\n  cert.validity.notAfter.setFullYear(curYear + (isShortPeriod ? 1 : 10));\n  return cert;\n}\n\nfunction getRootCAFile() {\n  createRootCASafe();\n  return ROOT_CRT_FILE;\n}\n\ncreateRootCA(); // 启动生成ca\n\nfunction getOrCreateCert(servername) {\n  var requestCert = servername[0] === ':';\n  if (requestCert) {\n    servername = servername.substring(1);\n  }\n  var cert = remoteCerts.get(servername);\n  if (!cert) {\n    servername = getDomain(servername);\n    cert = getCacheCert(servername);\n    if (!cert) {\n      cert = createSelfCert(servername);\n      cachePairs.set(servername, cert);\n    }\n  }\n  return requestCert\n    ? extend(\n      {\n        requestCert: true,\n        rejectUnauthorized: false\n      },\n        cert\n      )\n    : cert;\n}\n\nhagent.serverAgent.createCertificate = getOrCreateCert;\nvar getHttp2Server = hagent.create(h2.getHttpServer, 42900);\nvar getHttpsServer = hagent.create(h2.getServer, 43900);\nvar cbs = {};\nvar ports = {};\nvar TIMEOUT = 6000;\n\nvar SNICallback = function (servername, cb) {\n  var options = getOrCreateCert(servername);\n  if (!options._ctx) {\n    try {\n      options._ctx = createSecureContext(options);\n    } catch (e) {}\n  }\n  cb(null, options._ctx);\n};\n\nexports.getRootCA = function () {\n  createRootCASafe();\n  return {\n    key: rootKey,\n    cert: rootCrt\n  };\n};\n\nexports.getCustomCertsInfo = function () {\n  return customCertsInfo;\n};\nexports.getCustomCertsFiles = function () {\n  return customCertsFiles;\n};\n\nexports.getRootCAFile = getRootCAFile;\nexports.serverAgent = hagent.serverAgent;\n\nexports.SNICallback = SNICallback;\n\nfunction addCallback(name, callback) {\n  var cbList = cbs[name];\n  if (!cbList) {\n    cbList = [];\n    cbs[name] = cbList;\n  }\n  cbList.push(callback);\n  return cbList;\n}\n\nfunction createServer(name, cbList, listener, options) {\n  var removeServer = function () {\n    ports[name] = null;\n    try {\n      this.close();\n    } catch (e) {} //重复关闭会导致异常\n  };\n  ports[name] = false; // pending\n  var getServer = options ? getHttpsServer : getHttp2Server;\n  getServer(options, listener, function (server, port) {\n    server.on('error', removeServer);\n    var timeout = setTimeout(removeServer, TIMEOUT);\n    var clearup = function () {\n      clearTimeout(timeout);\n    };\n    if (options) {\n      server.once('tlsClientError', clearup);\n      server.once('secureConnection', clearup);\n    } else {\n      server.once('connection', clearup);\n    }\n    ports[name] = port;\n    cbList.forEach(function (cb) {\n      cb(port);\n    });\n    cbs[name] = [];\n  });\n}\n\nexports.getHttp2Server = function (listener, callback) {\n  var name = 'httpH2';\n  var curPort = ports[name];\n  if (curPort) {\n    return callback(curPort);\n  }\n  var cbList = addCallback(name, callback);\n  if (curPort === false) {\n    return;\n  }\n  createServer(name, cbList, listener);\n};\n\nexports.getSNIServer = function (listener, callback, disableH2, requestCert) {\n  var enableH2 = config.enableH2 && !disableH2;\n  var name = (enableH2 ? 'h2Sni' : 'sni') + (requestCert ? 'WithCert' : '');\n  var curPort = ports[name];\n  if (curPort) {\n    return callback(curPort);\n  }\n  var cbList = addCallback(name, callback);\n  if (curPort === false) {\n    return;\n  }\n  var options = { SNICallback: SNICallback };\n  options.allowHTTP1 = enableH2; // 是否启用http2\n  if (requestCert) {\n    options = extend(\n      {\n        requestCert: true,\n        rejectUnauthorized: false\n      },\n      options\n    );\n  }\n  createServer(name, cbList, listener, options);\n};\n\nvar checkTimer;\n\nfunction checkExpired() {\n  clearTimeout(checkTimer);\n  var files = Object.keys(customCertsFiles);\n  exports.hasInvalidCerts = false;\n  for (var i = 0, len = files.length; i < len; i++) {\n    var file = customCertsFiles[files[i]];\n    if (file && isExpiredCert(file)) {\n      exports.hasInvalidCerts = true;\n      return;\n    }\n  }\n  checkTimer = setTimeout(checkExpired, 600000);\n}\n\nfunction removeFile(filename) {\n  fs.unlink(filename, function (err) {\n    err && fs.unlink(filename, util.noop);\n  });\n}\n\nfunction writeFile(filename, ctn, callback) {\n  fs.writeFile(filename, ctn, function (err) {\n    if (!err) {\n      return callback();\n    }\n    fs.writeFile(filename, ctn, callback);\n  });\n}\n// 异步重试，出错重试即可\nfunction removeCertFile(filename, type) {\n  removeFile(path.join(CUSTOM_CERTS_DIR, filename + '.key'));\n  removeFile(path.join(CUSTOM_CERTS_DIR, filename + type));\n  delete pureCertFiles[filename];\n}\n// 异步写入，出错重试即可\nfunction writeCertFile(filename, type, cert, mtime) {\n  var keyFile = path.join(CUSTOM_CERTS_DIR, filename + '.key');\n  var certFile = path.join(CUSTOM_CERTS_DIR, filename + '.' + (type || 'crt'));\n  writeFile(keyFile, cert.key, function () {\n    fs.utimes && fs.utimes(keyFile, mtime, mtime, util.noop);\n  });\n  writeFile(certFile, cert.cert, function () {\n    fs.utimes && fs.utimes(certFile, mtime, mtime, util.noop);\n  });\n}\n\nfunction getCertType(type) {\n  if (type !== 'cer' && type !== 'pem') {\n    return 'crt';\n  }\n  return type;\n}\n\nexports.removeCert = function (opts) {\n  if (!CUSTOM_CERTS_DIR) {\n    return;\n  }\n  var filename = opts.filename;\n  var type = getCertType(opts.type);\n  if (checkFilename(filename) && allCustomCerts[filename]) {\n    removeCertFile(filename, type);\n    delete allCustomCerts[filename];\n    parseAllCustomCerts();\n  }\n};\n\nexports.setActiveCert = function (opts) {\n  if (!CUSTOM_CERTS_DIR) {\n    return;\n  }\n  var filename = opts.filename;\n  if (isRootCa(filename) && allCustomCerts[filename]) {\n    rulesUtil.setDisabledCertFile(filename, opts.disabled);\n    parseAllCustomCerts();\n  }\n};\n\nexports.uploadCerts = function (certs) {\n  if (!CUSTOM_CERTS_DIR) {\n    return;\n  }\n  var now = Date.now();\n  var hasChanged;\n  var index = 0;\n  Object.keys(certs).forEach(function (filename) {\n    if (!checkFilename(filename) || filename.length > 128) {\n      return;\n    }\n    var cert = certs[filename];\n    if (!cert) {\n      return;\n    }\n    var keyStr, certStr, type;\n    if (Array.isArray(cert)) {\n      keyStr = cert[0];\n      certStr = cert[1];\n      type = getCertType(cert[2]);\n    } else {\n      keyStr = cert.key;\n      certStr = cert.cert;\n      type = getCertType(cert.type);\n    }\n    if (util.isString(keyStr) && util.isString(certStr)) {\n      var mtime = now + index * 1000;\n      ++index;\n      try {\n        cert = parseCert({\n          key: keyStr,\n          type: type,\n          cert: certStr,\n          mtime: mtime\n        });\n        if (cert) {\n          writeCertFile(filename, type, cert.cert, new Date(mtime));\n          pureCertFiles[filename] = {\n            type: type,\n            name: filename,\n            key: keyStr,\n            cert: certStr\n          };\n          allCustomCerts[filename] = cert;\n          hasChanged = true;\n        }\n      } catch (e) {}\n    }\n  });\n  hasChanged && parseAllCustomCerts();\n};\n\nexports.getPureCertFiles = function () {\n  return pureCertFiles;\n};\n"
  },
  {
    "path": "lib/https/h2.js",
    "content": "var https = require('https');\nvar LRU = require('lru-cache');\nvar tls = require('tls');\nvar sockx = require('sockx');\nvar util = require('../util');\nvar config = require('../config');\nvar extend = require('extend');\nvar http2 = config.enableH2 ? require('http2') : null;\n\nvar SUPPORTED_PROTOS = ['h2', 'http/1.1', 'http/1.0'];\nvar H2_SETTINGS = { enablePush: false };\nvar H2_SVR_SETTINGS = { enablePush: false, enableConnectProtocol: false };\nvar CACHE_TIMEOUT = 1000 * 60;\nvar INTERVAL = 1000 * 60;\nvar clients = {};\nvar notH2 = new LRU({ max: 2560 });\nvar pendingH2 = {};\nvar pendingList = {};\nvar TIMEOUT = 36000;\nvar REQ_TIMEOUT = 16000;\nvar CONCURRENT = 3;\nvar CLOSED_ERR = new Error('closed');\nvar MSM = 1200;\nvar PMCS = 3600;\nvar RESET_BURST = 100000;\nvar RESET_RATE = 33;\nvar REQ_OPTS = { endStream: true };\n\nfunction onClose(stream, callback) {\n  stream.on('error', callback);\n  stream.once('close', function() {\n    callback(CLOSED_ERR);\n  });\n}\n\nsetInterval(function () {\n  var now = Date.now();\n  Object.keys(clients).forEach(function (name) {\n    var client = clients[name];\n    if (now - client._updateTime > CACHE_TIMEOUT) {\n      client.close();\n      delete clients[name];\n    }\n  });\n}, INTERVAL);\n\nfunction getKey(options) {\n  var proxyOpts = options._proxyOptions;\n  var proxyType = '';\n  if (proxyOpts) {\n    var auth =\n      (proxyOpts.headers && proxyOpts.headers['proxy-authorization']) || '';\n    proxyType = [\n      proxyOpts.proxyType,\n      proxyOpts.proxyHost,\n      proxyOpts.proxyPort,\n      proxyOpts.headers.host,\n      proxyOpts.proxyTunnelPath || '',\n      auth\n    ].join(':');\n  }\n  return [\n    options.servername,\n    options.host,\n    options.port || '',\n    proxyType,\n    options.cacheKey || ''\n  ].join('/');\n}\n\nfunction getSocksSocket(options, callback) {\n  var done;\n  var handleCallback = function (err, socket) {\n    if (!done) {\n      done = true;\n      callback(err, socket);\n    }\n  };\n  var proxyOpts = options._proxyOptions;\n  var client = sockx.connect(\n    {\n      localDNS: false,\n      proxyHost: proxyOpts.proxyHost,\n      proxyPort: proxyOpts.proxyPort,\n      auths: config.getAuths(proxyOpts),\n      host: options.host,\n      port: options.port || 443\n    },\n    function (socket) {\n      handleCallback(null, socket);\n    }\n  );\n  onClose(client, handleCallback);\n}\n\nfunction getTunnelSocket(options, callback) {\n  var done;\n  var handleCallback = function (err, socket) {\n    if (!done) {\n      done = true;\n      callback(err, socket);\n    }\n  };\n  var connReq = config.connect(options._proxyOptions, function (socket) {\n    handleCallback(null, socket);\n  });\n  onClose(connReq, handleCallback);\n}\n\nfunction addCert(opts, options, disabled) {\n  if (options.cert) {\n    opts.key = options.key;\n    opts.cert = options.cert;\n  } else if (options.pfx) {\n    opts.pfx = options.pfx;\n    if (options.passphrase) {\n      opts.passphrase = options.passphrase;\n    }\n  }\n  return disabled ? opts : util.setSecureOptions(opts);\n}\n\nfunction getProxySocket(options, callback, tlsOpts, isHttp, disabled) {\n  var handleConnect = function (err, socket) {\n    if (err) {\n      return callback(err);\n    }\n    if (isHttp) {\n      return callback(null, socket);\n    }\n    var timer = setTimeout(function () {\n      if (timer) {\n        handleCallback(util.TIMEOUT_ERR);\n        socket.destroy();\n      }\n    }, REQ_TIMEOUT);\n    var handleCallback = function (err) {\n      if (timer) {\n        clearTimeout(timer);\n        timer = null;\n        callback(err, err ? null : socket);\n      }\n    };\n    try {\n      var opts = {\n        servername: options.servername,\n        socket: socket,\n        rejectUnauthorized: config.rejectUnauthorized,\n        ALPNProtocols: SUPPORTED_PROTOS,\n        NPNProtocols: SUPPORTED_PROTOS\n      };\n      if (tlsOpts) {\n        extend(opts, tlsOpts);\n      }\n      socket = tls.connect(\n        addCert(\n          opts,\n          options,\n          disabled\n        ),\n        handleCallback\n      );\n      socket.on('error', function (e) {\n        if (!tlsOpts && util.isCiphersError(e)) {\n          return getProxySocket(\n            options,\n            callback,\n            util.getTlsOptions(options._rules),\n            false,\n            disabled\n          );\n        } else {\n          handleCallback(e);\n        }\n      });\n      socket.on('close', function() {\n        handleCallback(CLOSED_ERR);\n      });\n    } catch (e) {\n      handleCallback(e);\n    }\n  };\n  var proxyOpts = options._proxyOptions;\n  proxyOpts.proxyType === 'socks'\n    ? getSocksSocket(options, handleConnect)\n    : getTunnelSocket(options, handleConnect);\n}\n\nfunction getSocket(options, callback, req) {\n  var isHttp = req.useHttpH2;\n  var disabled = req.disable.secureOptions;\n  var handleCallback = function (err, socket) {\n    if (err) {\n      return callback(false, null, err);\n    }\n    var proto = socket.alpnProtocol || socket.npnProtocol;\n    callback(isHttp || proto === 'h2', socket);\n  };\n  options._proxyOptions\n    ? getProxySocket(options, handleCallback, null, isHttp, disabled)\n    : util.connect(\n        isHttp\n          ? {\n            host: options.host,\n            port: options.port || 80\n          }\n          : addCert(\n            {\n              servername: options.servername,\n              host: options.host,\n              port: options.port || 443,\n              rejectUnauthorized: config.rejectUnauthorized,\n              ALPNProtocols: SUPPORTED_PROTOS,\n              NPNProtocols: SUPPORTED_PROTOS,\n              _rules: options._rules\n            },\n              options,\n              disabled\n            ),\n        handleCallback\n      );\n}\n\nfunction getClient(req, socket, name, callback) {\n  var origin = (req.useHttpH2 ? 'http' : 'https') + '://' + req.headers.host;\n  var handleCallback = function(err, clt) {\n    if (callback) {\n      clearTimeout(timer);\n      if (err || !clt) {\n        delete clients[name];\n        socket.destroy();\n        client.close();\n      }\n      callback(clt);\n      callback = timer = null;\n    }\n  };\n  var timer = setTimeout(handleCallback, REQ_TIMEOUT);\n  var client = http2.connect(\n    origin, util.setRejectUnauthorized(req, {\n      settings: H2_SETTINGS,\n      maxSessionMemory: MSM,\n      peerMaxConcurrentStreams: PMCS,\n      streamResetBurst: RESET_BURST,\n      streamResetRate: RESET_RATE,\n      createConnection: function () {\n        return socket;\n      }\n    }), function() {\n      handleCallback(null, client);\n    });\n  clients[name] = client;\n  client._updateTime = Date.now();\n  onClose(client, handleCallback);\n  onClose(socket, handleCallback);\n  return client;\n}\n\nfunction requestH2(client, req, res, callback) {\n  if (req._hasError) {\n    return;\n  }\n  var headers = util.formatH2Headers(req.headers);\n  delete req.headers.connection;\n  delete req.headers['keep-alive'];\n  delete req.headers['http2-settings'];\n  delete req.headers['proxy-connection'];\n  delete req.headers['transfer-encoding'];\n  var options = req.options;\n  var responsed;\n  headers[':path'] = options.path;\n  headers[':method'] = options.method;\n  headers[':authority'] = req.headers.host;\n  try {\n    var h2Session = client.request(headers, req.noReqBody ? REQ_OPTS : undefined);\n    onClose(h2Session, function() {\n      if (!responsed) {\n        responsed = true;\n        h2Session.destroy();\n        req.noReqBody && callback();\n      }\n    });\n    var additionalHeaders;\n    h2Session.once('headers', function(headers) {\n      try {\n        additionalHeaders = JSON.stringify(headers);\n      } catch (e) {}\n    });\n    h2Session.on('response', function (h2Headers) {\n      if (responsed) {\n        return;\n      }\n      client._updateTime = Date.now();\n      responsed = true;\n      var newHeaders = {};\n      var statusCode = h2Headers[':status'];\n      var svrRes = h2Session;\n      if (additionalHeaders) {\n        newHeaders[util.ADDITIONAL_HEAD] = additionalHeaders;\n      }\n      svrRes.on('trailers', function (trailers) {\n        svrRes.trailers = trailers;\n      });\n      svrRes.statusCode = statusCode;\n      // HTTP2 对响应内容格式要求太严格（NGHTTP2_PROTOCOL_ERROR）\n      if (!util.hasBody(svrRes, req)) {\n        h2Session.on('data', util.noop);\n        svrRes = util.createTransform();\n        svrRes.statusCode = statusCode;\n        svrRes.push(null);\n      }\n      svrRes.httpVersion = '1.1';\n      svrRes.headers = newHeaders;\n      Object.keys(h2Headers).forEach(function (name) {\n        if (\n          name[0] !== ':' &&\n          name !== 'content-length' &&\n          name !== 'transfer-encoding'\n        ) {\n          newHeaders[name] = h2Headers[name];\n        }\n      });\n      if (req.isPluginReq && !req._isProxyReq) {\n        newHeaders[config.PROXY_ID_HEADER] = 'h2';\n      }\n      res.response(svrRes);\n    });\n    req.pipe(h2Session);\n  } catch (e) {\n    !responsed && callback();\n    responsed = true;\n  }\n}\n\nfunction bindListner(server, listener) {\n  if (typeof listener === 'function') {\n    server.on('request', listener);\n  } else if (listener) {\n    Object.keys(listener).forEach(function (name) {\n      server.on(name, listener[name]);\n    });\n  }\n  return server;\n}\n\nexports.getServer = function (options, listener) {\n  var server;\n  if (options.allowHTTP1 && http2) {\n    options.maxSessionMemory = MSM;\n    options.peerMaxConcurrentStreams = PMCS;\n    options.streamResetBurst = RESET_BURST;\n    options.streamResetRate = RESET_RATE;\n    options.settings = H2_SVR_SETTINGS;\n    server = http2.createSecureServer(options);\n  } else {\n    server = https.createServer(options);\n  }\n  server.requestTimeout = 0;\n  return bindListner(server, listener);\n};\n\nexports.getHttpServer = function (_, listener) {\n  var server = http2.createServer({\n    maxSessionMemory: MSM,\n    peerMaxConcurrentStreams: PMCS,\n    streamResetBurst: RESET_BURST,\n    streamResetRate: RESET_RATE,\n    settings: H2_SVR_SETTINGS,\n    allowHTTP1: true\n  });\n  server.requestTimeout = 0;\n  return bindListner(server, listener);\n};\n\nfunction checkTlsError(err) {\n  if (!err) {\n    return true;\n  }\n  var code = err.code;\n  return typeof code === 'string' && (code.indexOf('ERR_TLS_') === 0 || code.indexOf('ERR_SSL_') === 0);\n}\n\nexports.request = function (req, res, callback) {\n  var options = req.useH2 && req.options;\n  if (!options || options.isPlugin || req._isInternalProxy) {\n    return callback();\n  }\n  var key = getKey(options);\n  var reqId = req.disable.keepH2Session ? '' : req._h2ReqId;\n  var name = (reqId || req.clientIp) + '\\n' + key;\n  var client = clients[name];\n  if (client) {\n    if (!reqId) {\n      if (client.curIndex) {\n        name = name + '\\n' + client.curIndex;\n        client.curIndex = ++client.curIndex % CONCURRENT;\n        client = clients[name];\n      } else {\n        client.curIndex = 1;\n      }\n    }\n    if (client) {\n      client._updateTime = Date.now();\n      return requestH2(client, req, res, callback);\n    }\n  }\n  var time = notH2.peek(key);\n  if (time && (Date.now() - time < TIMEOUT || pendingH2[key])) {\n    return callback();\n  }\n  pendingH2[key] = 1;\n  var pendingItem = pendingList[name];\n  if (pendingItem) {\n    return pendingItem.push([req, res, callback]);\n  }\n  pendingItem = [[req, res, callback]];\n  pendingList[name] = pendingItem;\n  options._rules = req.rules;\n  var proxyOpts = options._proxyOptions;\n  if (proxyOpts) {\n    proxyOpts.enableIntercept = true;\n    proxyOpts.proxyTunnelPath = util.getProxyTunnelPath(req, true);\n  }\n  getSocket(\n    options,\n    function (isH2, socket, err) {\n      if (socket) {\n        socket.secureConnecting = false; // fix: node bug\n      }\n      req._connectTime = Date.now();\n      var handleH2 = function (clt) {\n        delete pendingList[name];\n        delete pendingH2[key];\n        if (clt) {\n          notH2.del(key);\n          pendingItem.forEach(function (list) {\n            requestH2(clt, list[0], list[1], list[2]);\n          });\n        } else {\n          checkTlsError(err) && notH2.set(key, Date.now());\n          if (req.useHttpH2) {\n            socket = null;\n          }\n          pendingItem.forEach(function (list) {\n            list[2](socket);\n            socket = null;\n          });\n        }\n      };\n      if (err || !socket || !isH2) {\n        return handleH2();\n      }\n      getClient(req, socket, name, function(clt) {\n        if (!clt) {\n          socket = null;\n        }\n        handleH2(clt);\n      });\n    },\n    req\n  );\n};\n"
  },
  {
    "path": "lib/https/index.js",
    "content": "var net = require('net');\nvar tls = require('tls');\nvar http = require('http');\nvar parseUrl = require('../util/parse-url-safe');\nvar socks = require('sockx');\nvar crypto = require('crypto');\nvar EventEmitter = require('events').EventEmitter;\nvar LRU = require('lru-cache');\nvar checkSNI = require('sni');\nvar util = require('../util');\nvar extend = require('extend');\nvar config = require('../config');\nvar rules = require('../rules');\nvar pluginMgr = require('../plugins');\nvar socketMgr = require('../socket-mgr');\nvar hparser = require('hparser');\nvar properties = require('../rules/util').properties;\nvar ca = require('./ca');\nvar loadCert = require('./load-cert');\nvar h2Consts = config.enableH2 ? require('http2').constants : {};\n\nvar STATUS_CODES = http.STATUS_CODES || {};\nvar getRawHeaders = hparser.getRawHeaders;\nvar getRawHeaderNames = hparser.getRawHeaderNames;\nvar formatHeaders = hparser.formatHeaders;\nvar parseReq = hparser.parse;\nvar getDomain = ca.getDomain;\nvar serverAgent = ca.serverAgent;\nvar getSNIServer = ca.getSNIServer;\nvar getHttp2Server = ca.getHttp2Server;\nvar LOCALHOST = '127.0.0.1';\nvar tunnelTmplData = new LRU({ max: 3000, maxAge: 30000 });\nvar uaCache = new LRU({ max: 1024 });\nvar TIMEOUT = 12000;\nvar CONN_TIMEOUT = 60000;\nvar X_RE = /^x/;\nvar PROTO_SEP_RE = /,\\s*/;\nvar HTTP_PROTO_RE = /^(ws|http)s?:\\/\\//;\nvar clientIpKey = config.CLIENT_IP_HEADER;\nvar proxy, server;\n\nfunction handleWebsocket(socket, clientIp, clientPort) {\n  var wss = socket.isHttps;\n  clientIp = util.removeIPV6Prefix(\n    clientIp || socket._remoteAddr || socket.remoteAddress\n  );\n  socket.clientIp = clientIp;\n  socket.method = 'GET';\n  socket.reqId = util.getReqId();\n  socket.clientPort = clientPort || socket._remotePort || socket.remotePort;\n  rules.initHeaderRules(socket);\n  pluginMgr.resolvePipePlugin(socket, function () {\n    socket.rules = rules.initRules(socket);\n    rules.resolveRulesFile(socket, function () {\n      resolveWebsocket(socket, wss);\n    });\n  });\n}\n\nfunction setCaptureUA(ua) {\n  if (ua && typeof ua === 'string' && ua.length < 1024) {\n    uaCache.set(ua, 1);\n  }\n}\n\nfunction isCaptureUA(ua) {\n  return ua && uaCache.get(ua);\n}\n\nfunction getTransProto(str) {\n  if (!str || typeof str !== 'string') {\n    return;\n  }\n  if (str.indexOf(',') === -1) {\n    return str;\n  }\n  return str.trim().split(PROTO_SEP_RE)[0];\n}\n\nfunction handleFrames(socket, reqSocket, headers) {\n  if (socket.enable.websocket || util.isWebSocket(headers)) {\n    socketMgr.handleUpgrade(socket, reqSocket);\n  } else {\n    socketMgr.handleConnect(socket, reqSocket, true);\n  }\n}\n\nfunction resolveWebsocket(socket, wss) {\n  var headers = socket.headers;\n  var reqEmitter = new EventEmitter();\n  var fullUrl = socket.fullUrl;\n  var _rules = socket.rules;\n  var clientIp = socket.clientIp;\n  var filter = socket._filters;\n  var now = Date.now();\n  var reqData = {\n    ip: clientIp,\n    port: socket.clientPort,\n    method: 'GET',\n    httpVersion: socket.httpVersion || '1.1',\n    headers: headers,\n    rawHeaderNames: socket.rawHeaderNames\n  };\n  var resData = {};\n  var data = {\n    _clientId: socket._clientId,\n    id: socket.reqId,\n    fc: socket.fromComposer ? 1 : undefined,\n    url: fullUrl,\n    startTime: now,\n    sniPlugin: socket.sniPlugin,\n    fwdHost: socket._fwdHost,\n    rules: _rules,\n    req: reqData,\n    res: resData,\n    pipe: socket._pipeRule,\n    rulesHeaders: socket.rulesHeaders\n  };\n\n  var reqSocket,\n    options,\n    isXProxy,\n    isInternalProxy;\n  var origProto, done, proxyUrl, clientKey, clientCert, isPfx, curStatus;\n  var timeout = setTimeout(function () {\n    destroy(util.TIMEOUT_ERR);\n  }, CONN_TIMEOUT);\n  var handleResponse = function () {\n    var svrRes = util.getStatusCodeFromRule(_rules);\n    if (!svrRes) {\n      return;\n    }\n    var statusCode = svrRes.statusCode;\n    var status = util.getStatusCode(statusCode);\n    var resHeaders = resData.headers = svrRes.headers;\n    var body = '';\n    var statusMsg;\n    util.deleteReqHeaders(socket);\n    resData.body = body;\n    resData.ip = resData.ip || LOCALHOST;\n    data.requestTime = data.dnsTime = Date.now();\n    getResRules(socket, resData, function () {\n      status = curStatus || status;\n      util.addMatchedRules(socket, resData);\n      var isSuccess = status == 101;\n      if (isSuccess) {\n        statusMsg = 'HTTP/1.1 101 Switching Protocols';\n        var key = headers['sec-websocket-key'];\n        var protocol = getTransProto(headers['sec-websocket-protocol']);\n        headers.upgrade = getTransProto(headers.upgrade) || 'websocket';\n        if (key) {\n          key += '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';\n          resHeaders['Sec-WebSocket-Accept'] = crypto.createHash('sha1').update(key, 'binary').digest('base64');\n        }\n        if (protocol) {\n          resHeaders['Sec-WebSocket-Protocol'] = protocol;\n        }\n        resHeaders.Upgrade = headers.upgrade ;\n        resHeaders.Connection = 'Upgrade';\n        reqSocket = util.getEmptyRes();\n        reqSocket.headers = resHeaders;\n        handleFrames(socket, reqSocket, headers);\n      } else {\n        if (!status) {\n          status = 502;\n          body = 'Invalid status code: ' + statusCode;\n          resHeaders['Content-Type'] = 'text/html; charset=utf8';\n          resHeaders['Content-Length'] = Buffer.byteLength(body) + '';\n        }\n        resHeaders.Connection = 'close';\n        statusMsg = 'HTTP/1.1 ' + status;\n      }\n      resData.statusCode = status;\n      var curHeaders = resHeaders;\n      if (socket.fromComposer) {\n        curHeaders = extend({}, resHeaders);\n        curHeaders['x-whistle-req-id'] = socket.reqId;\n        util.setFramesMode(curHeaders, isSuccess);\n      }\n      var rawData =\n        statusMsg + '\\r\\n' + getRawHeaders(curHeaders) + '\\r\\n\\r\\n' + body;\n      var end = function () {\n        if (util.needAbortRes(socket)) {\n          socket.__statusCode = status;\n          return destroy();\n        }\n        data.responseTime = data.endTime = Date.now();\n        if (isSuccess) {\n          socket.write(rawData);\n        } else {\n          socket.end(rawData);\n        }\n        execCallback(null, socket);\n      };\n      var reqDelay = util.getMatcherValue(_rules.reqDelay) || 0;\n      var resDelay = util.getMatcherValue(_rules.resDelay) || 0;\n      var delay = reqDelay + resDelay;\n      if (delay > 0) {\n        clearTimeout(timeout);\n        setTimeout(end, delay);\n      } else {\n        end();\n      }\n    });\n\n    return true;\n  };\n\n  var plugin = pluginMgr.resolveWhistlePlugins(socket);\n  abortIfUnavailable(socket);\n  pluginMgr.getRules(socket, function (rulesMgr) {\n    if (rulesMgr) {\n      socket.pluginRules = rulesMgr;\n      socket.curUrl = fullUrl;\n      util.mergeRules(socket, rulesMgr.resolveReqRules(socket));\n      util.filterWeakRule(socket);\n      if (util.isIgnored(filter, 'rule')) {\n        plugin = null;\n      } else {\n        plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));\n      }\n    } else {\n      util.filterWeakRule(socket);\n    }\n\n    var ruleUrlValue = plugin ? null : util.rule.getUrl(_rules.rule);\n    if (ruleUrlValue && fullUrl !== ruleUrlValue && HTTP_PROTO_RE.test(ruleUrlValue)) {\n      if (RegExp.$1 === 'http') {\n        ruleUrlValue = ruleUrlValue.replace('http', 'ws');\n      }\n      data.realUrl = fullUrl = util.encodeNonLatin1Char(ruleUrlValue);\n    }\n    if (_rules.referer) {\n      var referer = util.getMatcherValue(_rules.referer);\n      headers.referer = referer;\n    }\n    if (_rules.ua) {\n      var ua = util.getMatcherValue(_rules.ua);\n      headers['user-agent'] = ua;\n    }\n    if (util.showPluginReq(socket) && !socket.isInternalUrl) {\n      if (socket.isLogRequests !== false) {\n        ++util.proc.wsRequests;\n        ++util.proc.totalWsRequests;\n        socket.isLogRequests = true;\n      }\n      if (!util.isHide(socket)) {\n        data.abort = destroy;\n        if (socket.isPluginReq) {\n          data.isPR = 1;\n        }\n        proxy.emit('request', reqEmitter, data);\n      }\n    }\n    setupSocket(function () {\n      if (util.needAbortReq(socket)) {\n        return destroy();\n      }\n      if (handleResponse()) {\n        return;\n      }\n\n      pluginMgr.loadPlugin(\n        socket.isPluginReq ? null : plugin,\n        function (err, ports) {\n          if (plugin) {\n            data.dnsTime = Date.now();\n          }\n          if (err) {\n            return execCallback(err);\n          }\n          var port = ports && ports.upgrade && ports.port;\n          if (port) {\n            options.isPlugin = true;\n            options.port = port;\n            data.realUrl = util.changePort(fullUrl, options.port);\n            socket.customParser = util.getParserStatus(socket);\n            pluginMgr.addSessionInfo(socket, _rules);\n            socketMgr.setPending(socket);\n            options.protocol = 'ws:';\n            socket.headers[config.PLUGIN_HOOK_NAME_HEADER] =\n              config.PLUGIN_HOOKS.HTTP;\n            connectServer();\n            return;\n          }\n          plugin = null;\n          rules.getClientCert(socket, function (_key, _cert, _isPfx) {\n            clientKey = _key;\n            clientCert = _cert;\n            isPfx = _isPfx;\n            rules.getProxy(fullUrl, socket, function (err, hostIp, hostPort) {\n              var proxyRule = _rules.proxy;\n              var connectProxy;\n              var send = function () {\n                data.requestTime = Date.now();\n                if (connectProxy) {\n                  connectProxy();\n                } else {\n                  connectServer(hostIp, hostPort);\n                }\n              };\n              var connect = function () {\n                var reqDelay = util.getMatcherValue(_rules.reqDelay);\n                if (reqDelay > 0) {\n                  clearTimeout(timeout);\n                  setTimeout(function () {\n                    timeout = setTimeout(function () {\n                      destroy(util.TIMEOUT_ERR);\n                    }, CONN_TIMEOUT);\n                    send();\n                  }, reqDelay);\n                } else {\n                  send();\n                }\n              };\n              proxyUrl = proxyRule ? util.rule.getMatcher(proxyRule) : null;\n              if (proxyUrl) {\n                isXProxy = X_RE.test(proxyUrl);\n                var isSocks = proxyRule.isSocks;\n                isInternalProxy = proxyRule.isInternal || util.isInternalProxy(socket);\n                var isHttpsProxy = proxyRule.isHttps;\n                if (!isInternalProxy && !wss && proxyRule.isHttp2https) {\n                  wss = true;\n                } else {\n                  wss = options.protocol === 'wss:';\n                }\n                proxyUrl = 'http:' + util.removeProtocol(proxyUrl);\n                getServerIp(\n                  proxyUrl,\n                  function (ip) {\n                    var proxyOptions = parseUrl(proxyUrl);\n                    proxyOptions.auth = proxyOptions.auth || socket._pacAuth;\n                    hostPort = proxyOptions.port;\n                    hostIp = ip;\n                    var proxyPort = (proxyOptions.port =\n                      parseInt(hostPort, 10) ||\n                      (isSocks ? 1080 : isHttpsProxy ? 443 : 80));\n                    var isProxyPort = util.isProxyPort(proxyPort);\n                    if (isProxyPort && util.isLocalAddress(ip)) {\n                      return execCallback(\n                        new Error('Self loop (' + util.joinIpPort(ip, proxyPort) + ')')\n                      );\n                    }\n\n                    options.proxyHost = ip;\n                    resData.port = options.proxyPort = proxyPort;\n                    socket.serverPort = resData.port;\n                    options.host = options.hostname;\n                    if (!options.port) {\n                      options.port = wss ? 443 : 80;\n                    }\n                    var proxyOpts, tlsOpts;\n                    var handleProxy = function (proxySocket) {\n                      if (wss) {\n                        var conf = { socket: proxySocket };\n                        if (tlsOpts) {\n                          extend(conf, tlsOpts);\n                        }\n                        var opts = util.setRejectUnauthorized(socket, conf);\n                        if (!socket.disable.secureOptions) {\n                          util.setSecureOptions(opts);\n                        }\n                        if (!socket.disable.servername) {\n                          opts.servername = options.hostname;\n                        }\n                        util.setClientCert(opts, clientKey, clientCert, isPfx);\n                        var handleProxyError = function (err) {\n                          if (\n                            connectProxy &&\n                            !tlsOpts &&\n                            util.isCiphersError(err)\n                          ) {\n                            tlsOpts = util.getTlsOptions(_rules);\n                            connectProxy();\n                          } else if (\n                            connectProxy &&\n                            util.checkTlsError(err) &&\n                            util.checkAuto2Http(socket, ip, proxyUrl)\n                          ) {\n                            wss = false;\n                            data.httpsTime = data.httpsTime || Date.now();\n                            data.useHttp = true;\n                            if (proxyOpts && options.port == 443) {\n                              proxyOpts.port = options.port = 80;\n                              proxyOpts.headers.host = util.joinIpPort(proxyOpts.host, proxyOpts.port);\n                            }\n                            connectProxy();\n                          } else {\n                            execCallback(err);\n                          }\n                        };\n                        try {\n                          proxySocket = tls.connect(opts);\n                          proxySocket.on('error', handleProxyError);\n                          reqSocket = proxySocket;\n                          proxySocket.on('secureConnect', function () {\n                            abortIfUnavailable(reqSocket);\n                            pipeData();\n                          });\n                        } catch (e) {\n                          handleProxyError(e);\n                        }\n                        return;\n                      }\n                      reqSocket = proxySocket;\n                      abortIfUnavailable(reqSocket);\n                      pipeData();\n                    };\n                    // 对应 internal-proxy 要用直接请求，方便用来穿透 nginx\n                    if (isInternalProxy && !socket._phost) {\n                      if (wss) {\n                        headers[config.HTTPS_FIELD] = 1;\n                        options.protocol = null;\n                        origProto = 'wss:';\n                        wss = false;\n                      }\n                    } else {\n                      if (isSocks) {\n                        options.localDNS = false;\n                        options.auths = config.getAuths(proxyOptions);\n                      } else {\n                        var proxyHeaders = (options.headers = {});\n                        pluginMgr.getTunnelKeys().forEach(function (k) {\n                          var val = headers[k];\n                          if (val) {\n                            proxyHeaders[k] = val;\n                          }\n                        });\n                        var auth = headers['proxy-authorization'];\n                        if (auth) {\n                          proxyHeaders['proxy-authorization'] = auth;\n                        }\n                        if (socket.disable.proxyUA) {\n                          delete proxyHeaders['user-agent'];\n                        } else if (headers['user-agent']) {\n                          proxyHeaders['user-agent'] = headers['user-agent'];\n                        }\n                        if (wss && isInternalProxy) {\n                          headers[config.HTTPS_FIELD] = 1;\n                          options.protocol = null;\n                          origProto = 'wss:';\n                          wss = false;\n                        }\n                        if (!util.isLocalAddress(clientIp)) {\n                          proxyHeaders[clientIpKey] = clientIp;\n                        }\n                        if (isProxyPort) {\n                          proxyHeaders[config.WEBUI_HEAD] = 1;\n                        }\n                        if (util.isLocalPHost(socket, wss)) {\n                          headers[config.WEBUI_HEAD] = 1;\n                        }\n                        var clientId = headers[config.CLIENT_ID_HEADER];\n                        if (clientId) {\n                          proxyHeaders[config.CLIENT_ID_HEADER] = clientId;\n                        }\n                        util.checkIfAddInterceptPolicy(proxyHeaders, headers);\n                        util.setClientId(\n                          proxyHeaders,\n                          socket.enable,\n                          socket.disable,\n                          clientIp,\n                          isInternalProxy\n                        );\n                        options.proxyAuth = proxyOptions.auth;\n                      }\n                      var netMgr = isSocks ? socks : config;\n                      proxyOpts = util.setProxyHost(socket, options);\n                      if (isHttpsProxy) {\n                        proxyOpts.proxyServername = proxyOptions.hostname;\n                      }\n                      connectProxy = function () {\n                        if (destroyed) {\n                          return;\n                        }\n                        if (socket._phost) {\n                          resData.phost = socket._phost.host;\n                        }\n                        proxyOpts.enableIntercept = true;\n                        proxyOpts.proxyTunnelPath = util.getProxyTunnelPath(\n                          socket,\n                          wss\n                        );\n                        try {\n                          var s = netMgr.connect(proxyOpts, handleProxy);\n                          s.on(\n                            'error',\n                            isXProxy\n                              ? function () {\n                                if (isXProxy) {\n                                  isXProxy = false;\n                                  resData.phost = undefined;\n                                  if (isInternalProxy) {\n                                    options.protocol = origProto;\n                                  }\n                                  connectServer();\n                                }\n                              }\n                              : execCallback\n                          );\n                        } catch (e) {\n                          execCallback(e);\n                        }\n                      };\n                    }\n                    connect();\n                  },\n                  null,\n                  null,\n                  proxyRule\n                );\n              } else {\n                connect();\n              }\n            });\n          });\n        }\n      );\n    });\n  });\n\n  var retryConnect, auto2http, newIp;\n  var retryXHost = 0;\n  function connectServer(hostIp, hostPort, tlsOpts) {\n    getServerIp(\n      fullUrl,\n      function (ip, port) {\n        var isWss = options.protocol === 'wss:';\n        var _port = port;\n        resData.port = port = port || options.port || (isWss ? 443 : 80);\n        socket.serverPort = port;\n        if (destroyed) {\n          return;\n        }\n        if (util.isProxyPort(port) && util.isLocalAddress(ip)) {\n          return execCallback(new Error('Self loop (' + util.joinIpPort(ip, port) + ')'));\n        }\n        // checkHandUpError, retry\n        try {\n          var opts = util.setRejectUnauthorized(socket, {\n            host: ip,\n            port: port\n          });\n          if (!socket.disable.servername) {\n            opts.servername =\n              util.parseHost(headers.host)[0] || options.hostname;\n          }\n          isWss && util.setClientCert(opts, clientKey, clientCert, isPfx);\n          if (tlsOpts) {\n            extend(opts, tlsOpts);\n          }\n          if (!socket.disable.secureOptions) {\n            util.setSecureOptions(opts);\n          }\n          reqSocket = (isWss ? tls : net).connect(opts, pipeData);\n        } catch (e) {\n          return execCallback(e);\n        }\n        if (retryConnect) {\n          abortIfUnavailable(reqSocket);\n        } else {\n          retryConnect = function (e) {\n            if (!newIp && (newIp = util.getLocalhostIP(e, socket, socket._w2hostname, socket.hostIp))) {\n              ip = newIp;\n            } else if (\n              retryXHost < 2 &&\n              ((_rules.host && X_RE.test(_rules.host.matcher)) ||\n                (isInternalProxy && isXProxy))\n            ) {\n              ++retryXHost;\n              retryConnect = false;\n              if (retryXHost > 1) {\n                socket.curUrl = fullUrl;\n                rules.lookupHost(socket, function (err, _ip) {\n                  socket.hostIp = resData.ip = _ip || LOCALHOST;\n                  if (err) {\n                    return execCallback(err);\n                  }\n                  connectServer(_ip);\n                });\n                return;\n              }\n            } else if (isWss && util.checkAuto2Http(socket, ip, proxyUrl)) {\n              if (auto2http || util.checkTlsError(e)) {\n                options.protocol = null;\n                data.httpsTime = data.httpsTime || Date.now();\n                data.useHttp = true;\n              } else {\n                retryConnect = false;\n                auto2http = true;\n              }\n            }\n            connectServer(ip, _port);\n          };\n          var retried;\n          // 不要用once，防止多次触发error导致crash\n          reqSocket.on('error', function (err) {\n            if (retried) {\n              return;\n            }\n            retried = true;\n            this.destroy && this.destroy();\n            if (destroyed || !retryConnect) {\n              return;\n            }\n            if (!tlsOpts && isWss && util.isCiphersError(err)) {\n              connectServer(hostIp, hostPort, util.getTlsOptions(_rules));\n            } else {\n              retryConnect(err);\n            }\n          });\n        }\n      },\n      hostIp,\n      hostPort\n    );\n  }\n\n  function pipeData() {\n    var enable = socket.enable;\n    var disable = socket.disable;\n    if (retryConnect) {\n      reqSocket.removeListener('error', retryConnect);\n      abortIfUnavailable(reqSocket);\n      retryConnect = null;\n    }\n    clearTimeout(timeout);\n    var clientId = headers[config.CLIENT_ID_HEADER];\n    if (clientId) {\n      if (!options.isPlugin && !socket._customClientId && !util.isKeepClientId(socket, proxyUrl)) {\n        socket._origClientId = clientId;\n        util.removeClientId(headers);\n      }\n      data.clientId = clientId;\n    } else {\n      util.setClientId(headers, enable, disable, clientIp, isInternalProxy);\n    }\n    if (disable.clientIp || disable.clientIP) {\n      delete headers[clientIpKey];\n    } else {\n      var forwardedFor = util.getMatcherValue(_rules.forwardedFor);\n      if (net.isIP(forwardedFor)) {\n        headers[clientIpKey] = forwardedFor;\n      } else if (socket._customXFF) {\n        headers[clientIpKey] = socket._customXFF;\n      } else if (\n        (!isInternalProxy &&\n          !plugin &&\n          !socket.enableXFF &&\n          !enable.clientIp &&\n          !enable.clientIP) ||\n        util.isLocalAddress(clientIp)\n      ) {\n        delete headers[clientIpKey];\n      } else {\n        headers[clientIpKey] = clientIp;\n      }\n    }\n    util.deleteReqHeaders(socket);\n    util.addMatchedRules(socket);\n    reqSocket.write(socket.getBuffer(headers, options.path));\n    reqSocket.resume();\n    delete headers['x-whistle-frame-parser'];\n    delete headers[config.HTTPS_FIELD];\n    _pipeData();\n  }\n\n  function setupSocket(cb) {\n    var authObj = util.getAuthByRules(_rules);\n    var list = [\n      _rules.reqHeaders,\n      _rules.reqCors,\n      _rules.reqCookies,\n      _rules.params,\n      _rules.urlReplace,\n      _rules.urlParams,\n      authObj ? null : _rules.auth\n    ];\n    util.parseRuleJson(\n      list,\n      function (\n        reqHeaders,\n        reqCors,\n        reqCookies,\n        params,\n        urlReplace,\n        urlParams,\n        auth\n      ) {\n        if (params && urlParams) {\n          extend(params, urlParams);\n        } else {\n          params = params || urlParams;\n        }\n        var newUrl = params\n          ? util.replaceUrlQueryString(fullUrl, params)\n          : fullUrl;\n        var dProps = util.parseDelQuery(socket);\n        newUrl = util.parsePathReplace(newUrl, urlReplace, dProps.paths) || newUrl;\n        newUrl = util.deleteQuery(newUrl, dProps.query, socket._delQueryString);\n        if (newUrl !== fullUrl) {\n          fullUrl = newUrl;\n          socket._realUrl = newUrl;\n        }\n        options = util.parseUrl(fullUrl);\n        socket._w2hostname = options.hostname;\n        data.realUrl = fullUrl;\n        var host = (headers.host = options.host);\n        socket._origin = headers.origin;\n        if (reqHeaders) {\n          reqHeaders = util.lowerCaseify(reqHeaders, socket.rawHeaderNames);\n          var xff = reqHeaders[clientIpKey];\n          if (net.isIP(xff)) {\n            socket._customXFF = xff;\n          }\n          socket._customClientId = reqHeaders[config.CLIENT_ID_HEADER];\n          delete reqHeaders[clientIpKey];\n          extend(headers, reqHeaders);\n          headers.host = headers.host || host;\n        }\n        auth = util.getAuthBasic(auth || authObj);\n        if (auth) {\n          headers['authorization'] = auth;\n        }\n        var reqRuleData = { headers: headers };\n        util.setReqCors(reqRuleData, reqCors);\n        util.setReqCookies(reqRuleData, reqCookies, headers.cookie, socket);\n        if (_rules.referer) {\n          headers.referer = util.getMatcherValue(_rules.referer);\n        }\n\n        util.disableReqProps(socket);\n        pluginMgr.postStats(socket);\n        cb();\n      },\n      socket\n    );\n  }\n\n  function getResRules(socket, res, callback) {\n    socket.statusCode = res.statusCode || '';\n    var curResHeaders = (socket.resHeaders = res.headers);\n    pluginMgr.getResRules(socket, res, function () {\n      util.parseRuleJson(\n        [_rules.resHeaders, _rules.resCors, _rules.resCookies],\n        function (resHeaders, cors, cookies) {\n          if (resHeaders) {\n            resHeaders = util.lowerCaseify(resHeaders, res.rawHeaderNames);\n            extend(res.headers, resHeaders);\n          }\n          var origin = curResHeaders['access-control-allow-origin'];\n          if (socket._origin && origin && origin !== '*') {\n            curResHeaders['access-control-allow-origin'] = socket._origin;\n          }\n          util.setResCors(res, cors, socket);\n          util.setResCookies(res, cookies, socket);\n          util.setResponseFor(_rules, curResHeaders, socket, socket.hostIp, socket._phost);\n          util.deleteResHeaders(socket, curResHeaders);\n          util.disableResProps(socket, curResHeaders);\n          curStatus = util.getMatcherValue(_rules.replaceStatus);\n          callback();\n        },\n        socket\n      );\n    });\n  }\n\n  function _pipeData() {\n    parseReq(\n      reqSocket,\n      function (err, res) {\n        if (err) {\n          return execCallback(err);\n        }\n        reqSocket.pause();\n        socket.statusCode = res.statusCode || '';\n        var curResHeaders = (reqSocket.headers = res.headers);\n        socket.resHeaders = curResHeaders;\n        getResRules(socket, res, function () {\n          util.delay(util.getMatcherValue(_rules.resDelay), function () {\n            if (util.needAbortRes(socket)) {\n              socket.__statusCode = res.statusCode;\n              return destroy();\n            }\n            reqSocket.reqId = data.id;\n            var code = curStatus || res.statusCode;\n            var isSuccess = code == 101;\n            socket.statusCode = res.statusCode = code;\n            util.addMatchedRules(socket, res);\n            var curHeaders = curResHeaders;\n            if (socket.fromComposer) {\n              curHeaders = extend({}, curResHeaders);\n              curHeaders['x-whistle-req-id'] = reqSocket.reqId;\n              util.setFramesMode(curHeaders, isSuccess);\n            }\n            if (isSuccess) {\n              socket.write(res.getHeaders(curHeaders, curStatus));\n              if (res.bodyBuffer.length) {\n                reqSocket.unshift(res.bodyBuffer);\n              }\n              handleFrames(socket, reqSocket, curResHeaders);\n              resData.body = '';\n            } else {\n              socket.write(res.getBuffer(curHeaders, curStatus));\n              reqSocket.resume();\n              resData.body = res.body;\n            }\n            resData.headers = curResHeaders;\n            resData.rawHeaderNames = res.rawHeaderNames;\n            resData.statusCode = code;\n            reqEmitter.emit('response', data);\n            execCallback(null, reqSocket);\n          });\n        });\n      },\n      true\n    );\n  }\n\n  function getServerIp(url, callback, hostIp, hostPort, proxyRule) {\n    if (plugin) {\n      return callback(LOCALHOST);\n    }\n    var hostHandler = function (err, ip, port, hostRule) {\n      if (err) {\n        return execCallback(err);\n      }\n      if (hostRule) {\n        (proxyRule || _rules).host = hostRule;\n      }\n      socket.hostIp = resData.ip = util.joinIpPort(ip, port);\n      data.requestTime = data.dnsTime = Date.now();\n      reqEmitter.emit('send', data);\n      callback(ip, port);\n    };\n    if (hostIp) {\n      hostHandler(null, hostIp, hostPort);\n    } else {\n      socket.curUrl = url;\n      rules.resolveHost(\n        socket,\n        hostHandler,\n        socket.pluginRules,\n        socket.rulesFileMgr,\n        socket.headerRulesMgr\n      );\n    }\n  }\n\n  function abortIfUnavailable(socket) {\n    return util.onSocketEnd(socket, destroy);\n  }\n  var destroyed, reqDestroyed, resDestroyed;\n  function destroy(err) {\n    if (!reqDestroyed) {\n      reqDestroyed = true;\n      socket.destroy();\n    }\n    if (reqSocket && !resDestroyed) {\n      resDestroyed = true;\n      reqSocket.destroy();\n    }\n    if (destroyed) {\n      return;\n    }\n    destroyed = true;\n    execCallback(err);\n  }\n\n  function execCallback(err, _socket) {\n    if (done) {\n      return;\n    }\n    done = true;\n    data.dnsTime = data.dnsTime || Date.now();\n    clearTimeout(timeout);\n    data.responseTime = data.endTime = Date.now();\n    socket.hostIp = resData.ip = resData.ip || LOCALHOST;\n    if (!err && !_socket) {\n      err = new Error('Aborted');\n      data.reqError = true;\n      resData.statusCode = 'aborted' + (socket.__statusCode ? ' (' + socket.__statusCode + ')' : '');\n      reqData.body = util.getErrorStack(err);\n      reqEmitter.emit('abort', data);\n    } else if (err) {\n      data.resError = true;\n      resData.statusCode = resData.statusCode || 502;\n      resData.body = util.getErrorStack(err);\n      util.emitError(reqEmitter, data);\n      destroy(err);\n    } else {\n      reqEmitter.emit('end', data);\n    }\n    if (socket.resHeaders) {\n      resData.headers = socket.resHeaders;\n    } else if (err) {\n      resData.headers = { 'x-server': 'whistle' };\n    }\n    pluginMgr.postStats(socket, _socket || resData);\n  }\n}\n\nfunction getTunnelData(socket, clientIp, clientPort, isHttpH2) {\n  var enable = socket.enable || '';\n  var disable = socket.disable || '';\n  var headers = socket.headers;\n  var tunnelData = headers[util.TUNNEL_DATA_HEADER];\n  var tdKey = config.tdKey;\n  if (tdKey && (!tunnelData || config.overTdKey)) {\n    tunnelData = headers[tdKey] || tunnelData;\n  }\n  var tunnelHeaders;\n  var tunnelKeys = pluginMgr.getTunnelKeys();\n  tunnelKeys.forEach(function (k) {\n    var val = headers[k];\n    if (val) {\n      tunnelHeaders = tunnelHeaders || {};\n      tunnelHeaders[k] = val;\n    }\n  });\n  return {\n    id: socket.reqId,\n    clientIp: clientIp,\n    clientPort: clientPort,\n    remoteAddr: socket._remoteAddr,\n    remotePort: socket._remotePort,\n    clientId: headers[config.CLIENT_ID_HEADER],\n    proxyAuth: disable.tunnelAuthHeader\n                  ? undefined\n                  : headers['proxy-authorization'],\n    tunnelData: tunnelData,\n    headers: tunnelHeaders,\n    tunnelFirst:\n                  enable.tunnelHeadersFirst && !disable.tunnelHeadersFirst,\n    isHttpH2: isHttpH2,\n    sniPlugin: socket.sniPlugin\n  };\n}\n\nfunction addReqInfo(req) {\n  var socket = req.socket;\n  var remoteData =\n    socket._remoteDataInfo ||\n    tunnelTmplData.get(socket.remotePort + ':' + socket.localPort);\n  var headers = req.headers;\n  if (remoteData) {\n    req.isHttpH2 = remoteData.isHttpH2;\n    req.reqId = remoteData.id;\n    socket._remoteDataInfo = remoteData;\n    remoteData._connected = true;\n    headers[config.CLIENT_INFO_HEADER] =\n      (remoteData.clientIp || LOCALHOST) +\n      ',' + remoteData.clientPort +\n      ',' + remoteData.remoteAddr +\n      ',' + remoteData.remotePort;\n    util.setTunnelHeaders(headers, remoteData);\n  }\n  if (!req.isHttpH2) {\n    headers[config.HTTPS_FIELD] = 1;\n    setCaptureUA(req.headers['user-agent']);\n  }\n}\n\nfunction getStatusMessage(obj) {\n  return obj.statusMessage || STATUS_CODES[obj.code] || 'Unknown';\n}\n\nfunction isIllegalcHeader(name, value) {\n  switch (name) {\n  case h2Consts.HTTP2_HEADER_CONNECTION:\n  case h2Consts.HTTP2_HEADER_UPGRADE:\n  case h2Consts.HTTP2_HEADER_HOST:\n  case h2Consts.HTTP2_HEADER_HTTP2_SETTINGS:\n  case h2Consts.HTTP2_HEADER_KEEP_ALIVE:\n  case h2Consts.HTTP2_HEADER_PROXY_CONNECTION:\n  case h2Consts.HTTP2_HEADER_TRANSFER_ENCODING:\n    return true;\n  case h2Consts.HTTP2_HEADER_TE:\n    return value !== 'trailers';\n  default:\n    return false;\n  }\n}\n\nfunction formatRawHeaders(obj, isH2) {\n  var headers = obj.headers;\n  if (isH2) {\n    var newHeaders = {};\n    Object.keys(headers).forEach(function (name) {\n      var value = headers[name];\n      if (!isIllegalcHeader(name, value)) {\n        newHeaders[name] = value;\n      }\n    });\n    return newHeaders;\n  }\n  var rawNames =\n    Array.isArray(obj.rawHeaders) && getRawHeaderNames(obj.rawHeaders);\n  return formatHeaders(headers, rawNames);\n}\n\nfunction addStreamEvents(stream, handleAbort) {\n  if (stream) {\n    stream.on('error', handleAbort);\n    stream.on('aborted', handleAbort);\n    stream.on('close', handleAbort);\n  }\n}\n\nfunction toHttp1(req, res) {\n  var isH2 = req.httpVersion == 2;\n  var client;\n  var handleAbort = function () {\n    if (client) {\n      client.abort();\n      res.destroy();\n      client = null;\n    }\n  };\n  addReqInfo(req);\n  addStreamEvents(req.stream, handleAbort);\n  req.on('error', handleAbort);\n  res.on('error', handleAbort);\n  res.once('close', handleAbort);\n  var host = req.headers[':authority'];\n  var headers = req.headers;\n  if (host) {\n    req.headers.host = host;\n    var newHeaders = { host: host };\n    Object.keys(headers).forEach(function (name) {\n      if (name[0] !== ':') {\n        newHeaders[name] = headers[name];\n      }\n    });\n    headers = newHeaders;\n  } else {\n    headers = formatRawHeaders(req);\n  }\n  var options = util.parseUrl(util.getFullUrl(req));\n  options.protocol = null;\n  options.hostname = null;\n  options.agent = false;\n  options.host = config.host || LOCALHOST;\n  options.port = config.port;\n  options.headers = headers;\n  if (isH2) {\n    headers[config.ALPN_PROTOCOL_HEADER] = (req.isHttpH2 ? 'httpH2' : 'h2') + (req.reqId ? '.' + req.reqId : '');\n  }\n  options.method = req.method;\n  client = http.request(options);\n  client.on('error', handleAbort);\n  client.on('response', function (svrRes) {\n    svrRes.on('error', handleAbort);\n    svrRes.once('end', function () {\n      var trailers = svrRes.trailers;\n      if (!util.isEmptyObject(trailers)) {\n        var rawHeaderNames = svrRes.rawTrailers\n          ? getRawHeaderNames(svrRes.rawTrailers)\n          : null;\n        try {\n          res.addTrailers(formatHeaders(trailers, rawHeaderNames));\n        } catch (e) {}\n      }\n    });\n    var addHeaders = svrRes.headers[util.ADDITIONAL_HEAD];\n    if (addHeaders) {\n      delete svrRes.headers[util.ADDITIONAL_HEAD];\n      if (isH2 && res.stream && typeof res.stream.additionalHeaders === 'function'\n        && (addHeaders = util.parseRawJson(addHeaders))) {\n        try {\n          res.stream.additionalHeaders(addHeaders);\n        } catch (e) {}\n      }\n    }\n    try {\n      var code = svrRes.statusCode;\n      res.writeHead(\n        code,\n        getStatusMessage(svrRes),\n        formatRawHeaders(svrRes, isH2)\n      );\n      var write = res.write;\n      var handleError = function (e) {\n        e && handleAbort();\n      };\n      res.write = function (chunk) {\n        return write.call(res, chunk, handleError);\n      };\n      svrRes.pipe(res);\n      res.flushHeaders && res.flushHeaders();\n    } catch (e) {\n      handleAbort();\n    }\n  });\n  req.pipe(client);\n}\n\nvar handlers = {\n  request: function (req, res) {\n    addReqInfo(req);\n    server.emit('request', req, res);\n  },\n  upgrade: function (req, socket) {\n    addReqInfo(req);\n    server.emit('upgrade', req, socket);\n  }\n};\nvar h2Handlers = config.enableH2\n  ? {\n    request: toHttp1,\n    upgrade: handlers.upgrade\n  }\n  : handlers;\n\nvar HTTP_RE = /^(\\w+)\\s+(\\S+)\\s+HTTP\\/1.\\d$/im;\nvar HTTP2_RE = /^PRI\\s\\*\\s+HTTP\\/2.0$/im;\nvar CONNECT_RE = /^CONNECT$/i;\n\nfunction addClientInfo(socket, chunk, statusLine, clientIp, clientPort) {\n  var len = Buffer.byteLength(statusLine);\n  chunk = chunk.slice(len);\n  statusLine +=\n    '\\r\\n' +\n    config.CLIENT_INFO_HEADER +\n    ': ' +\n    clientIp +\n    ',' +\n    clientPort +\n    ',' +\n    socket._remoteAddr +\n    ',' +\n    socket._remotePort;\n  var tunnelData = getTunnelData(socket, clientIp, clientPort);\n  if (tunnelData) {\n    statusLine += '\\r\\n' + util.TEMP_TUNNEL_DATA_HEADER + ': ' + encodeURIComponent(JSON.stringify(tunnelData));\n  }\n  return Buffer.concat([Buffer.from(statusLine), chunk]);\n}\n\nmodule.exports = function (socket, next, isWebPort) {\n  var reqSocket, reqDestroyed, resDestroyed;\n  var headersStr;\n  var enable = socket.enable || '';\n  var disable = socket.disable || '';\n  var isCaptureIp = function() {\n    if (disable.captureIp || disable.captureIP) {\n      return false;\n    }\n    if (enable.capture || enable.captureIp || enable.captureIP) {\n      return true;\n    }\n    return isCaptureUA(socket.headers['user-agent']);\n  };\n  var isEnable = function(p1) {\n    return enable[p1] && !disable[p1];\n  };\n  var destroy = function (err) {\n    if (reqSocket) {\n      if (!resDestroyed) {\n        resDestroyed = true;\n        reqSocket.destroy(err);\n      }\n    } else if (!reqDestroyed) {\n      reqDestroyed = true;\n      socket.destroy(err);\n    }\n  };\n\n  util.onSocketEnd(socket, destroy);\n\n  function abortIfUnavailable(s) {\n    return s.on('error', destroy);\n  }\n  var clientIp = socket.clientIp;\n  var clientPort = socket.clientPort;\n  util.readOneChunk(\n    socket,\n    function (chunk) {\n      headersStr = chunk && chunk.toString();\n      var isHttp = chunk && HTTP_RE.test(headersStr);\n      var statusLine = isHttp && RegExp['$&'];\n      if (isHttp && CONNECT_RE.test(RegExp.$1)) {\n        chunk = addClientInfo(socket, chunk, statusLine, clientIp, clientPort);\n        util.connect(\n          {\n            port: config.port,\n            host: LOCALHOST\n          },\n          function (err, s) {\n            reqSocket = s;\n            if (err || socket._hasError) {\n              return destroy(err);\n            }\n            reqSocket.write(chunk);\n            reqSocket.pipe(socket).pipe(reqSocket);\n            abortIfUnavailable(reqSocket);\n            socket.resume();\n          }\n        );\n        return;\n      }\n      if (!chunk) {\n        //没有数据\n        return isWebPort ? socket.destroy() : next(chunk);\n      }\n      if (isHttp) {\n        if (isEnable('forHttps') || disable.captureHttp) {\n          next(chunk);\n        } else {\n          socket.resume();\n          server.emit('connection', socket);\n          socket.emit(\n            'data',\n            addClientInfo(socket, chunk, statusLine, clientIp, clientPort)\n          );\n        }\n      } else if (isEnable('forHttp') || disable.captureHttps) {\n        return next(chunk);\n      } else {\n        var isHttpH2 = HTTP2_RE.test(headersStr);\n        if (!isHttpH2 && chunk[0] != 22) {\n          return next(chunk);\n        }\n        var useSNI, domain, serverKey;\n        var handleConnect = function (port) {\n          var promise =\n            !isHttpH2 && !useSNI && serverAgent.existsServer(serverKey);\n          var httpsServer = promise && promise.cert && promise.server;\n          if (httpsServer && httpsServer.setSecureContext) {\n            var cert = serverAgent.createCertificate(domain);\n            if (\n              cert.key !== promise.cert.key ||\n              cert.cert !== promise.cert.cert\n            ) {\n              try {\n                cert._ctx = cert._ctx || ca.createSecureContext(cert);\n                httpsServer.setSecureContext(cert._ctx);\n                promise.cert = cert;\n              } catch (e) {}\n            }\n          }\n          util.connect(\n            {\n              port: port,\n              host: LOCALHOST,\n              localAddress: LOCALHOST\n            },\n            function (err, s) {\n              reqSocket = s;\n              if (err || socket._hasError) {\n                return destroy(err);\n              }\n              socket._w2TunnelKey = reqSocket.localPort + ':' + reqSocket.remotePort;\n              tunnelTmplData.set(socket._w2TunnelKey,  getTunnelData(socket, clientIp, clientPort, isHttpH2));\n              reqSocket.write(chunk);\n              reqSocket.pipe(socket).pipe(reqSocket);\n              socket.resume();\n              abortIfUnavailable(reqSocket);\n            }\n          );\n        };\n        var useNoSNIServer = function () {\n          serverKey = requestCert ? ':' + domain : domain;\n          serverAgent.createServer(serverKey, handlers, handleConnect, 0, 0);\n        };\n\n        var handleRequest = function () {\n          if (useSNI) {\n            var disableH2 = !properties.isEnableHttp2();\n            if (disable.http2) {\n              disableH2 = true;\n            } else if (enable.http2) {\n              disableH2 = false;\n            }\n            getSNIServer(h2Handlers, handleConnect, disableH2, requestCert);\n          } else if (isHttpH2) {\n            getHttp2Server(h2Handlers, handleConnect);\n          } else {\n            useNoSNIServer();\n          }\n        };\n        if (isHttpH2) {\n          return handleRequest();\n        }\n        useSNI = checkSNI(chunk);\n        var servername = useSNI || socket.tunnelHostname;\n        if (\n          !servername ||\n          (useSNI ? disable.captureSNI : (disable.captureNoSNI || (net.isIP(servername) && !isCaptureIp()))) ||\n          (socket.useProxifier && !ca.existsCustomCert(servername))\n        ) {\n          return next(chunk);\n        }\n        var requestCert =\n          (enable.clientCert || enable.requestCert) &&\n          !disable.clientCert &&\n          !disable.requestCert;\n        domain = getDomain(servername);\n        socket.curUrl = socket.fullUrl = 'https://' + servername;\n        socket.useSNI = useSNI;\n        socket.serverName = socket.servername = servername;\n        socket.commonName = domain;\n        loadCert(socket, function (cert) {\n          if (socket._hasError) {\n            return destroy();\n          }\n          if (cert === false) {\n            return next(chunk);\n          }\n          if (cert) {\n            domain = servername;\n          }\n          handleRequest();\n        });\n      }\n    },\n    isWebPort ? 0 : TIMEOUT\n  );\n};\nmodule.exports.setup = function (s, p) {\n  server = s;\n  proxy = p;\n};\nmodule.exports.handleWebsocket = handleWebsocket;\n\nmodule.exports.getTunnelDataOnce = function(socket) {\n  if (!socket._w2TunnelKey) {\n    return;\n  }\n  var data = tunnelTmplData.peek(socket._w2TunnelKey);\n  return data && !data._connected ? data : null;\n};\n"
  },
  {
    "path": "lib/https/load-cert.js",
    "content": "var util = require('../util');\nvar rules = require('../rules');\nvar pluginMgr = require('../plugins');\nvar ca = require('./ca');\n\nvar remoteCerts = ca.remoteCerts;\nvar SNI_CALLBACK_RE =\n  /^sniCallback:\\/\\/(?:whistle\\.|plugin\\.)?([a-z\\d_\\-]+)(?:\\(([\\s\\S]*)\\))?$/;\nvar certCallbacks = {};\n\nmodule.exports = function (socket, callback) {\n  var servername = socket.servername;\n  var curCert = remoteCerts.get(servername);\n  var plugin = rules.resolveSNICallback(socket);\n  if (plugin) {\n    if (socket.rules) {\n      socket.rules.sniCallback = plugin;\n    }\n    if (SNI_CALLBACK_RE.test(plugin.matcher)) {\n      socket.sniRuleValue = RegExp.$2;\n      var pluginName = RegExp.$1;\n      plugin = pluginMgr.getPlugin(pluginName + ':');\n      if (plugin) {\n        if (curCert && curCert.name) {\n          socket.hasCertCache =\n            curCert.name + (curCert.mtime ? '+' + curCert.mtime : '');\n        }\n        var cbKey = servername + '/' + pluginName;\n        var cbList = certCallbacks[cbKey];\n        var handleCert = function (cert) {\n          if (cert === false) {\n            return callback(false);\n          }\n          if (cert && util.isString(cert.key) && util.isString(cert.cert)) {\n            socket.sniPlugin = cert.name;\n            if (\n              !curCert ||\n              curCert.key !== cert.key ||\n              curCert.cert !== cert.cert\n            ) {\n              remoteCerts.set(servername, cert);\n              curCert = cert;\n            }\n          } else {\n            if (curCert) {\n              if (cert) {\n                socket.sniPlugin = curCert.name;\n              } else {\n                remoteCerts.del(servername);\n                curCert = null;\n              }\n            }\n          }\n          callback(curCert);\n        };\n        if (cbList) {\n          return cbList.push(handleCert);\n        }\n        certCallbacks[cbKey] = [handleCert];\n        return pluginMgr.loadCert(socket, plugin, function (cert) {\n          cbList = certCallbacks[cbKey];\n          delete certCallbacks[cbKey];\n          cbList &&\n            cbList.forEach(function (handleCb) {\n              handleCb(cert);\n            });\n        });\n      }\n    }\n  }\n  curCert && remoteCerts.del(servername);\n  callback();\n};\n"
  },
  {
    "path": "lib/index.js",
    "content": "var express = require('express');\nvar http = require('http');\nvar https = require('https');\nvar socks = require('sockx');\nvar extend = require('extend');\nvar EventEmitter = require('events');\nvar util = require('./util');\nvar logger = require('./util/logger');\nvar rules = require('./rules');\nvar setupHttps = require('./https').setup;\nvar httpsUtil = require('./https/ca');\nvar rulesUtil = require('./rules/util');\nvar initDataServer = require('./util/data-server');\nvar initLogServer = require('./util/log-server');\nvar pluginMgr = require('./plugins');\nvar config = require('./config');\nvar loadService = require('./service');\nvar initSocketMgr = require('./socket-mgr');\nvar tunnelProxy = require('./tunnel');\nvar upgradeProxy = require('./upgrade');\nvar proc = require('./util/process');\nvar perf = require('./util/perf');\nvar loadCert = require('./https/load-cert');\nvar common = require('./util/common');\n\nfunction handleClientError(err, socket) {\n  if (!socket.writable) {\n    return socket.destroy(err);\n  }\n  var errCode = err && err.code;\n  var statusCode = errCode === 'HPE_HEADER_OVERFLOW' ? 431 : 400;\n  var stack = util.getErrorStack(\n    'clientError: Bad request' + (errCode ? ' (' + errCode + ')' : '')\n  );\n  socket.end('HTTP/1.1 ' + statusCode + ' Bad Request\\r\\n\\r\\n' + stack);\n}\n\nfunction proxy(callback, _server) {\n  var app = express();\n  var server = _server || http.createServer();\n  var proxyEvents = new EventEmitter();\n  var middlewares = ['./init', '../biz']\n    .concat(require('./inspectors'))\n    .concat(config.middlewares)\n    .concat(require('./handlers'));\n  server.timeout = config.timeout;\n  server.requestTimeout = 0;\n  proxyEvents.config = config;\n  proxyEvents.server = server;\n  app.disable('x-powered-by');\n  app.logger = logger;\n  middlewares.forEach(function (mw) {\n    mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));\n  });\n  server.on('clientError', handleClientError);\n  pluginMgr.setProxy(proxyEvents);\n  perf.setProxy(proxyEvents);\n  initSocketMgr(proxyEvents);\n  setupHttps(server, proxyEvents);\n  exportInterfaces(proxyEvents);\n  tunnelProxy(server, proxyEvents);\n  upgradeProxy(server);\n  initDataServer(proxyEvents);\n  initLogServer(proxyEvents);\n  rulesUtil.setup(proxyEvents);\n  var properties = rulesUtil.properties;\n  if (config.disableAllRules) {\n    properties.set('disabledAllRules', true);\n  } else if (config.disableAllRules === false) {\n    properties.set('disabledAllRules', false);\n  }\n  if (config.disableAllPlugins) {\n    properties.set('disabledAllPlugins', true);\n  } else if (config.disableAllPlugins === false) {\n    properties.set('disabledAllPlugins', false);\n  }\n  if (config.allowMultipleChoice) {\n    properties.set('allowMultipleChoice', true);\n  } else if (config.allowMultipleChoice === false) {\n    properties.set('allowMultipleChoice', false);\n  }\n  rulesUtil.addValues(config.values, config.replaceExistValue);\n  rulesUtil.addRules(config.rules, config.replaceExistRule);\n  config.debug && rules.disableDnsCache();\n  var count = _server ? 1 : 2;\n  var execCallback = function () {\n    if (--count === 0) {\n      process.whistleStarted = true;\n      process.emit('whistleStarted', proxyEvents);\n      typeof callback === 'function' && callback.call(server, proxyEvents);\n    }\n  };\n  !_server && util.getBoundIp(config.host, function (host) {\n    util.checkPort(!config.INADDR_ANY && !host && config.port, function () {\n      config.host = host;\n      server.listen(config.port, host, execCallback);\n    });\n  });\n  var createNormalServer = function (port, httpModule, opts) {\n    if (!port) {\n      return;\n    }\n    ++count;\n    var optionServer = httpModule.createServer(opts);\n    var isHttps = !!opts;\n    proxyEvents[isHttps ? 'httpsServer' : 'httpServer'] = optionServer;\n    optionServer.timeout = config.timeout;\n    optionServer.requestTimeout = 0;\n    optionServer.on('request', function (req, res) {\n      req.isHttps = isHttps;\n      app.handle(req, res);\n    });\n    optionServer.isHttps = isHttps;\n    tunnelProxy(optionServer, proxyEvents, isHttps ? 1 : 2);\n    upgradeProxy(optionServer);\n    optionServer.on('clientError', handleClientError);\n    util.getBoundIp(\n      config[isHttps ? 'httpsHost' : 'httpHost'],\n      function (host) {\n        util.checkPort(!config.INADDR_ANY && !host && port, function () {\n          optionServer.listen(port, host, execCallback);\n        });\n      }\n    );\n  };\n  createNormalServer(config.httpPort, http);\n  createNormalServer(\n    config.httpsPort,\n    https,\n    extend(\n      {\n        SNICallback: function (servername, callback) {\n          var curUrl = 'https://' + servername;\n          loadCert(\n            {\n              isHttpsServer: true,\n              fullUrl: curUrl,\n              curUrl: curUrl,\n              useSNI: true,\n              headers: {},\n              servername: servername,\n              serverName: servername,\n              commonName: httpsUtil.getDomain(servername)\n            },\n            function () {\n              httpsUtil.SNICallback(servername, callback);\n            }\n          );\n        }\n      },\n      httpsUtil.createCertificate('*.wproxy.org')\n    )\n  );\n  if (config.socksPort) {\n    ++count;\n    var boundHost;\n    var socksServer = socks.createServer(function (info, accept, deny) {\n      var dstPort = info.dstPort;\n      var dstAddr = info.dstAddr;\n      var connPath = util.joinIpPort(dstAddr, dstPort);\n      var headers = { host: connPath };\n      var clientIp = util.removeIPV6Prefix(info.srcAddr) || '127.0.0.1';\n      var clientPort = info.srcPort;\n      headers['x-whistle-server'] = 'socks';\n      headers[config.CLIENT_INFO_HEADER] = [clientIp, clientPort, clientIp, clientPort].join();\n      if (config.socksMode) {\n        headers[config.WHISTLE_POLICY_HEADER] = 'weakTunnel';\n      }\n      var client = http.request({\n        method: 'CONNECT',\n        agent: false,\n        path: connPath,\n        host: boundHost,\n        port: config.port,\n        headers: headers\n      });\n      var destroy = function () {\n        if (client) {\n          client.abort();\n          client = null;\n          deny();\n        }\n      };\n      client.on('error', destroy);\n      client.on('connect', function (res, socket) {\n        socket.on('error', destroy);\n        if (res.statusCode != 200) {\n          return destroy();\n        }\n        var reqSock = accept(true);\n        if (reqSock) {\n          reqSock.pipe(socket).pipe(reqSock);\n        } else {\n          destroy();\n        }\n      });\n      client.end();\n    });\n    proxyEvents.socksServer = socksServer;\n    util.getBoundIp(config.socksHost, function (host) {\n      boundHost = host || '127.0.0.1';\n      util.checkPort(\n        !config.INADDR_ANY && !host && config.socksPort,\n        function () {\n          socksServer.listen(config.socksPort, host, execCallback);\n        }\n      );\n      socksServer.useAuth(socks.auth.None());\n    });\n  }\n  require('../biz/init')(proxyEvents, function () {\n    server.on('request', app);\n    execCallback();\n  });\n  return proxyEvents;\n}\n\nfunction exportInterfaces(obj) {\n  obj.getWhistlePath = common.getWhistlePath;\n  obj.rules = rules;\n  obj.util = util;\n  obj.rulesUtil = rulesUtil;\n  obj.rulesMgr = rules;\n  obj.httpsUtil = httpsUtil;\n  obj.pluginMgr = pluginMgr;\n  obj.logger = logger;\n  obj.loadService = loadService;\n  obj.setAuth = config.setAuth;\n  obj.setUIHost = config.setUIHost;\n  obj.setPluginUIHost = config.setPluginUIHost;\n  obj.socketMgr = initSocketMgr;\n  obj.getRuntimeInfo = function () {\n    return proc;\n  };\n  obj.getShadowRules = function () {\n    return config.shadowRules;\n  };\n  obj.setShadowRules = function (shadowRules) {\n    if (typeof shadowRules === 'string') {\n      config.shadowRules = shadowRules;\n      rulesUtil.parseRules();\n    }\n  };\n  return obj;\n}\nvar HTTP2_ERR_RE = /^ERR_HTTP2_/;\nfunction handleGlobalException(err) {\n  var code = err && err.code;\n  if (\n    !config.diagnose &&\n    (code === 'EPIPE' ||\n    HTTP2_ERR_RE.test(code) ||\n    code === 'ENETUNREACH' ||\n    code === 'ERR_HTTP_TRAILER_INVALID' ||\n    code === 'ERR_INTERNAL_ASSERTION' ||\n    code === 'ERR_INVALID_ARG_TYPE' ||\n    (err && /finishwrite/i.test(err.message)))\n  ) {\n    return;\n  }\n  if (\n    !err ||\n    (code !== 'ERR_IPC_CHANNEL_CLOSED' && code !== 'ERR_IPC_DISCONNECTED')\n  ) {\n    var stack = util.getErrorStack(err);\n    common.writeLogSync('\\r\\n' + stack + '\\r\\n');\n    /*eslint no-console: \"off\"*/\n    console.error(stack);\n    if (\n      typeof process.handleUncauthtWhistleErrorMessage === 'function' &&\n      process.handleUncauthtWhistleErrorMessage(stack, err) === false\n    ) {\n      return;\n    }\n  }\n  setTimeout(function () {\n    process.exit(1);\n  }, 360);\n}\n\nprocess.on('unhandledRejection', handleGlobalException);\nprocess.on('uncaughtException', handleGlobalException);\n\nrulesUtil.setPluginMgr(pluginMgr);\nrulesUtil.parseRules();\n\nmodule.exports = exportInterfaces(proxy);\n"
  },
  {
    "path": "lib/init.js",
    "content": "var PipeStream = require('pipestream');\nvar util = require('./util');\nvar socketMgr = require('./socket-mgr');\nvar config = require('./config');\n\nvar HTTPS_RE = /^https:/i;\nvar clientIpKey = config.CLIENT_IP_HEADER;\n\nfunction addErrorEvents(req, res) {\n  ++util.proc.allHttpRequests;\n  ++util.proc.totalAllHttpRequests;\n  var finished;\n  var countdown = function () {\n    if (req.isLogRequests) {\n      --util.proc.httpRequests;\n      req._hasClosed = true;\n      req.emit('_closed');\n    }\n    req.isLogRequests = false;\n    if (!finished) {\n      finished = true;\n      --util.proc.allHttpRequests;\n    }\n  };\n  var clientReq;\n  var done;\n  req\n    .on('dest', function (_req) {\n      clientReq = _req;\n      if (!req.noReqBody) {\n        clientReq.on('error', abort);\n      }\n    })\n    .on('error', abort);\n  res\n    .on('src', function (_res) {\n      if (clientReq && req.noReqBody) {\n        clientReq.on('error', abort);\n      }\n      _res.on('error', abort);\n    })\n    .on('error', abort)\n    .once('close', abort)\n    .once('finish', countdown);\n\n  function abort(err) {\n    if (clientReq === false) {\n      return;\n    }\n    countdown();\n    req._hasError = true;\n    clientReq = req._clientReq || clientReq;\n    if (clientReq) {\n      if (clientReq.destroy) {\n        clientReq.destroy();\n      } else if (clientReq.abort) {\n        clientReq.abort();\n      }\n      clientReq = false;\n    }\n    if (done) {\n      return;\n    }\n    done = true;\n    if (req.customParser) {\n      socketMgr.removeContext(req);\n      socketMgr.removePending(req);\n    }\n\n    if (\n      req._hasRespond ||\n      res._headerSent ||\n      !res.writable ||\n      (err && err.code === 'ERR_WHISTLE_ABORTED')\n    ) {\n      if (!finished) {\n        res.emit('error', new Error('Aborted'));\n      }\n      return res.destroy();\n    }\n    err = util.getErrorStack(err || 'Closed');\n    res.response(util.wrapGatewayError(err));\n  }\n}\n\nfunction addTransforms(req, res) {\n  var reqIconvPipeStream, resIconvPipeStream, svrRes, initedResTransform;\n\n  req.addTextTransform = function (transform) {\n    if (!reqIconvPipeStream) {\n      reqIconvPipeStream = util.getPipeIconvStream(req.headers);\n      initReqZipTransform().add(reqIconvPipeStream);\n    }\n    reqIconvPipeStream.add(transform);\n    return req;\n  };\n\n  req.addZipTransform = function (transform, head, tail) {\n    initReqZipTransform()[head ? 'addHead' : tail ? 'addTail' : 'add'](\n      transform\n    );\n    return req;\n  };\n\n  function initReqZipTransform() {\n    if (!req._needGunzip) {\n      delete req.headers['content-length'];\n      req._needGunzip = true;\n    }\n    return req;\n  }\n\n  function initResZipTransform() {\n    if (!initedResTransform) {\n      initedResTransform = true;\n      res._needGunzip = true;\n      removeContentLength();\n      res.add(function (src, next) {\n        if (resIconvPipeStream) {\n          var pipeIconvStream = util.getPipeIconvStream(res.headers);\n          pipeIconvStream.add(resIconvPipeStream);\n          next(src.pipe(pipeIconvStream));\n        } else {\n          next(src);\n        }\n      });\n    }\n  }\n\n  res.addZipTransform = function (transform, head, tail) {\n    initResZipTransform();\n    res[head ? 'addHead' : tail ? 'addTail' : 'add'](transform);\n    return res;\n  };\n  res.addTextTransform = function (transform, head, tail) {\n    if (!resIconvPipeStream) {\n      resIconvPipeStream = new PipeStream();\n      initResZipTransform();\n    }\n    resIconvPipeStream[head ? 'addHead' : tail ? 'addTail' : 'add'](transform);\n    return res;\n  };\n\n  res.on('src', function (_res) {\n    svrRes = _res;\n    removeContentLength();\n  });\n\n  function removeContentLength() {\n    if (svrRes && res._needGunzip) {\n      delete svrRes.headers['content-length'];\n    }\n  }\n}\n\nmodule.exports = function (req, res, next) {\n  util.removeSpecPath(req);\n  PipeStream.wrapSrc(req);\n  PipeStream.wrapDest(res);\n  addTransforms(req, res);\n  addErrorEvents(req, res);\n  req.isPluginReq = util.checkPluginReqOnce(req);\n  var headers = req.headers;\n  var socket = req.socket || {};\n  var clientInfo = util.parseClientInfo(req);\n  var clientIp = clientInfo[0] || util.getForwardedFor(headers);\n  req._remoteAddr = clientInfo[2] || util.getRemoteAddr(req);\n  req._remotePort = clientInfo[3] || util.getRemotePort(req);\n  if (clientIp && util.isLocalAddress(clientIp)) {\n    delete headers[clientIpKey];\n    clientIp = null;\n  }\n  if (!socket[clientIpKey]) {\n    socket[clientIpKey] = clientIp || util.getClientIp(req);\n  }\n  req.clientIp = clientIp = clientIp || socket[clientIpKey];\n  req.method = util.getMethod(req.method);\n  req._clientId = util.getComposerClientId(headers);\n  var clientPort = clientInfo[1] || headers[config.CLIENT_PORT_HEADER];\n  delete headers[config.CLIENT_PORT_HEADER];\n  if (!(clientPort > 0)) {\n    clientPort = null;\n  }\n  if (!socket[config.CLIENT_PORT_HEADER]) {\n    socket[config.CLIENT_PORT_HEADER] = clientPort || socket.remotePort;\n  }\n  req.clientPort = clientPort = clientPort || socket[config.CLIENT_PORT_HEADER];\n  util.handleForwardedProps(req);\n  var isHttps =\n    req.socket.isHttps || req.isHttps || headers[config.HTTPS_FIELD];\n  if (isHttps) {\n    req.isHttps = true;\n    delete headers[config.HTTPS_FIELD];\n    delete headers[util.HTTPS_PROTO_HEADER];\n  }\n  var sniPlugin = headers[util.SNI_PLUGIN_HEADER];\n  if (sniPlugin) {\n    req.sniPlugin = sniPlugin;\n    delete headers[util.SNI_PLUGIN_HEADER];\n  }\n  if (!req.isHttps && HTTPS_RE.test(req.url)) {\n    req.isHttps = true;\n  }\n  util.addTunnelData(socket, headers);\n  var alpn = headers[config.ALPN_PROTOCOL_HEADER];\n  if (alpn) {\n    var index = typeof alpn === 'string' ? alpn.indexOf('.') : -1;\n    if (index > 0) {\n      req._alpn = alpn;\n      req._h2ReqId = alpn.substring(index + 1);\n      alpn = alpn.substring(0, index);\n      // 如果保留 h2 session，则删除默认的 Timeout 逻辑\n      req.setTimeout(0);\n    }\n    if (alpn === 'httpH2') {\n      req.isH2 = !req.isHttps;\n    } else if (alpn === 'httpsH2') {\n      req.isH2 = req.isHttps;\n    } else if (req.isHttps || req.isPluginReq) {\n      req.isH2 = true;\n    }\n    req.rawHeaders = [];\n    delete headers[config.ALPN_PROTOCOL_HEADER];\n    delete headers.connection;\n  }\n  var conn = headers['proxy-connection'];\n  if (conn) {\n    if (headers.connection) {\n      headers.connection = conn;\n    }\n    delete headers['proxy-connection'];\n  }\n  res.response = function (_res) {\n    if (req._hasRespond) {\n      return;\n    }\n    _res.once('readable', function() {\n      req._ttfb = Date.now();\n      req.setTTFB && req.setTTFB();\n    });\n    req._hasRespond = true;\n    if (_res.realUrl) {\n      req.realUrl = res.realUrl = _res.realUrl;\n    }\n    res.headers = req.resHeaders = _res.headers;\n    res.statusCode =\n      req.statusCode =\n      _res.statusCode =\n        util.getStatusCode(_res.statusCode);\n    util.drain(req, function () {\n      if (util.getStatusCode(_res.statusCode)) {\n        res.src(_res);\n        res.writeHead(_res.statusCode, _res.headers);\n      } else {\n        util.sendStatusCodeError(res, _res);\n      }\n    });\n  };\n\n  next();\n};\n"
  },
  {
    "path": "lib/inspectors/data.js",
    "content": "var EventEmitter = require('events').EventEmitter;\nvar zlib = require('../util/zlib');\nvar util = require('../util');\nvar socketMgr = require('../socket-mgr');\nvar config = require('../config');\n\nvar MAX_BODY_SIZE = 360 * 1024;\nvar MAX_SIZE = (config.strict ? 256 : 1024) * 1024;\nvar MAX_REQ_BODY_SIZE = (config.strict ? 256 : 2048) * 1024;\nvar MAX_RES_BODY_SIZE = (config.strict ? 256 : 2048) * 1024;\nvar BIG_DATA_SIZE = 1024 * 1024 * 16;\nvar LOCALHOST = '127.0.0.1';\nvar SSE_RE = /^\\s*text\\/event-stream/i;\n\nfunction getZipType(options) {\n  return options.headers && options.headers['content-encoding'];\n}\n\nfunction unzipBody(options, body, callback) {\n  return zlib.unzip(getZipType(options), body, callback);\n}\n\nfunction checkType(res) {\n  if (!config.strict) {\n    return true;\n  }\n  var type = res.headers['content-type'];\n  if (!type) {\n    return true;\n  }\n  type = util.getContentType(type);\n  return type && type !== 'CSS' && type !== 'IMG';\n}\n\nfunction checkBodySize(data, useBigData) {\n  if (\n    config.strict &&\n    data.body &&\n    data.body.length > (useBigData ? BIG_DATA_SIZE : MAX_BODY_SIZE)\n  ) {\n    data.body = '';\n  }\n}\n\nfunction getEventName(proxy) {\n  if (util.listenerCount(proxy, '_request')) {\n    return '_request';\n  } else if (util.listenerCount(proxy, 'httpRequest')) {\n    return 'httpRequest';\n  }\n}\n\nfunction emitDataEvents(req, res, proxy) {\n  var now = Date.now();\n  var eventName = getEventName(proxy);\n  eventName && proxy.emit(eventName, req.fullUrl);\n  if (!util.showPluginReq(req) || util.isHide(req)) {\n    return;\n  }\n  var getTTFB = function () {\n    if (req._ttfb >= now) {\n      return req._ttfb - now;\n    }\n  };\n  var _res = {};\n  var reqEmitter = new EventEmitter();\n  var reqData = {\n    method: util.toUpperCase(req.method) || 'GET',\n    httpVersion: req.httpVersion || '1.1',\n    ip: req.clientIp,\n    port: req.clientPort,\n    isWhistleHttps: req.isWhistleHttps,\n    rawHeaderNames: req.rawHeaderNames,\n    headers: req.headers\n  };\n  var resData = {};\n  var reqBody = false;\n  var resBody = false;\n  var cleared;\n  var data = {\n    useH2: req.isH2,\n    h2Id: req._alpn,\n    fc: req.fromComposer ? 1 : undefined,\n    isPR: req.isPluginReq ? 1 : undefined,\n    _clientId: req._clientId,\n    startTime: now,\n    ttfb: getTTFB(),\n    id: req.reqId,\n    sniPlugin: req.sniPlugin,\n    url: req.fullUrl,\n    req: reqData,\n    res: resData,\n    rules: req.rules,\n    fwdHost: req._fwdHost,\n    pipe: req._pipeRule,\n    rulesHeaders: req.rulesHeaders,\n    abort: function (clear) {\n      if (clear === true) {\n        data = reqData = resData = reqBody = resBody = false;\n        cleared = true;\n      } else {\n        var err = new Error('Aborted');\n        err.code = 'ERR_WHISTLE_ABORTED';\n        req.emit('error', err);\n      }\n    }\n  };\n  proxy.emit('request', reqEmitter, data);\n\n  var requestTime;\n  var endTime;\n  var updateEvent;\n  var useFrames;\n  var enable = req.enable;\n  var disable = req.disable;\n  var useBigData =\n    (enable.bigData || enable.largeData) &&\n    !disable.bigData &&\n    !disable.largeData;\n  var setRequestTime = function (time) {\n    data.requestTime = time;\n    data.connectTime = req._connectTime;\n  };\n  var setEndTime = function () {\n    if (data.requestTime || !requestTime) {\n      data.endTime = endTime || Date.now();\n      setRequestTime(data.requestTime || data.endTime);\n    } else if (endTime == null) {\n      endTime = endTime || Date.now();\n    }\n  };\n  var updateVersion = function () {\n    if (req.useH2 != null) {\n      data.useH2 = req.useH2;\n    }\n    data.httpsTime = req.httpsTime;\n    data.useHttp = req.useHttp;\n    if (data.useH2 && !req.useHttp) {\n      reqData.httpVersion = '2.0';\n    } else {\n      reqData.httpVersion = '1.1';\n    }\n  };\n  var setUnzipSize = function (body, obj) {\n    var len = body ? body.length : -1;\n    if (len >= 0 && len !== obj.size) {\n      obj.unzipSize = len;\n    }\n  };\n  req.setServerPort = function (serverPort) {\n    req.serverPort = serverPort;\n    setReqStatus(LOCALHOST);\n    resData.port = serverPort;\n  };\n  req.setClientId = function (clientId) {\n    data.clientId = clientId;\n  };\n  req.setTTFB = function() {\n    data.ttfb = getTTFB();\n  };\n  res.setCurTrailers = function (trailers, rawTrailerNames) {\n    resData.trailers = trailers;\n    resData.rawTrailerNames = rawTrailerNames;\n  };\n  var reqDone;\n  var resDone;\n  var captureReqStream;\n  var maxReqSize;\n  var reqInfo;\n  var updateReqInfo = function() {\n    if (reqInfo && !captureReqStream) {\n      captureReqStream = !disable.captureStream && (resDone || enable.captureStream) && !getZipType(reqInfo);\n      maxReqSize = useBigData ? BIG_DATA_SIZE : (captureReqStream ? MAX_REQ_BODY_SIZE : MAX_SIZE);\n      if (captureReqStream && reqBody && !reqData.body) {\n        reqData.body = reqBody;\n      }\n    }\n  };\n\n  var handleReqBody = function (stream, info) {\n    if (reqDone) {\n      return;\n    }\n    reqDone = true;\n    reqInfo = info || (req._needGunzip ? { method: req.method, headers: '' } : req);\n    if (!cleared && util.hasRequestBody(reqInfo)) {\n      reqBody = null;\n    }\n    reqData.size = 0;\n    updateReqInfo();\n    var write = stream.write;\n    var end = stream.end;\n    stream.write = function (chunk) {\n      if (chunk) {\n        if (reqBody || reqBody === null) {\n          if (useFrames) {\n            reqBody = '';\n          } else {\n            reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;\n            if (captureReqStream) {\n              reqData.body = reqBody;\n            }\n            if (reqBody.length > maxReqSize) {\n              reqBody = false;\n            }\n          }\n        }\n        reqData.size += chunk.length;\n      }\n      updateEvent && proxy.emit(updateEvent, req.fullUrl, false);\n      return write.apply(this, arguments);\n    };\n    stream.end = function (chunk) {\n      if (chunk) {\n        reqData.size += chunk.length;\n        reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;\n      }\n      requestTime = Date.now();\n      if (useFrames) {\n        reqBody = '';\n      }\n      unzipBody(reqInfo, reqBody, function (err, body) {\n        setRequestTime(requestTime);\n        if (endTime) {\n          data.endTime = endTime;\n        }\n        reqBody = err ? util.getErrorStack(err) : body;\n        reqData.body = reqBody;\n        setUnzipSize(body, reqData);\n        checkBodySize(reqData, useBigData);\n      });\n      updateEvent && proxy.emit(updateEvent, req.fullUrl, false, true);\n      return end.apply(this, arguments);\n    };\n  };\n  var handleResBody = function (stream, info) {\n    if (resDone) {\n      return;\n    }\n    resDone = true;\n    info = info || (res._needGunzip ? { statusCode: res.statusCode, headers: '' } : res);\n    var captureStream = !disable.captureStream && (requestTime == null || enable.captureStream ||\n      (info.headers && SSE_RE.test(info.headers['content-type']))) && !getZipType(info);\n    var maxResSize = useBigData ? BIG_DATA_SIZE : (captureStream ? MAX_RES_BODY_SIZE : MAX_SIZE);\n    captureStream && updateReqInfo();\n    if (!cleared && util.hasBody(info, req) && checkType(info)) {\n      if (info.headers['content-length'] > maxResSize && !captureStream) {\n        resBody = false;\n      } else {\n        resBody = null;\n      }\n    }\n    resData.size = 0;\n    var write = stream.write;\n    var end = stream.end;\n    stream.write = function (chunk) {\n      if (chunk) {\n        if (resBody || resBody === null) {\n          if (useFrames) {\n            resBody = '';\n          } else {\n            resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;\n            if (captureStream) {\n              resData.body = resBody;\n            }\n            if (resBody.length > maxResSize) {\n              resBody = false;\n            }\n          }\n        }\n        resData.size += chunk.length;\n      }\n      updateEvent && proxy.emit(updateEvent, req.fullUrl, true);\n      return write.apply(this, arguments);\n    };\n    stream.end = function (chunk) {\n      if (chunk) {\n        resData.size += chunk.length;\n        resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;\n      }\n      endTime = Date.now();\n      delete data.abort;\n      if (useFrames) {\n        resBody = '';\n      }\n      resData.hasGzipError = unzipBody(info, resBody, function (err, body) {\n        if (!data.resError) {\n          resBody = err ? util.getErrorStack(err) : body;\n          resData.body = resBody;\n        }\n        setUnzipSize(body, resData);\n        checkBodySize(resData, useBigData);\n        setEndTime();\n        reqEmitter.emit('end', data);\n      });\n      updateEvent && proxy.emit(updateEvent, req.fullUrl, true, true);\n      return end.apply(this, arguments);\n    };\n  };\n\n  var hasReqPipe = req._pipePluginPorts.reqWritePort;\n  var hasResPipe = req._pipePluginPorts.resWritePort;\n\n  if (hasReqPipe) {\n    req.on('bodyStreamReady', handleReqBody);\n  }\n\n  if (hasResPipe) {\n    res.on('bodyStreamReady', handleResBody);\n  }\n\n  req.once('dest', function (_req) {\n    _req.once('finish', function () {\n      if (!requestTime) {\n        setRequestTime(requestTime);\n      }\n    });\n    setReqStatus();\n    reqEmitter.emit('send', data);\n    !hasReqPipe && handleReqBody(_req, req);\n  });\n  res.once('src', function (r) {\n    _res = r;\n    data.pipe = req._pipeRule;\n    resData.rawHeaderNames = res.rawHeaderNames;\n    if (!data.endTime) {\n      setResStatus();\n      reqEmitter.emit('response', data);\n    }\n    !hasResPipe && handleResBody(res, _res);\n  });\n  req.once('error', handleError);\n  res.once('error', handleError);\n  res.once('close', handleError);\n  res.once('finish', setEndTime);\n\n  function handleError(err) {\n    req._hasError = true;\n    if (!data || endTime || data.endTime || (data.responseTime && !err)) {\n      return;\n    }\n    !endTime && setEndTime();\n    delete data.abort;\n    if (err && err.message !== 'Aborted') {\n      data.resError = true;\n      if (resData.body) {\n        data.errMsg = util.getErrorStack(err);\n      } else {\n        resData.body = util.getErrorStack(err);\n      }\n      util.emitError(reqEmitter, data);\n      setResStatus(502);\n    } else {\n      data.reqError = true;\n      if (!reqData.body) {\n        reqData.body = 'aborted';\n      } else if (!resData.body) {\n        resData.body = 'aborted';\n      } else {\n        data.errMsg = 'aborted';\n      }\n      if (req.__resHeaders) {\n        _res.headers = req.__resHeaders;\n      }\n      reqEmitter.emit('abort', data);\n      setResStatus('aborted' + (req.__statusCode ? ' (' + req.__statusCode + ')' : ''));\n    }\n  }\n\n  function setReqStatus(defaultHost) {\n    data.dnsTime = (req.dnsTime || 0) + now;\n    data.realUrl = data.url === req.realUrl ? undefined : req.realUrl;\n    updateVersion();\n    resData.ip = req.hostIp || defaultHost;\n    resData.phost = req._phost && req._phost.host;\n  }\n\n  req.initCustomParser = function () {\n    if (req.customParser) {\n      data.useFrames = false;\n      socketMgr.setPending(req);\n      req.disableCustomParser = function () {\n        data.useFrames = null;\n        req.customParser = null;\n        req.enableCustomParser = null;\n        req.disableCustomParser = null;\n        socketMgr.removePending(req);\n        delete req.headers['x-whistle-frame-parser'];\n      };\n      req.enableCustomParser = function (svrRes) {\n        useFrames = true;\n        reqData.body = '';\n        resData.body = '';\n        reqData.base64 = '';\n        resData.base64 = '';\n        data.useFrames = true;\n        socketMgr.setContext(\n          req,\n          svrRes,\n          eventName,\n          { data: [] },\n          { data: [] }\n        );\n        socketMgr.removePending(req);\n        delete req.headers['x-whistle-frame-parser'];\n      };\n    }\n  };\n\n  function setResStatus(defaultCode) {\n    if (data.responseTime) {\n      return;\n    }\n    setReqStatus(LOCALHOST);\n    resData.statusCode = _res.statusCode || defaultCode || 502;\n    resData.statusMessage = _res.statusMessage;\n    data.responseTime = Date.now();\n    if (!requestTime && !data.requestTime) {\n      updateEvent = eventName;\n    }\n    resData.headers = _res.headers;\n  }\n}\n\nmodule.exports = function (req, res, next) {\n  emitDataEvents(req, res, this);\n  util.delay(util.getMatcherValue(req.rules.reqDelay), function () {\n    if (util.needAbortReq(req)) {\n      return res.destroy();\n    }\n    next();\n  });\n};\n"
  },
  {
    "path": "lib/inspectors/index.js",
    "content": "module.exports = [\n  './rules',\n  './req',\n  './data',\n  './res'\n].map(function (mod) {\n  return require(mod);\n});\n"
  },
  {
    "path": "lib/inspectors/log.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar Transform = require('pipestream').Transform;\nvar util = require('../util');\nvar config = require('../config');\nvar logScriptFile = path.join(config.ASSESTS_PATH, 'js/log.js');\nvar logScript = fs.readFileSync(logScriptFile, { encoding: 'utf8' });\nvar logScriptTag = '<script>' + logScript + '</script>\\r\\n';\nvar logHtmlScript = '<!DOCTYPE html>\\r\\n' + logScriptTag;\nvar LOG_ID_RE = /^log:\\/\\/(\\{[^\\s]{1,36}\\}|[^/\\\\{}()<>\\s]{1,36})$/;\n\nfunction wrapScript(script, isHtml) {\n  return isHtml\n    ? '\\r\\n<script>' + script + '</script>\\r\\n'\n    : '\\r\\n' + script + '\\r\\n';\n}\n\nfunction getScript(host, isHtml, req, noDoctype) {\n  host = util.getInternalHost(req, host);\n  var logCgiPath =\n    config.WEBUI_PATH + 'log.' + config.port + '/cgi-bin/log/set';\n  var result = isHtml ? (noDoctype ? logScriptTag : logHtmlScript) : logScript;\n  var baseUrl = (req.isHttps ? 'https://' : 'http://') + host;\n  return result.replace('$BASE_URL', baseUrl).replace('$LOG_CGI', logCgiPath);\n}\n\nmodule.exports = function (req, res) {\n  var log = req.rules.log;\n  if (log) {\n    util.disableReqCache(req.headers);\n    var host = req.headers.host;\n    res.on('src', function (_res) {\n      var topScript, isHtml;\n      if (util.supportHtmlTransform(_res, req)) {\n        isHtml = true;\n        topScript = getScript(host, isHtml, req, util.isDisable(req, 'logDoctype'));\n      } else if (util.getContentType(_res.headers) == 'JS') {\n        topScript = getScript(host, isHtml, req, util.isDisable(req, 'logDoctype'));\n      }\n\n      if (topScript) {\n        var enable = req.enable;\n        topScript = topScript.split('$INTERCEPT_CONSOLE').join(!req.disable.interceptConsole || !!enable.interceptConsole);\n        !util.isEnable(req, 'keepAllCSP') && util.disableCSP(_res.headers);\n        !req._customCache && util.disableResStore(_res.headers);\n        var userScript;\n        var transform = new Transform();\n        var added;\n        transform._transform = function (chunk, _, callback) {\n          if (!added) {\n            added = true;\n            var logId = '';\n            var isValue;\n            if (LOG_ID_RE.test(log.matcher)) {\n              logId = RegExp.$1;\n              if (logId[0] === '{') {\n                logId = logId.slice(1, -1);\n                isValue = true;\n              }\n              try {\n                logId = encodeURIComponent(logId);\n              } catch (e) {}\n            }\n            util.getRuleValue(\n              logId && !isValue ? null : log,\n              function (script) {\n                topScript = topScript.replace('$LOG_ID', logId);\n                var buf = [util.toBuffer(topScript)];\n                userScript = script || null;\n                if (userScript) {\n                  userScript = util.toBuffer(wrapScript(userScript, isHtml));\n                  if (isHtml || !chunk) {\n                    buf.push(userScript);\n                    userScript = null;\n                  }\n                }\n                chunk && buf.push(chunk);\n                callback(null, Buffer.concat(buf));\n              },\n              null,\n              null,\n              null,\n              req\n            );\n          } else {\n            callback(null, chunk || userScript);\n          }\n        };\n\n        res.addZipTransform(transform, false, true);\n      }\n    });\n  }\n};\n"
  },
  {
    "path": "lib/inspectors/req.js",
    "content": "var qs = require('querystring');\nvar iconv = require('iconv-lite');\nvar net = require('net');\nvar util = require('../util');\nvar extend = require('extend');\nvar mime = require('mime');\nvar pluginMgr = require('../plugins');\nvar config = require('../config');\nvar handleWeinre = require('./weinre');\nvar handleLog = require('./log');\n\nvar Transform = require('pipestream').Transform;\nvar WhistleTransform = util.WhistleTransform;\nvar ReplacePatternTransform = util.ReplacePatternTransform;\nvar ReplaceStringTransform = util.ReplaceStringTransform;\nvar FileWriterTransform = util.FileWriterTransform;\n\nvar JSON_RE = /{[\\w\\W]*}|\\[[\\w\\W]*\\]/;\nvar BUOUNDARY_RE = /boundary=(?:\"([^\"]+)\"|([^;]+))/i;\nvar MAX_REQ_SIZE = 1024 * 1024 * (config.strict ? 1 : 2); // 2MB\nvar BIG_MAX_REQ_SIZE = 1024 * 1024 * 16;\nvar MAX_HEADER_SIZE = 1024 * 8;\nvar CRLF = Buffer.from('\\r\\n');\nvar CR = CRLF[0];\nvar LF = CRLF[1];\nvar LINE = Buffer.from('-')[0];\nvar UPLOAD_CTN_SEP = Buffer.from('\\r\\n\\r\\n');\nvar CTN_DIS = 'Content-Disposition: form-data; name=\"';\nvar clientIpKey = config.CLIENT_IP_HEADER;\n\nvar REQ_TYPE = {\n  urlencoded: 'application/x-www-form-urlencoded',\n  form: 'application/x-www-form-urlencoded',\n  json: 'application/json',\n  xml: 'application/xml',\n  text: 'text/plain',\n  upload: 'multipart/form-data',\n  multipart: 'multipart/form-data',\n  defaultType: 'application/octet-stream'\n};\nvar NAME_RE = /name=(?:\"([^\"]+)\"|([^;]+))/i;\n\nfunction getName(firstLine) {\n  if (!NAME_RE.test(firstLine)) {\n    return null;\n  }\n  var name = RegExp.$1;\n  if (name) {\n    return name;\n  }\n  name = RegExp.$2;\n  if (name[0] === '\\'') {\n    var lastIndex = name.length - 1;\n    if (name[lastIndex] === '\\'') {\n      return name.substring(1, lastIndex);\n    }\n  }\n  return name;\n}\n\nfunction toMultipart(name, value) {\n  if (value === undefined) {\n    return null;\n  }\n  if (value == null) {\n    value = '';\n  }\n  if (typeof value == 'object') {\n    var filename = value.filename || value.name;\n    var base64 = value.base64;\n    var type = value.type;\n    filename = filename == null ? name : filename + '';\n    value = value.content || value.value || '';\n    if (value && typeof value === 'object') {\n      try {\n        value = JSON.stringify(value, null, '  ') || '';\n      } catch (e) {}\n    } else if (base64) {\n      try {\n        base64 = Buffer.from(base64, 'base64');\n        value = '';\n      } catch (e) {\n        base64 = '';\n      }\n    }\n    if (!util.isString(type)) {\n      type = '';\n    } else if (type.indexOf('/') === -1) {\n      type = mime.lookup(type, type);\n    }\n    value = Buffer.from(CTN_DIS + name + '\"; filename=\"' + filename\n      + '\"\\r\\nContent-Type: ' + (type || mime.lookup(filename)) + '\\r\\n\\r\\n' + value);\n    return base64 ? Buffer.concat([value, base64]) : value;\n  }\n  return Buffer.from(CTN_DIS + name + '\"\\r\\n\\r\\n' + value);\n}\n\n/**\n * 处理请求数据\n *\n * @param req：method、body、headers，top，bottom，speed、delay，charset, timeout\n * @param data\n */\nfunction handleReq(req, data, reqRules, delType) {\n  extend(req.headers, data.headers);\n  if (reqRules.reqType) {\n    var newType = util.getMatcherValue(reqRules.reqType).split(';');\n    var type = newType[0];\n    newType[0] =\n      !type || type.indexOf('/') != -1\n        ? type\n        : REQ_TYPE[type] || util.lookupType(type);\n    req.headers['content-type'] = util.getNewType(newType.join(';'), req.headers);\n  }\n  util.setCharset(req.headers, data.charset, delType.reqType, delType.reqCharset);\n  if (!util.hasRequestBody(req.method)) {\n    delete data.top;\n    delete data.bottom;\n    delete data.body;\n    delete req.headers['content-length'];\n  } else {\n    util.removeReqBody(req, data);\n    if (data.top || data.bottom || data.body) {\n      delete req.headers['content-length'];\n      req._hasInjectBody = true;\n    }\n  }\n\n  util.isWhistleTransformData(data) &&\n    req.addZipTransform(new WhistleTransform(data));\n  var opList = util.parseHeaderReplace(req.rules.headerReplace).req;\n  if (opList) {\n    var host = req.headers.host;\n    var xff = req.headers[clientIpKey];\n    var clientId = req.headers[config.CLIENT_ID_HEADER];\n    util.handleHeaderReplace(req.headers, opList);\n    if (req.headers.host !== host) {\n      req._customHost = req.headers.host;\n    }\n    var newXFF = req.headers[clientIpKey];\n    if (xff !== newXFF && net.isIP(newXFF)) {\n      req._customXFF = newXFF;\n    }\n    if (clientId !== req.headers[config.CLIENT_ID_HEADER]) {\n      req._customClientId = req.headers[config.CLIENT_ID_HEADER];\n    }\n  }\n}\n\nfunction handleAuth(data, auth) {\n  auth = util.getAuthBasic(auth);\n  auth && util.setHeader(data, 'authorization', auth);\n}\n\nfunction handleParams(req, params, urlParams, enableBigData) {\n  params = util.isEmptyObject(params) ? null : params;\n  var delProps = util.parseDelReqBody(req);\n  var hasBody;\n  var buffer;\n  if (params || delProps) {\n    var maxReqSize = enableBigData || util.isEnable(req, 'reqMergeBigData') ? BIG_MAX_REQ_SIZE : MAX_REQ_SIZE;\n    var transform;\n    var headers = req.headers;\n    var isJson = util.isJSONContent(req);\n    if (isJson || util.isUrlEncoded(req)) {\n      delete headers['content-length'];\n      transform = new Transform();\n      var interrupt;\n      transform._transform = function (chunk, _, callback) {\n        if (chunk) {\n          if (!interrupt) {\n            buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;\n            chunk = null;\n            if (buffer.length > maxReqSize) {\n              interrupt = true;\n              chunk = buffer;\n              buffer = null;\n            }\n          }\n        } else if (buffer && buffer.length) {\n          var body;\n          var isGBK = !util.isUtf8(buffer);\n          if (isGBK) {\n            try {\n              body = iconv.decode(buffer, 'GB18030');\n            } catch (e) {}\n          }\n          if (!body) {\n            body = buffer + '';\n          }\n          if (isJson) {\n            body = body.replace(JSON_RE, function (json) {\n              var obj = util.parseRawJson(json);\n              if (obj) {\n                if (params) {\n                  obj = extend(true, obj, params);\n                }\n                util.deleteProps(obj, delProps);\n                json = JSON.stringify(obj);\n              }\n              return json;\n            });\n          } else {\n            body = util.replaceQueryString(body, params, delProps);\n          }\n          if (isGBK) {\n            try {\n              body = iconv.encode(body, 'GB18030');\n            } catch (e) {}\n          } else {\n            chunk = util.toBuffer(body);\n          }\n          buffer = null;\n        } else if (!interrupt && params) {\n          util.deleteProps(params, delProps);\n          var data = isJson ? JSON.stringify(params) : qs.stringify(params);\n          chunk = util.toBuffer(data);\n        }\n\n        callback(null, chunk);\n      };\n      req.addZipTransform(transform);\n      hasBody = true;\n    } else if (util.isMultipart(req) && BUOUNDARY_RE.test(headers['content-type'])) {\n      delete headers['content-length'];\n      var boundaryStr = '--' + (RegExp.$1 || RegExp.$2);\n      var startBoundary = Buffer.from(boundaryStr + '\\r\\n');\n      var boundary = Buffer.from('\\r\\n' + boundaryStr);\n      var sepBoundary = Buffer.from('\\r\\n' + boundaryStr + '\\r\\n');\n      var endBoudary = Buffer.from('\\r\\n' + boundaryStr + '--');\n      var length = startBoundary.length;\n      var endLength = sepBoundary.length;\n      var minCtnLen = length + endLength;\n      var preBuffer;\n      var ended;\n      var lastChunk;\n      var badMultipart;\n      var pass;\n      var ignore;\n      var result;\n\n      transform = new Transform();\n\n      if (delProps) {\n        params = params || delProps;\n        Object.keys(delProps).forEach(function(key) {\n          params[key] = undefined;\n        });\n      }\n\n      var getRestData = function() {\n        var rest;\n        Object.keys(params).forEach(function(name) {\n          var part = toMultipart(name, params[name]);\n          if (part) {\n            if (rest) {\n              rest.push(sepBoundary);\n            } else {\n              rest = [];\n            }\n            rest.push(part);\n          }\n        });\n        if (!rest) {\n          return null;\n        }\n        return rest.length < 2 ? rest[0] : Buffer.concat(rest);\n      };\n\n      var pushResult = function(buf, addSep) {\n        if (buf && buf.length) {\n          result = result || [];\n          if (addSep) {\n            if (startBoundary) {\n              result.unshift(startBoundary);\n              startBoundary = null;\n            } else {\n              result.push(sepBoundary);\n            }\n          }\n          result.push(buf);\n        }\n      };\n      var getResult = function() {\n        if (ended) {\n          pushResult(getRestData(), true);\n        }\n        if (!result) {\n          return null;\n        }\n        if (ended && !pass && result.length) {\n          result.push(endBoudary);\n        }\n        if (result.length < 2) {\n          return result[0];\n        }\n        return Buffer.concat(result);\n      };\n\n      var getChunk = function() {\n        result = null;\n        while(!ended && buffer.length >= endLength) {\n          var index = buffer.indexOf(boundary);\n          var isMatched = index !== -1;\n          if (isMatched) {\n            var first = buffer[index + length];\n            var sec = buffer[index + length + 1];\n            ended = first === LINE && sec === LINE;\n            isMatched = ended || (first === CR && sec === LF);\n          }\n          if (ignore || pass) {\n            if (!isMatched) {\n              index = -endLength + 1;\n              if (pass) {\n                pushResult(buffer.slice(0, index));\n              }\n              buffer = buffer.slice(index);\n              return getResult();\n            }\n            if (pass) {\n              pushResult(buffer.slice(0, index));\n            }\n            buffer = buffer.slice(index + endLength);\n            ignore = false;\n            pass = false;\n          } else {\n            var part;\n            var ctnIndex;\n            var name;\n            if (isMatched) {\n              part = buffer.slice(0, index);\n              ctnIndex = part.indexOf(UPLOAD_CTN_SEP);\n              if (ctnIndex !== -1) {\n                name = getName(buffer.slice(0, ctnIndex) + '');\n                if (name != null && (name in params)) {\n                  part = toMultipart(name, params[name]);\n                  params[name] = undefined;\n                }\n              }\n              pushResult(part, true);\n              buffer = buffer.slice(index + endLength);\n            } else {\n              ctnIndex = buffer.indexOf(UPLOAD_CTN_SEP);\n              if (ctnIndex !== -1) {\n                name = getName(buffer.slice(0, ctnIndex) + '');\n                pass = name == null || !(name in params);\n                index = -endLength + 1;\n                if (pass) {\n                  pushResult(buffer.slice(0, index), true);\n                } else {\n                  pushResult(toMultipart(name, params[name]), true);\n                  params[name] = undefined;\n                  ignore = true;\n                }\n                buffer = buffer.slice(index);\n              } else {\n                if (buffer.length > MAX_HEADER_SIZE) {\n                  badMultipart = true;\n                  pass = true;\n                  pushResult(buffer, true);\n                }\n                return getResult();\n              }\n            }\n          }\n        }\n        if (pass && lastChunk) {\n          pushResult(buffer);\n        }\n        return getResult();\n      };\n\n      var getCustomData = function() {\n        var chunk = lastChunk ? getRestData() : null;\n        return chunk && Buffer.concat([startBoundary, chunk, endBoudary]);\n      };\n\n      transform._transform = function (chunk, _, callback) {\n        if (ended) {\n          return callback();\n        }\n        if (badMultipart) {\n          return callback(null, chunk);\n        }\n\n        lastChunk = !chunk;\n        if (chunk && preBuffer !== null) {\n          preBuffer = preBuffer ? Buffer.concat([preBuffer, chunk]) : chunk;\n          if (!preBuffer || preBuffer.length <= length) {\n            return callback(null, lastChunk ? preBuffer : null);\n          }\n          if (!util.startWithList(preBuffer, startBoundary)) {\n            lastChunk = true;\n            if (chunk = getCustomData()) {\n              ended = true;\n            } else {\n              badMultipart = true;\n              chunk = preBuffer;\n            }\n            return callback(null, chunk);\n          }\n          chunk = preBuffer.slice(length);\n          preBuffer = null;\n        }\n\n        buffer = buffer && chunk ? Buffer.concat([buffer, chunk]) : (buffer || chunk);\n        if (!buffer || buffer.length < minCtnLen) {\n          return callback(null, getCustomData());\n        }\n        callback(null, getChunk());\n      };\n      req.addZipTransform(transform);\n      hasBody = true;\n    }\n    var matcher = req.rules.params && req.rules.params.matcher;\n    if (matcher && !matcher.indexOf(' params://')) {\n      hasBody = true;\n    }\n  }\n  var _params = hasBody ? null : params;\n  if (_params || urlParams) {\n    req._urlParams =\n      urlParams && _params ? extend(_params, urlParams) : _params || urlParams;\n  }\n}\n\nfunction handleReplace(req, replacement) {\n  if (!util.hasRequestBody(req) || !replacement) {\n    return;\n  }\n\n  var type = req.headers['content-type'];\n  type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);\n  if (!type || type == 'IMG') {\n    return;\n  }\n\n  Object.keys(replacement).forEach(function (pattern) {\n    var value = replacement[pattern];\n    if (\n      util.isOriginalRegExp(pattern) &&\n      (pattern = util.toOriginalRegExp(pattern))\n    ) {\n      req.addTextTransform(new ReplacePatternTransform(pattern, value));\n    } else if (pattern) {\n      req.addTextTransform(new ReplaceStringTransform(pattern, value));\n    }\n  });\n}\n\nmodule.exports = function (req, res, next) {\n  handleWeinre(req, res);\n  handleLog(req, res);\n  var reqRules = req.rules;\n  var authObj = util.getAuthByRules(reqRules);\n  var reqMerge = reqRules.params;\n\n  util.parseRuleJson(\n    [\n      reqRules.reqHeaders,\n      reqRules.reqCookies,\n      authObj ? null : reqRules.auth,\n      reqMerge,\n      reqRules.reqCors,\n      reqRules.reqReplace,\n      reqRules.urlReplace,\n      reqRules.urlParams\n    ],\n    function (\n      headers,\n      cookies,\n      auth,\n      params,\n      cors,\n      replacement,\n      urlReplace,\n      urlParams\n    ) {\n      var data = {};\n      if (headers) {\n        data.headers = extend(data.headers || {}, headers);\n      }\n      if (data.body && typeof data.body !== 'string') {\n        try {\n          data.body = JSON.stringify(data.body);\n        } catch (e) {}\n      }\n\n      if (data.headers) {\n        data.headers = util.lowerCaseify(data.headers, req.rawHeaderNames);\n        req._customHost = data.headers.host;\n        req._customXFF = data.headers[clientIpKey];\n        req._customClientId = data.headers[config.CLIENT_ID_HEADER];\n        if (typeof data.headers['content-type'] !== 'string') {\n          delete data.headers['content-type'];\n        }\n      }\n\n      if (reqRules.method) {\n        var method = util.getMatcherValue(reqRules.method);\n        data.method = method;\n      }\n\n      if (reqRules.reqCharset) {\n        data.charset = util.getMatcherValue(reqRules.reqCharset);\n      }\n\n      if (reqRules.referer) {\n        var referer = util.getMatcherValue(reqRules.referer);\n        util.setHeader(data, 'referer', referer);\n      }\n\n      if (reqRules.ua) {\n        var ua = util.getMatcherValue(reqRules.ua);\n        util.setHeader(data, 'user-agent', ua);\n      }\n\n      var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);\n      reqSpeed = reqSpeed && parseFloat(reqSpeed);\n      if (reqSpeed > 0) {\n        data.speed = reqSpeed;\n      }\n      var cookie = data.headers && data.headers.cookie;\n      if (typeof cookie !== 'string') {\n        if (Array.isArray(cookie)) {\n          cookie = cookie.join('; ');\n        } else {\n          cookie = req.headers.cookie;\n        }\n      }\n      util.setReqCookies(data, cookies, cookie, req);\n      handleAuth(data, auth || authObj);\n      util.setReqCors(data, cors);\n      req.method = util.getMethod(data.method || req.method);\n      var bodyList = [\n        reqRules.reqBody,\n        reqRules.reqPrepend,\n        reqRules.reqAppend\n      ];\n      util.getRuleValue(\n        bodyList,\n        function (reqBody, reqPrepend, reqAppend) {\n          if (reqBody != null) {\n            data.body = reqBody || util.EMPTY_BUFFER;\n          }\n\n          if (reqPrepend) {\n            data.top = reqPrepend;\n          }\n\n          if (reqAppend) {\n            data.bottom = reqAppend;\n          }\n          var delType = {};\n          var dProps = util.parseDelQuery(req, delType);\n          var queryProps = dProps.query;\n          var pathProps = dProps.paths;\n          handleReq(req, data, reqRules, delType);\n          handleParams(req, params, urlParams, reqMerge && reqMerge.lineProps.enableBigData);\n          if (req._urlParams || urlReplace || pathProps || queryProps || req._delQueryString) {\n            var options = req.options;\n            var isUrl = util.isUrl(options.href);\n            var newUrl = isUrl ? options.href : req.fullUrl;\n            if (req._urlParams) {\n              newUrl = util.replaceUrlQueryString(newUrl, req._urlParams);\n            }\n            newUrl = util.parsePathReplace(newUrl, urlReplace, pathProps) || newUrl;\n            newUrl = util.deleteQuery(newUrl, queryProps, req._delQueryString);\n            if (newUrl !== options.href) {\n              if (isUrl) {\n                req.options = util.parseUrl(newUrl);\n              } else {\n                req._realUrl = newUrl;\n              }\n            }\n          }\n          util.removeUnsupportsHeaders(req.headers);\n          util.disableReqProps(req);\n          handleReplace(req, replacement);\n          var reqWriter = util.hasRequestBody(req)\n            ? util.getWriteFilePath(reqRules.reqWrite)\n            : null;\n          util.getFileWriters(\n            [reqWriter, util.getWriteFilePath(reqRules.reqWriteRaw)],\n            function (writer, rawWriter) {\n              if (writer) {\n                req.addZipTransform(\n                  new FileWriterTransform(writer, req, false, false, true)\n                );\n              }\n              if (rawWriter) {\n                req.addZipTransform(\n                  new FileWriterTransform(rawWriter, req, true, false, true)\n                );\n              }\n              pluginMgr.postStats(req);\n              next();\n            },\n            util.isEnable(req, 'forceReqWrite')\n          );\n        },\n        !util.hasRequestBody(req.method),\n        null,\n        null,\n        req\n      );\n    },\n    req\n  );\n};\n"
  },
  {
    "path": "lib/inspectors/res.js",
    "content": "var https = require('https');\nvar http = require('http');\nvar net = require('net');\nvar parseUrl = require('../util/parse-url-safe');\nvar extend = require('extend');\nvar util = require('../util');\nvar Transform = require('pipestream').Transform;\nvar h2 = require('../https/h2');\nvar rules = require('../rules');\nvar pluginMgr = require('../plugins');\nvar hparser = require('hparser');\nvar config = require('../config');\n\nvar WhistleTransform = util.WhistleTransform;\nvar SpeedTransform = util.SpeedTransform;\nvar ReplacePatternTransform = util.ReplacePatternTransform;\nvar ReplaceStringTransform = util.ReplaceStringTransform;\nvar FileWriterTransform = util.FileWriterTransform;\nvar formatHeaders = hparser.formatHeaders;\nvar getRawHeaderNames = hparser.getRawHeaderNames;\n\nvar TIMEOUT = config.timeout < 16000 && config.timeout > 0 ? 0 : 16000;\nvar LOCALHOST = '127.0.0.1';\nvar CRLF = util.toBuffer('\\r\\n');\nvar MAX_RES_SIZE = 1024 * 1024 * (config.strict ? 1 : 2); // 2MB\nvar BIG_MAX_RES_SIZE = 1024 * 1024 * 16;\nvar JSON_RE = /{[\\w\\W]*}|\\[[\\w\\W]*\\]/;\nvar LIKE_JSON_RE = /^\\s*[\\{\\[]/;\nvar NO_STORE_RE = /^no-store$/i;\nvar NO_CACHE_RE = /^(?:no|no-cache|no-store)$/i;\nvar X_RE = /^x/;\nvar clientIpKey = config.CLIENT_IP_HEADER;\nvar BODY_PROTOCOLS = [\n  'attachment',\n  'resReplace',\n  'resBody',\n  'resPrepend',\n  'resAppend',\n  'htmlBody',\n  'htmlPrepend',\n  'htmlAppend',\n  'jsBody',\n  'jsPrepend',\n  'jsAppend',\n  'cssBody',\n  'cssPrepend',\n  'cssAppend',\n  'resWrite',\n  'resWriteRaw',\n  'resMerge'\n];\nvar BODY_PROTOCOLS_LEN = BODY_PROTOCOLS.length;\n\nfunction notAllowCache(resRules) {\n  for (var i = 0; i < BODY_PROTOCOLS_LEN; i++) {\n    if (resRules[BODY_PROTOCOLS[i]]) {\n      return true;\n    }\n  }\n}\n\nfunction joinArr(src, dest) {\n  if (!src || !dest) {\n    return src || dest;\n  }\n  return src.concat(dest);\n}\n\nfunction pipeClient(req, client) {\n  if (req._hasError) {\n    client.destroy();\n  } else if (req.noReqBody) {\n    util.drain(req, function () {\n      if (!req._hasError) {\n        client.end();\n      }\n    });\n  } else {\n    req.pipe(client);\n  }\n}\n\nfunction showDnsError(res, err) {\n  res.response(\n    util.wrapGatewayError('DNS Lookup Failed\\r\\n' + util.getErrorStack(err))\n  );\n}\n\nfunction setCookies(headers, data) {\n  var newCookies = data.headers['set-cookie'];\n  if (!Array.isArray(newCookies)) {\n    if (!newCookies || typeof newCookies !== 'string') {\n      return;\n    }\n    newCookies = newCookies.split(',');\n  }\n  if (newCookies.length) {\n    var cookies = headers['set-cookie'];\n    var isArray = Array.isArray(cookies);\n    if (!isArray && cookies) {\n      isArray = true;\n      cookies = [String(cookies)];\n    }\n    if (isArray) {\n      var newNameMap = {};\n      newCookies.forEach(function (cookie) {\n        var index = cookie.indexOf('=');\n        var name = index == -1 ? name : cookie.substring(0, index);\n        newNameMap[name] = 1;\n      });\n      cookies.forEach(function (cookie) {\n        var index = cookie.indexOf('=');\n        var name = index == -1 ? name : cookie.substring(0, index);\n        if (!newNameMap[name]) {\n          newCookies.push(cookie);\n        }\n      });\n    }\n    headers['set-cookie'] = newCookies;\n    delete data.headers['set-cookie'];\n  }\n}\n\nfunction handleReplace(res, replacement) {\n  if (!replacement) {\n    return;\n  }\n\n  var type = util.getContentType(res.headers);\n  if (!type || type == 'IMG') {\n    return;\n  }\n\n  Object.keys(replacement).forEach(function (pattern) {\n    var value = replacement[pattern];\n    if (\n      util.isOriginalRegExp(pattern) &&\n      (pattern = util.toOriginalRegExp(pattern))\n    ) {\n      res.addTextTransform(new ReplacePatternTransform(pattern, value, util.isSSE(res)));\n    } else if (pattern) {\n      res.addTextTransform(new ReplaceStringTransform(pattern, value, util.isSSE(res)));\n    }\n  });\n}\n\nfunction getWriterFile(file, statusCode) {\n  if (!file || statusCode == 200) {\n    return file;\n  }\n\n  return file + '.' + statusCode;\n}\n\nfunction readFirstChunk(req, res, src, cb) {\n  var ports = req._pipePluginPorts;\n  if (!cb) {\n    if (ports.reqReadPort || ports.reqWritePort) {\n      delete req.headers['content-length'];\n    }\n    return ports.reqReadPort ? req.getPayload(res, 1) : res();\n  }\n  if (ports.resReadPort || ports.resWritePort) {\n    delete src.headers['content-length'];\n  }\n  if (!ports.resReadPort) {\n    return cb();\n  }\n  res.prepareSrc(src, function (stream) {\n    util.readOneChunk(stream, cb);\n  });\n}\n\nfunction checkH2(req, isHttps) {\n  if (!config.enableH2) {\n    return;\n  }\n  req.useH2 = req.isH2;\n  var d = req.disable;\n  var e = req.enable;\n  if (isHttps) {\n    if (d.h2 || d.http2 || d.httpsH2) {\n      req.useH2 = false;\n    } else if (e.h2 || e.http2 || e.httpsH2) {\n      req.useH2 = true;\n    }\n  } else {\n    if (d.httpH2) {\n      req.useH2 = false;\n    } else if (e.httpH2) {\n      req.useH2 = true;\n    }\n    req.useHttpH2 = req.useH2;\n  }\n}\n\nmodule.exports = function (req, res, next) {\n  var origProto;\n  var resRules = req.rules;\n\n  req.request = function (options) {\n    readFirstChunk(req, function () {\n      options = options || req.options;\n      req.realUrl = res.realUrl = options.isPlugin\n        ? req._realUrl || req.fullUrl\n        : options.href;\n      var originPort = options.port;\n      var originHost = options.host;\n      var originDomain = options.hostname;\n      var now = Date.now();\n      rules.getClientCert(req, function (key, cert, isPfx, cacheKey) {\n        rules.getProxy(\n          options.href,\n          options.isPlugin ? null : req,\n          function (err, hostIp, hostPort) {\n            var proxyRule = resRules.proxy;\n            var proxyUrl =\n              !options.isPlugin && proxyRule\n                ? util.rule.getMatcher(proxyRule)\n                : null;\n            var headers = req.headers;\n            var auth, isInternalProxy, isHttpsProxy, origPath, origProxy;\n            if (!hostIp) {\n              if (options.localDNS) {\n                req.curUrl = '';\n              } else if (proxyUrl) {\n                isHttpsProxy = proxyRule.isHttps;\n                isInternalProxy = proxyRule.isInternal || util.isInternalProxy(req);\n                if (isInternalProxy) {\n                  req._isInternalProxy = true;\n                  if (options.protocol === 'https:') {\n                    headers[config.HTTPS_FIELD] = 1;\n                    origProto = options.protocol;\n                    options.protocol = null;\n                  }\n                } else if (proxyRule.isHttp2https) {\n                  options.protocol = 'https:';\n                }\n                req.curUrl = 'http:' + util.removeProtocol(proxyUrl);\n              } else {\n                req.curUrl = options.href;\n              }\n            }\n            if (!req.setServerPort) {\n              req.setServerPort = function (port) {\n                req.serverPort = port;\n              };\n            }\n            rules.resolveHost(req, function (err, ip, port, hostRule) {\n              var setHostsInfo = function (_ip, _port, _host, withPort) {\n                ip = _ip || '127.0.0.1';\n                port = _port;\n                req.dnsTime = Date.now() - now;\n                req.hostIp = util.joinIpPort(_ip, withPort && _port);\n                if (_host) {\n                  resRules.host = _host;\n                }\n              };\n              if (proxyUrl && proxyRule && hostRule) {\n                proxyRule.host = hostRule;\n                hostRule = null;\n              }\n              setHostsInfo(hostIp || ip, hostPort || port, hostRule);\n              if (err) {\n                showDnsError(res, err);\n                return;\n              }\n              if (req.disable.keepalive) {\n                req.disable.keepAlive = true;\n              }\n              var isHttps = options.protocol == 'https:';\n              var proxyOptions, isProxyPort, isSocks;\n              var setAgent = function (disable) {\n                if (disable || req.disable.keepAlive || (isHttps && cert)) {\n                  options.agent = false;\n                } else {\n                  options.agent = isHttps ? config.httpsAgent : config.httpAgent;\n                }\n              };\n              checkH2(req, isHttps);\n              if (proxyUrl) {\n                proxyOptions = parseUrl(proxyUrl);\n                proxyOptions.host = ip;\n                proxyOptions.auth = proxyOptions.auth || req._pacAuth;\n                isSocks = proxyRule.isSocks;\n                var proxyPort = proxyOptions.port;\n                if (!proxyPort) {\n                  proxyPort = proxyOptions.port = isSocks ? 1080 : isHttpsProxy ? 443 : 80;\n                }\n                if (proxyOptions.auth) {\n                  auth = 'Basic ' + util.toBuffer(proxyOptions.auth + '').toString('base64');\n                } else {\n                  auth = headers['proxy-authorization'];\n                }\n                if (\n                    isHttps ||\n                    (req.useH2 && !isInternalProxy) ||\n                    isSocks ||\n                    isHttpsProxy ||\n                    req._phost\n                  ) {\n                  isProxyPort = util.isProxyPort(proxyPort);\n                  if (isProxyPort && util.isLocalAddress(ip)) {\n                    req.setServerPort(config.port);\n                    res.response(\n                        util.wrapResponse({\n                          statusCode: 302,\n                          headers: {\n                            location:\n                              'http://' +\n                              util.joinIpPort(ip, config.port) +\n                              (options.path || '')\n                          }\n                        })\n                      );\n                  } else {\n                    var curServerPort = options.port || (isHttps ? 443 : 80);\n                    var proxyHeaders = {\n                      host: util.joinIpPort(options.hostname, curServerPort),\n                      'proxy-connection': req.disable.proxyConnection\n                          ? 'close'\n                          : 'keep-alive'\n                    };\n                    pluginMgr.getTunnelKeys().forEach(function (k) {\n                      var val = headers[k];\n                      if (val) {\n                        proxyHeaders[k] = val;\n                      }\n                    });\n                    if (auth) {\n                      proxyHeaders['proxy-authorization'] = auth;\n                    }\n                    if (req.disable.proxyUA) {\n                      delete proxyHeaders['user-agent'];\n                    } else if (headers['user-agent']) {\n                      proxyHeaders['user-agent'] = headers['user-agent'];\n                    }\n                    if (!util.isLocalAddress(req.clientIp)) {\n                      proxyHeaders[clientIpKey] = req.clientIp;\n                    }\n                    if (isHttps || req.useH2) {\n                      util.checkIfAddInterceptPolicy(proxyHeaders, headers);\n                    }\n                    if (isProxyPort) {\n                      proxyHeaders[config.WEBUI_HEAD] = 1;\n                    }\n                    if (util.isProxyPort(curServerPort) || util.isLocalPHost(req, isHttps)) {\n                      headers[config.WEBUI_HEAD] = 1;\n                    }\n                    var clientId = req.headers[config.CLIENT_ID_HEADER];\n                    if (clientId) {\n                      proxyHeaders[config.CLIENT_ID_HEADER] = clientId;\n                    }\n                    util.setClientId(\n                        proxyHeaders,\n                        req.enable,\n                        req.disable,\n                        req.clientIp,\n                        isInternalProxy\n                      );\n                    var phost = req._phost;\n                    var opts = {\n                      isSocks: isSocks,\n                      isHttps: isHttps,\n                      _phost: phost,\n                      proxyServername: isHttpsProxy\n                          ? proxyOptions.hostname\n                          : null,\n                      proxyHost: ip,\n                      clientIp: proxyHeaders[clientIpKey],\n                      proxyPort: proxyPort,\n                      url: options.href,\n                      auth: proxyOptions.auth,\n                      headers: proxyHeaders\n                    };\n                    if (phost) {\n                      options.host = phost.hostname;\n                      if (phost.port > 0) {\n                        options.port = phost.port;\n                      } else if (!options.port) {\n                        options.port = isHttps ? 443 : 80;\n                      }\n                      proxyHeaders.host = util.joinIpPort(options.host, options.port);\n                    } else {\n                      options.host = options.hostname;\n                    }\n                    options._proxyOptions = opts;\n                    opts.proxyType = isSocks\n                        ? 'socks'\n                        : isHttpsProxy\n                        ? 'https'\n                        : 'http';\n                    options._proxyPort = opts.proxyPort;\n                    origProxy = opts;\n                    request(options);\n                  }\n                  return;\n                }\n\n                if (auth) {\n                  headers['proxy-authorization'] = auth;\n                }\n              }\n\n              req.hostIp = util.joinIpPort(ip, port);\n              port = proxyOptions ? proxyOptions.port : port || options.port;\n              options.host = ip; //设置ip\n              var curPort = port || (isHttps ? 443 : 80);\n              isProxyPort = util.isProxyPort(curPort);\n              var isLocalAddress = util.isLocalAddress(options.host);\n              if (isProxyPort && isLocalAddress) {\n                var redirectHost = config.customLocalUIHost || ip;\n                var redirectPort = config.realPort || config.port;\n                res.response(\n                    util.wrapResponse({\n                      statusCode: 302,\n                      headers: {\n                        location:\n                          'http://' +\n                          util.joinIpPort(redirectHost, redirectPort) +\n                          (options.path || '')\n                      }\n                    })\n                  );\n              } else {\n                if (\n                    isProxyPort ||\n                    util.isProxyPort(options.port || (isHttps ? 443 : 80)) ||\n                    util.isLocalPHost(req, isHttps)\n                  ) {\n                  headers[config.WEBUI_HEAD] = 1;\n                }\n                setAgent(isLocalAddress);\n                request(options, port, true);\n              }\n              function request(options, serverPort, direct) {\n                options.headers = headers;\n                options.method = req.method;\n                util.setRejectUnauthorized(req, options);\n                if (\n                    !options.isPlugin &&\n                    !req._customHost &&\n                    (req.fullUrl !== req.realUrl || !headers.host)\n                  ) {\n                  headers.host = originHost;\n                }\n                if (req.disable.keepAlive) {\n                  headers.connection = 'close';\n                }\n                if (direct) {\n                  options.port = serverPort;\n                  if (proxyUrl) {\n                    origPath = options.path || '';\n                  }\n                }\n\n                delete options.hostname; //防止自动dns\n                delete options.protocol;\n                if (isHttps && !req.disable.servername) {\n                  options.servername = util.parseHost(headers.host)[0];\n                }\n                var piped;\n                var maxRetryCount = 1;\n                var retryCount = 0;\n                var retryXHost = 0;\n                var resetCount = 0;\n                var curClient, timer, newIp;\n                var setProxyAgent = function (options, proxyOpts) {\n                  proxyOpts.cacheKey = options.cacheKey;\n                  proxyOpts.proxyTunnelPath = util.getProxyTunnelPath(\n                      req,\n                      isHttps\n                    );\n                  proxyOpts.enableIntercept = true;\n                  options.agent = proxyOpts.isSocks\n                      ? config.getSocksAgent(proxyOpts)\n                      : config.getHttpsAgent(proxyOpts, options);\n                };\n                var retry = function (err) {\n                  clearTimeout(timer);\n                  timer = null;\n                  if (curClient) {\n                    curClient.removeListener('error', retry);\n                    curClient.removeListener('close', retry);\n                    curClient.on('error', util.noop);\n                    curClient.destroy();\n                    curClient = null;\n                  }\n                  if (req._hasError || req._hasRespond) {\n                    return;\n                  }\n                  if (err) {\n                      // Chrome 浏览器自动兼容 127.0.0.1 和 ::1\n                    if (util.notStarted(err)) {\n                      if (!newIp && (newIp = util.getLocalhostIP(err, req, originDomain, options.host))) {\n                        options.host = newIp;\n                        setHostsInfo(newIp);\n                      }\n                    } else if (isHttps && !options.ciphers && util.isCiphersError(err)) {\n                      extend(options, util.getTlsOptions(resRules));\n                      return send();\n                    }\n                  }\n                  if (retryCount >= maxRetryCount) {\n                    var toHttp;\n                    if (\n                        isHttps &&\n                        (!piped || req.noReqBody) &&\n                        util.checkTlsError(err) &&\n                        util.checkAuto2Http(req, ip, proxyUrl)\n                      ) {\n                      isHttps = false;\n                      toHttp = true;\n                      req.httpsTime = req.httpsTime || Date.now();\n                      req.useHttp = true;\n                      if (origProxy) {\n                        origProxy.isHttps = false;\n                        if (req._phost && !req._phost.port) {\n                          options.port = 80;\n                          origProxy.headers.host =\n                              req._phost.hostname + ':80';\n                        }\n                        setProxyAgent(options, origProxy);\n                      } else {\n                        setAgent(util.isLocalAddress(ip));\n                      }\n                    }\n                    var code = err && err.code;\n                    if (\n                        !toHttp &&\n                        (resetCount > 1 ||\n                          (code !== 'EPROTO' && code !== 'ECONNRESET') ||\n                          (piped && !req.noReqBody))\n                      ) {\n                      var stack = util.getErrorStack(\n                          err || new Error('socket connect timeout')\n                        );\n                      res.response(util.wrapGatewayError(stack));\n                    } else {\n                      ++resetCount;\n                      send();\n                    }\n                    return;\n                  }\n                  ++retryCount;\n                  if (proxyUrl) {\n                    if (X_RE.test(proxyUrl)) {\n                      proxyUrl = '';\n                      req._phost = undefined;\n                      if (isInternalProxy) {\n                        isHttps = origProto === 'https:';\n                      }\n                      req.curUrl = req.realUrl;\n                      delete options._proxyPort;\n                      rules.resolveHost(\n                          req,\n                          function (_err, _ip, _port, _host) {\n                            setAgent(util.isLocalAddress(_ip));\n                            setHostsInfo(_ip, _port, _host, true);\n                            if (_err) {\n                              showDnsError(res, _err);\n                              return;\n                            }\n                            options.host = ip;\n                            options.port = _port || originPort;\n                            ++maxRetryCount;\n                            send();\n                          }\n                        );\n                      return;\n                    }\n                  } else if (\n                      retryXHost < 2 &&\n                      req.rules.host &&\n                      X_RE.test(req.rules.host.matcher)\n                    ) {\n                    ++maxRetryCount;\n                    ++retryXHost;\n                    if (retryXHost > 1) {\n                      req.curUrl = req.realUrl;\n                      delete options._proxyPort;\n                      rules.lookupHost(req, function (_err, _ip) {\n                        setHostsInfo(_ip);\n                        if (_err) {\n                          showDnsError(res, _err);\n                          return;\n                        }\n                        options.host = ip;\n                        options.port = originPort;\n                        send();\n                      });\n                      return;\n                    }\n                  } else if (\n                      isHttps &&\n                      util.checkAuto2Http(req, ip, proxyUrl)\n                    ) {\n                    ++maxRetryCount;\n                    if (maxRetryCount > 2 || util.checkTlsError(err)) {\n                      isHttps = false;\n                      req.httpsTime = req.httpsTime || Date.now();\n                      req.useHttp = true;\n                      setAgent(util.isLocalAddress(options.host));\n                    }\n                  }\n                  send();\n                };\n                var send = function (sock) {\n                  if (req._hasError) {\n                    return;\n                  }\n                  req.useH2 = false;\n                  req.setServerPort(\n                      options._proxyPort || options.port || (isHttps ? 443 : 80)\n                    );\n                  if (origPath != null && proxyUrl) {\n                    origPath = null;\n                    options.path =\n                        (isHttps ? 'https:' : 'http:') +\n                        '//' +\n                        (headers.host || options.host) +\n                        (options.path || '/');\n                  }\n                  var useHttps = isHttps;\n                  if (sock) {\n                    options.agent = null;\n                    options.createConnection = function () {\n                      return sock;\n                    };\n                  } else {\n                    var proxyOpts = options._proxyOptions;\n                    if (proxyOpts) {\n                      if (!req.useHttpH2 || proxyOpts._phost) {\n                        setProxyAgent(options, proxyOpts);\n                      } else {\n                        options.host = proxyOpts.proxyHost;\n                        options.port = proxyOpts.proxyPort;\n                        useHttps = useHttps || isHttpsProxy;\n                      }\n                      delete options._proxyOptions;\n                    }\n                  }\n                  options.protocol = useHttps ? 'https:' : 'http:';\n                  if (useHttps && !req.disable.secureOptions) {\n                    util.setSecureOptions(options);\n                  }\n                  try {\n                    var client = (useHttps ? https : http).request(\n                        options,\n                        res.response\n                      );\n                    curClient = client;\n                    req._clientReq = client;\n                    client.once('error', retry);\n                    client.once('socket', function (socket) {\n                      if (socket.connecting || socket._connecting) {\n                        if (TIMEOUT) {\n                          timer = setTimeout(function () {\n                            socket.destroy();\n                            timer = null;\n                            retry();\n                          }, TIMEOUT);\n                        }\n                        socket.once(\n                            isHttpsProxy || isHttps\n                              ? 'secureConnect'\n                              : 'connect',\n                            function () {\n                              req._connectTime = Date.now();\n                              retryCount = maxRetryCount;\n                              piped = true;\n                              clearTimeout(timer);\n                              timer = null;\n                              pipeClient(req, client);\n                            }\n                          );\n                      } else {\n                        retryCount = maxRetryCount;\n                        piped = true;\n                        pipeClient(req, client);\n                        socket.resume();\n                      }\n                    });\n                  } catch (e) {\n                    res.response(\n                        util.wrapGatewayError(util.getErrorStack(e))\n                      );\n                  }\n                };\n                if (req.disable.clientIp || req.disable.clientIP) {\n                  delete headers[clientIpKey];\n                } else {\n                  var forwardedFor = util.getMatcherValue(\n                      resRules.forwardedFor\n                    );\n                  if (net.isIP(forwardedFor)) {\n                    headers[clientIpKey] = forwardedFor;\n                  } else if (req._customXFF) {\n                    headers[clientIpKey] = req._customXFF;\n                  } else if (\n                      (!options.isPlugin &&\n                        !req.enable.clientIp &&\n                        !req.enable.clientIP &&\n                        !req.enableXFF &&\n                        (isHttps || isSocks || !proxyUrl)) ||\n                      util.isLocalAddress(req.clientIp)\n                    ) {\n                    delete headers[clientIpKey];\n                  } else {\n                    headers[clientIpKey] = req.clientIp;\n                  }\n                }\n                util.deleteReqHeaders(req);\n                var optHeaders = options.headers;\n                var transfer =\n                    options.method === 'DELETE' &&\n                    optHeaders['transfer-encoding'];\n                if (transfer) {\n                  delete optHeaders['transfer-encoding'];\n                }\n                var clientId = optHeaders[config.CLIENT_ID_HEADER];\n                if (clientId) {\n                  if (!options.isPlugin && !req._customClientId && !util.isKeepClientId(req, proxyUrl)) {\n                    req._origClientId = clientId;\n                    util.removeClientId(optHeaders);\n                  }\n                  req.setClientId && req.setClientId(clientId);\n                } else {\n                  util.setClientId(\n                      optHeaders,\n                      req.enable,\n                      req.disable,\n                      req.clientIp,\n                      isInternalProxy\n                    );\n                }\n                if (\n                    req.useH2 &&\n                    (isInternalProxy ||\n                      headers[config.HTTPS_FIELD] ||\n                      options.isPlugin)\n                  ) {\n                  headers[config.ALPN_PROTOCOL_HEADER] = 'h2';\n                }\n                options.headers = optHeaders = formatHeaders(\n                    optHeaders,\n                    req.rawHeaderNames\n                  );\n                delete headers[config.WEBUI_HEAD];\n                delete headers[config.HTTPS_FIELD];\n                delete headers[config.ALPN_PROTOCOL_HEADER];\n                if (transfer) {\n                  optHeaders['Transfer-Encoding'] = transfer;\n                }\n                if (options.isPlugin) {\n                  optHeaders[config.PLUGIN_HOOK_NAME_HEADER] =\n                      config.PLUGIN_HOOKS.HTTP;\n                }\n                req.noReqBody = !util.hasRequestBody(req);\n                if (\n                    req.method === 'DELETE' &&\n                    (req._hasInjectBody ||\n                      req.headers['transfer-encoding'] ||\n                      req.headers['content-length'] > 0)\n                  ) {\n                  req.useH2 = false;\n                }\n                req.setServerPort(\n                    options._proxyPort || options.port || (isHttps ? 443 : 80)\n                  );\n                req.options = options;\n                isHttps &&\n                    util.setClientCert(options, key, cert, isPfx, cacheKey);\n                util.addMatchedRules(req);\n                h2.request(req, res, send);\n              }\n            },\n              req.pluginRules,\n              req.rulesFileMgr,\n              req.headerRulesMgr\n            );\n          }\n        );\n      });\n    });\n  };\n\n  res.response = function (_res) {\n    if (req._hasRespond) {\n      return;\n    }\n    _res.once('readable', function() {\n      req._ttfb = Date.now();\n      req.setTTFB && req.setTTFB();\n    });\n    req._hasRespond = true;\n    res._srcResponse = _res;\n    if (_res.realUrl) {\n      req.realUrl = res.realUrl = _res.realUrl;\n    }\n    var headers = _res.headers;\n    var useFrames;\n    res.headers = req.resHeaders = headers;\n    res._originEncoding = headers['content-encoding'];\n    req.statusCode = _res.statusCode;\n    if (_res.rawHeaderNames) {\n      res.rawHeaderNames = _res.rawHeaderNames;\n    } else {\n      res.rawHeaderNames = _res.rawHeaderNames = Array.isArray(_res.rawHeaders)\n        ? getRawHeaderNames(_res.rawHeaders)\n        : {};\n    }\n    _res.on('error', function (err) {\n      res.emit('error', err);\n    });\n    if (!req.isPluginReq && headers[config.PROXY_ID_HEADER] === 'h2') {\n      req.useH2 = true;\n      delete headers[config.PROXY_ID_HEADER];\n    }\n    if (req.disable.additionalHeaders) {\n      delete headers[util.ADDITIONAL_HEAD];\n    }\n    util.drain(req, function () {\n      readFirstChunk(req, res, _res, function (firstChunk) {\n        pluginMgr.getResRules(req, _res, function () {\n          var replaceStatus = util.getMatcherValue(resRules.replaceStatus);\n          if (replaceStatus && replaceStatus != _res.statusCode) {\n            res.statusCode = _res.statusCode = replaceStatus;\n            if (!util.isDisableUserLogin(resRules.replaceStatus, req)) {\n              util.handleStatusCode(replaceStatus, headers);\n            }\n          }\n          if (req.disable['301'] && _res.statusCode == 301) {\n            res.statusCode = _res.statusCode = 302;\n          }\n          var resMerge = resRules.resMerge;\n          var ruleList = [\n            resRules.resHeaders,\n            resRules.resCookies,\n            resRules.resCors,\n            resRules.resReplace,\n            resMerge,\n            resRules.trailers\n          ];\n          util.parseRuleJson(\n            ruleList,\n            function (\n              headers,\n              cookies,\n              cors,\n              replacement,\n              params,\n              newTrailers\n            ) {\n              var data = {};\n              if (headers) {\n                data.headers = extend(data.headers || {}, headers);\n              }\n              if (data.body && typeof data.body !== 'string') {\n                try {\n                  data.body = JSON.stringify(data.body);\n                } catch (e) {}\n              }\n              if (data.headers) {\n                data.headers = util.lowerCaseify(\n                  data.headers,\n                  res.rawHeaderNames\n                );\n                if (typeof data.headers['content-type'] !== 'string') {\n                  delete data.headers['content-type'];\n                }\n              }\n\n              util.setResCookies(_res, cookies, req);\n              util.setResCors(_res, cors, req);\n\n              var cache = util.getMatcherValue(resRules.cache);\n              var enable = req.enable;\n              if (cache === 'reserve' || cache === 'keep' || util.isEnable(req, 'keepAllCache')) {\n                req._customCache = true;\n              } else {\n                var maxAge = parseInt(cache, 10);\n                var noCache = NO_CACHE_RE.test(cache) || maxAge < 0;\n                if (maxAge >= 0 || noCache) {\n                  req._customCache = true;\n                  util.setHeaders(data, {\n                    'cache-control': noCache\n                      ? NO_STORE_RE.test(cache)\n                        ? 'no-store'\n                        : 'no-cache'\n                      : 'max-age=' + maxAge,\n                    expires: new Date(\n                      Date.now() + (noCache ? -60000000 : maxAge * 1000)\n                    ).toGMTString(),\n                    pragma: noCache ? 'no-cache' : ''\n                  });\n                }\n              }\n\n              if (resRules.attachment) {\n                var attachment =\n                  util.getMatcherValue(resRules.attachment) ||\n                  util.getFilename(req.fullUrl);\n                util.setHeader(\n                  data,\n                  'content-disposition',\n                  'attachment; filename=\"' +\n                    util.encodeNonLatin1Char(attachment) +\n                    '\"'\n                );\n              }\n\n              if (resRules.resCharset) {\n                data.charset = util.getMatcherValue(resRules.resCharset);\n              }\n\n              var resSpeed = util.getMatcherValue(resRules.resSpeed);\n              resSpeed = resSpeed && parseFloat(resSpeed);\n              if (resSpeed > 0) {\n                data.speed = resSpeed;\n              }\n\n\n              var _resHeaders = _res.headers;\n              if (data.headers) {\n                setCookies(_resHeaders, data);\n                extend(_resHeaders, data.headers);\n              }\n\n              if (resRules.resType) {\n                var newType = util.getMatcherValue(resRules.resType).split(';');\n                var type = newType[0];\n                newType[0] =\n                  !type || type.indexOf('/') != -1\n                    ? type\n                    : util.lookupType(type);\n                _resHeaders['content-type'] = util.getNewType(newType.join(';'), _resHeaders);\n              }\n              var delProps = util.parseDelProps(req);\n              util.setCharset(_resHeaders, data.charset, delProps.resType, delProps.resCharset);\n              if (!_resHeaders.pragma) {\n                delete _resHeaders.pragma;\n              }\n              var hr = util.parseHeaderReplace(resRules.headerReplace);\n              util.handleHeaderReplace(_resHeaders, hr.res);\n              if (_resHeaders.location) {\n                //nodejs的url只支持ascii，对非ascii的字符要encodeURIComponent，否则传到浏览器是乱码\n                _resHeaders.location = util.encodeNonLatin1Char(_resHeaders.location);\n              }\n              var resType = util.getContentType(_resHeaders);\n              var charset = util.getCharset(_resHeaders['content-type']);\n              var isHtml = resType === 'HTML';\n              var isJs = isHtml || resType === 'JS';\n              var isCss = isHtml || resType === 'CSS';\n              var hasResBody = util.hasBody(_res, req);\n              var injectRules = [\n                resRules.resBody,\n                resRules.resPrepend,\n                resRules.resAppend,\n                isHtml && resRules.htmlAppend,\n                isJs && resRules.jsAppend,\n                isCss && resRules.cssAppend,\n                isHtml && resRules.htmlBody,\n                isJs && resRules.jsBody,\n                isCss && resRules.cssBody,\n                isHtml && resRules.htmlPrepend,\n                isJs && resRules.jsPrepend,\n                isCss && resRules.cssPrepend\n              ];\n              if (isHtml) {\n                data.isHtml = true;\n                if (util.isEnable(req, 'strictHtml')) {\n                  data.strictHtml = true;\n                  injectRules.forEach(function(rule) {\n                    if (rule) {\n                      rule.strictHtml = true;\n                    }\n                  });\n                } else if (util.isEnable(req, 'safeHtml')) {\n                  data.safeHtml = true;\n                  injectRules.forEach(function(rule) {\n                    if (rule) {\n                      rule.safeHtml = true;\n                    }\n                  });\n                }\n              }\n              util.getRuleValue(injectRules, function (\n                  resBody,\n                  resPrepend,\n                  resAppend,\n                  htmlAppend,\n                  jsAppend,\n                  cssAppend,\n                  htmlBody,\n                  jsBody,\n                  cssBody,\n                  htmlPrepend,\n                  jsPrepend,\n                  cssPrepend\n                ) {\n                if (req._hasError) {\n                  return;\n                }\n                if (resBody != null) {\n                  data.body = resBody || util.EMPTY_BUFFER;\n                }\n                data.top = resPrepend;\n                data.bottom = resAppend;\n\n                var speedTransform = data.speed || data.delay ? new SpeedTransform(data) : null;\n                delete data.headers;\n                delete data.speed;\n                delete data.delay;\n                if (isJs || resType === 'JSON' || !_resHeaders['content-type']) {\n                  var delBodyProps = util.parseDelResBody(req);\n                  var maxResSize = (resMerge && resMerge.lineProps.enableBigData) || util.isEnable(req, 'resMergeBigData') ? BIG_MAX_RES_SIZE : MAX_RES_SIZE;\n                  params = util.isEmptyObject(params) ? null : params;\n                  if (params || delBodyProps) {\n                    var transform = new Transform();\n                    var interrupt;\n                    var ctn = '';\n                    transform._transform = function (text, _, callback) {\n                      if (text) {\n                        if (!interrupt) {\n                          ctn += text;\n                          text = null;\n                          if (\n                            ((isHtml || !_resHeaders['content-type']) && !LIKE_JSON_RE.test(ctn)) ||\n                            Buffer.byteLength(ctn) > maxResSize\n                          ) {\n                            interrupt = true;\n                            text = ctn;\n                            ctn = null;\n                          }\n                        }\n                      } else if (ctn) {\n                        text = ctn.replace(JSON_RE, function (json) {\n                          var obj = util.parseRawJson(json);\n                          if (obj) {\n                            if (params) {\n                              obj = extend(true, obj, params);\n                            }\n                            util.deleteProps(obj, delBodyProps);\n                            json = JSON.stringify(obj);\n                          }\n                          return json;\n                        });\n                        ctn = null;\n                      } else if (!interrupt && params) {\n                        util.deleteProps(params, delBodyProps);\n                        try {\n                          text = JSON.stringify(params);\n                        } catch (e) {}\n                      }\n\n                      callback(null, text);\n                    };\n                    res.addTextTransform(transform);\n                  }\n                }\n                var top, body, bottom;\n                if (isHtml) {\n                  top = joinArr(data.top, cssPrepend);\n                  top = joinArr(top, htmlPrepend);\n                  data.top = joinArr(top, jsPrepend);\n                  body = joinArr(data.body, cssBody);\n                  body = joinArr(body, htmlBody);\n                  data.body = joinArr(body, jsBody);\n                  bottom = joinArr(data.bottom, cssAppend);\n                  bottom = joinArr(bottom, htmlAppend);\n                  data.bottom = joinArr(bottom, jsAppend);\n                } else {\n                  if (isJs) {\n                    top = jsPrepend;\n                    body = jsBody;\n                    bottom = jsAppend;\n                  } else if (isCss) {\n                    top = cssPrepend;\n                    body = cssBody;\n                    bottom = cssAppend;\n                  }\n                  if (top) {\n                    top = util.toBuffer(top, charset);\n                    data.top = data.top ? Buffer.concat([data.top, CRLF, top]) : top;\n                  }\n                  if (body) {\n                    body = util.toBuffer(body, charset);\n                    data.body = data.body ? Buffer.concat([data.body, CRLF, body]) : body;\n                  }\n                  if (bottom) {\n                    bottom = util.toBuffer(bottom, charset);\n                    data.bottom = data.bottom ? Buffer.concat([data.bottom, CRLF, bottom]) : bottom;\n                  }\n                }\n\n                if (data.body || data.top || data.bottom) {\n                  !util.isEnable(req, 'keepAllCSP') &&\n                      !util.isEnable(req, 'keepCSP') &&\n                      util.disableCSP(_resHeaders);\n                  !req._customCache &&\n                      !util.isEnable(req, 'keepCache') &&\n                      util.disableResStore(_resHeaders);\n                }\n\n                if (!hasResBody) {\n                  delete data.speed;\n                  delete data.body;\n                  delete data.top;\n                  delete data.bottom;\n                } else {\n                  util.removeResBody(req, data);\n                }\n                if (util.isWhistleTransformData(data)) {\n                  data.noDoctype = util.isDisable(req, 'doctype');\n                  res.addZipTransform(new WhistleTransform(data));\n                }\n                if (hasResBody) {\n                  handleReplace(res, replacement);\n                }\n                  //一定放在最后，确保能过滤到动态注入的内容\n                if (speedTransform) {\n                  res.add(speedTransform);\n                }\n\n                var bodyFile = hasResBody\n                    ? getWriterFile(\n                        util.getWriteFilePath(resRules.resWrite),\n                        _res.statusCode\n                      )\n                    : null;\n                var rawFile = getWriterFile(\n                    util.getWriteFilePath(resRules.resWriteRaw),\n                    _res.statusCode\n                  );\n                util.getFileWriters(\n                    [bodyFile, rawFile],\n                    function (writer, rawWriter) {\n                      if (req._hasError) {\n                        return;\n                      }\n                      res.on('src', function (_res) {\n                        if (writer) {\n                          res.addZipTransform(\n                            new FileWriterTransform(writer, _res)\n                          );\n                        }\n\n                        if (rawWriter) {\n                          res.addZipTransform(\n                            new FileWriterTransform(\n                              rawWriter,\n                              _res,\n                              true,\n                              req\n                            )\n                          );\n                        }\n                      });\n                      var resHeaders = delProps.resHeaders;\n                      if (resHeaders) {\n                        Object.keys(resHeaders).forEach(function (prop) {\n                          delete _resHeaders[prop];\n                        });\n                      }\n                      if (_resHeaders[config.ALPN_PROTOCOL_HEADER] === 'h2') {\n                        req.useH2 = true;\n                      }\n                      util.delay(\n                        util.getMatcherValue(resRules.resDelay),\n                        function () {\n                          if (req._hasError) {\n                            return;\n                          }\n                          if (util.needAbortRes(req)) {\n                            req.__resHeaders = _res.headers;\n                            req.__statusCode = _res.statusCode;\n                            return res.destroy();\n                          }\n                          res.src(_res, null, firstChunk);\n                          firstChunk = null;\n                          var rawNames = res.rawHeaderNames || {};\n                          var encoding = util.getEnableEncoding(enable);\n                          if (encoding) {\n                            rawNames['content-encoding'] =\n                              rawNames['content-encoding'] ||\n                              'Content-Encoding';\n                            _resHeaders['content-encoding'] = encoding;\n                            delete _resHeaders['content-length'];\n                          } else if (\n                            req._pipePluginPorts.resReadPort ||\n                            req._pipePluginPorts.resWritePort\n                          ) {\n                            delete req.headers['content-length'];\n                          }\n                          util.disableResProps(req, _resHeaders);\n                          if (req._filters.showHost || enable.showHost) {\n                            _resHeaders['x-host-ip'] = req.hostIp || LOCALHOST;\n                          }\n                          util.setResponseFor(\n                            resRules,\n                            _resHeaders,\n                            req,\n                            req.hostIp,\n                            req._phost\n                          );\n                          pluginMgr.postStats(req, res);\n                          if (\n                            !hasResBody &&\n                            _resHeaders['content-length'] > 0 &&\n                            !util.isHead(req)\n                          ) {\n                            delete _resHeaders['content-length'];\n                          }\n                          if (!req.disable.trailerHeader) {\n                            util.addTrailerNames(\n                              _res,\n                              newTrailers,\n                              rawNames,\n                              delProps.trailers,\n                              req\n                            );\n                          }\n                          if (req.enableCustomParser) {\n                            if (\n                              _res.isCustomRes ||\n                              _resHeaders['x-whistle-disable-custom-frames']\n                            ) {\n                              delete _resHeaders[\n                                'x-whistle-disable-custom-frames'\n                              ];\n                              req.disableCustomParser();\n                            } else {\n                              useFrames = true;\n                              req.enableCustomParser(_res);\n                            }\n                          }\n                          var curHeaders = _resHeaders;\n                          if (req.fromComposer) {\n                            curHeaders = extend({}, _resHeaders);\n                            curHeaders['x-whistle-req-id'] = req.reqId;\n                            util.setFramesMode(curHeaders, useFrames);\n                          }\n                          util.addMatchedRules(req, _res);\n                          try {\n                            res.writeHead(\n                              _res.statusCode,\n                              formatHeaders(curHeaders, rawNames)\n                            );\n                            util.onResEnd(_res, function () {\n                              var trailers = _res.trailers;\n                              if (\n                                !res.chunkedEncoding ||\n                                req.disable.trailers ||\n                                req.disable.trailer ||\n                                (util.isEmptyObject(trailers) &&\n                                  util.isEmptyObject(newTrailers))\n                              ) {\n                                return;\n                              }\n                              var rawHeaderNames = _res.rawTrailers\n                                ? getRawHeaderNames(_res.rawTrailers)\n                                : {};\n                              if (newTrailers) {\n                                newTrailers = util.lowerCaseify(\n                                  newTrailers,\n                                  rawHeaderNames\n                                );\n                                if (trailers) {\n                                  extend(trailers, newTrailers);\n                                } else {\n                                  trailers = newTrailers;\n                                }\n                              }\n                              var delTrailers = delProps.trailers;\n                              if (delTrailers) {\n                                Object.keys(delTrailers).forEach(function (prop) {\n                                  delete trailers[prop];\n                                });\n                              }\n                              util.handleHeaderReplace(trailers, hr.trailer);\n                              res.setCurTrailers &&\n                                res.setCurTrailers(trailers, rawHeaderNames);\n                              try {\n                                util.removeIllegalTrailers(trailers);\n                                res.addTrailers(\n                                  formatHeaders(trailers, rawHeaderNames)\n                                );\n                              } catch (e) {}\n                            });\n                            if (\n                              res.flushHeaders &&\n                              (!req.disable.flushHeaders ||\n                                enable.flushHeaders)\n                            ) {\n                              res.flushHeaders();\n                            }\n                          } catch (e) {\n                            util.emitError(res, e);\n                          }\n                        }\n                      );\n                    },\n                    util.isEnable(req, 'forceReqWrite')\n                  );\n              },\n                !hasResBody,\n                charset,\n                isHtml,\n                req\n              );\n            },\n            req\n          );\n        });\n      });\n    });\n  };\n  var resHeaders = {};\n  var svRes = util.getStatusCodeFromRule(resRules, req);\n  if (svRes) {\n    req.hostIp = LOCALHOST;\n    resHeaders.Connection = 'close';\n    res.response(util.wrapResponse(svRes));\n    return;\n  }\n\n  notAllowCache(resRules) && util.disableReqCache(req.headers);\n  if (!req._hasError) {\n    next();\n  }\n};\n"
  },
  {
    "path": "lib/inspectors/rules.js",
    "content": "var rules = require('../rules');\nvar util = require('../util');\nvar pluginMgr = require('../plugins');\nvar fileMgr = require('../util/file-mgr');\nvar transproto = require('../util/transproto');\nvar getEncodeTransform = transproto.getEncodeTransform;\nvar getDecodeTransform = transproto.getDecodeTransform;\nvar getRawHeaderNames = require('hparser').getRawHeaderNames;\n\nvar MAX_PAYLOAD_SIZE = 1024 * 256;\n\nfunction resolveRules(req, rules) {\n  if (!rules) {\n    return;\n  }\n  req.curUrl = req.fullUrl = util.getFullUrl(req);\n  if (rules.initRules) {\n    rules.initRules(req);\n  } else {\n    var _pluginRules = rules.resolveReqRules(req);\n    // 插件不支持rulesFile协议\n    delete req.rules.rulesFile;\n    util.mergeRules(req, _pluginRules);\n  }\n}\n\nfunction setupRules(req, next) {\n  var gRules = req.rules;\n  resolveRules(req, rules);\n  if (gRules) {\n    req.rules.P = gRules.P;\n    req.rules.G = gRules.G;\n  }\n  rules.resolveRulesFile(req, function () {\n    pluginMgr.resolveWhistlePlugins(req);\n    pluginMgr.getRules(req, function (pluginRules) {\n      req.pluginRules = pluginRules;\n      resolveRules(req, pluginRules);\n      util.filterWeakRule(req);\n      var ruleUrl = util.rule.getUrl(req.rules.rule);\n      if (ruleUrl !== req.fullUrl && util.isUrl(ruleUrl)) {\n        ruleUrl = util.encodeNonLatin1Char(ruleUrl);\n      }\n      req.options = util.parseUrl(ruleUrl || req.fullUrl);\n      if (\n        req.isH2 &&\n        ruleUrl &&\n        req.options.protocol === 'http:' &&\n        ruleUrl !== req.fullUrl\n      ) {\n        req.isH2 = false;\n      }\n      var rawNames = (req.rawHeaderNames = Array.isArray(req.rawHeaders)\n        ? getRawHeaderNames(req.rawHeaders)\n        : {});\n      rawNames.connection = rawNames.connection || 'Connection';\n      rawNames['proxy-authorization'] =\n        rawNames['proxy-authorization'] || 'Proxy-Authorization';\n      next();\n    });\n  });\n}\n\nfunction getDecoder(obj) {\n  return function (socket, callback) {\n    var encoding = obj._originEncoding;\n    var handleError = function (err) {\n      obj.emit('error', err);\n    };\n    var decoder;\n    if (\n      obj._needGunzip ||\n      socket ||\n      encoding !== obj.headers['content-encoding']\n    ) {\n      var stream = encoding && (obj._srcResponse || obj);\n      util.readOneChunk(stream, function (chunk) {\n        obj._needGunzip = false;\n        if (chunk) {\n          if (util.isZip(encoding, chunk)) {\n            decoder = util.getUnzipStream(encoding);\n            obj._needGunzip = true;\n          } else {\n            decoder = util.createTransform();\n            obj._originEncoding = null;\n          }\n          decoder.write(chunk);\n        }\n        handleDecode(stream);\n      });\n    } else {\n      handleDecode();\n    }\n    function handleDecode(stream) {\n      decoder && decoder.on('error', handleError);\n      if (socket) {\n        delete obj.headers['content-length'];\n        var enTrans = getEncodeTransform();\n        var deTrans = getDecodeTransform();\n        enTrans.pipe(socket).pipe(deTrans);\n        enTrans.on('error', handleError);\n        deTrans.on('error', handleError);\n        if (decoder) {\n          decoder.pipe(enTrans);\n        } else {\n          decoder = enTrans;\n        }\n        socket = deTrans;\n      }\n      callback(decoder, socket, stream);\n    }\n  };\n}\n\nfunction getEncoder(obj, req) {\n  return function (socket, callback) {\n    var encoding = util.getEnableEncoding(req && req.enable);\n    if (!encoding || (!obj._needGunzip && obj._originEncoding)) {\n      encoding = obj._needGunzip && obj.headers;\n    }\n    var encoder = encoding && util.getZipStream(encoding);\n    var handleError = function (err) {\n      obj.emit('error', err);\n    };\n    encoder && encoder.on('error', handleError);\n    if (socket) {\n      delete obj.headers['content-length'];\n      var enTrans = getEncodeTransform();\n      var deTrans = getDecodeTransform();\n      enTrans.on('error', handleError);\n      deTrans.on('error', handleError);\n      enTrans.pipe(socket).pipe(deTrans);\n      socket = enTrans;\n      if (encoder) {\n        deTrans.pipe(encoder);\n      } else {\n        encoder = deTrans;\n      }\n      socket.pipe = function (stream) {\n        return encoder.pipe(stream);\n      };\n      obj.emit('bodyStreamReady', socket);\n    }\n    callback(socket || encoder);\n  };\n}\n\nmodule.exports = function (req, res, next) {\n  if (req.isLogRequests !== false) {\n    ++util.proc.httpRequests;\n    ++util.proc.totalHttpRequests;\n    req.isLogRequests = true;\n  }\n  req.reqId = util.getReqId();\n  req.curUrl = req.fullUrl = util.getFullUrl(req);\n  req._originEncoding = req.headers['content-encoding'];\n  req.onDecode = function (callback) {\n    var decode = getDecoder(req);\n    pluginMgr.getReqReadPipe(req, function (socket) {\n      decode(socket, callback);\n    });\n  };\n  req.onEncode = function (callback) {\n    var encode = getEncoder(req);\n    pluginMgr.getReqWritePipe(req, function (socket) {\n      encode(socket, callback);\n    });\n  };\n  res.onDecode = function (callback) {\n    var decode = getDecoder(res, req);\n    pluginMgr.getResReadPipe(req, res, function (socket) {\n      decode(socket, callback);\n    });\n  };\n  res.onEncode = function (callback) {\n    var encode = getEncoder(res, req);\n    pluginMgr.getResWritePipe(req, res, function (socket) {\n      encode(socket, callback);\n    });\n  };\n  rules.initHeaderRules(req, true);\n  var gList = rules.getGRuleList(req);\n  var hList = req.headerRulesMgr && req.headerRulesMgr.getGRuleList(req);\n  if (gList || hList) {\n    rules.resolvePluginVars(req, (gList || []).concat(hList || []));\n  }\n  req._resolvedG = true;\n  pluginMgr.resolvePipePlugin(req, function () {\n    var reqReadPort = req._pipePluginPorts.reqReadPort;\n    if (reqReadPort || req._pipePluginPorts.reqWritePort) {\n      delete req.headers['content-length'];\n    }\n    var hasBodyFilter = rules.resolveBodyFilter(req);\n    req._bodyFilters = null;\n    if (hasBodyFilter || reqReadPort) {\n      req._needGunzip = true;\n      var payloadSize = MAX_PAYLOAD_SIZE;\n      if (!hasBodyFilter) {\n        payloadSize = rules.hasReqScript(req) ? 0 : 1;\n      }\n      req.getPayload(function (err, payload) {\n        req._reqBody = fileMgr.decode(payload);\n        setupRules(req, next);\n      }, payloadSize);\n    } else {\n      setupRules(req, next);\n    }\n  });\n};\n"
  },
  {
    "path": "lib/inspectors/weinre.js",
    "content": "var Transform = require('pipestream').Transform;\nvar path = require('path');\nvar fs = require('fs');\nvar util = require('../util');\nvar config = require('../config');\n\nvar weinreScriptFile = path.join(config.ASSESTS_PATH, 'js/weinre.js');\nvar weinreScript = fs.readFileSync(weinreScriptFile, { encoding: 'utf8' });\nvar weinreHtmlScript = '\\r\\n<script>' + weinreScript + '</script>\\r\\n';\n\nfunction getScript(host, name, isHtml, req) {\n  host = util.getInternalHost(req, host);\n  var baseUrl = (req.isHttps ? 'https://' : 'http://') + host;\n  var weinrePath = config.WEBUI_PATH + 'weinre.' + config.port;\n  var result = isHtml ? weinreHtmlScript : weinreScript;\n  var weinreUrl =\n    weinrePath + '/target/target-script-min.js#' + (name || 'anonymous');\n  return result.replace('$BASE_URL', baseUrl)\n    .replace('$WEINRE_PATH', weinrePath)\n    .replace('$WEINRE_URL', weinreUrl);\n}\n\nmodule.exports = function (req, res) {\n  if (req.rules.weinre) {\n    util.disableReqCache(req.headers);\n    var host = req.headers.host;\n    res.on('src', function (_res) {\n      var isHtml = util.supportHtmlTransform(_res, req);\n      if (!isHtml && util.getContentType(_res.headers) !== 'JS') {\n        return;\n      }\n      !util.isEnable(req, 'keepAllCSP') && util.disableCSP(_res.headers);\n      !req._customCache && util.disableResStore(_res.headers);\n      var name = util.getPath(util.rule.getMatcher(req.rules.weinre));\n      var transform = new Transform();\n      transform._transform = function (chunk, _, callback) {\n        if (!chunk) {\n          chunk = util.toBuffer(getScript(host, name, isHtml, req));\n        }\n        callback(null, chunk);\n      };\n\n      res.addZipTransform(transform, false, true);\n    });\n  }\n};\n"
  },
  {
    "path": "lib/plugins/compat.js",
    "content": "\nvar extend = require('extend');\n\nvar propsMap = {\n  fullUrl: 'FULL_URL_HEADER',\n  _extraUrl: 'EXTRA_URL_HEADER',\n  reqId: 'REQ_ID_HEADER',\n  _existsCustomCert: 'CUSTOM_CERT_HEADER',\n  _enableCapture: 'ENABLE_CAPTURE_HEADER',\n  _ruleProtocol: 'RULE_PROTO_HEADER',\n  _ruleValue: 'RULE_VALUE_HEADER',\n  sniRuleValue: 'SNI_VALUE_HEADER',\n  _ruleUrl: 'RULE_URL_HEADER',\n  _finalUrl: 'REAL_URL_HEADER',\n  _relativeUrl: 'RELATIVE_URL_HEADER',\n  _pipeValue: 'PIPE_VALUE_HEADER',\n  customParser: 'CUSTOM_PARSER_HEADER',\n  _statusCode: 'STATUS_CODE_HEADER',\n  host: 'HOST_VALUE_HEADER',\n  proxy: 'PROXY_VALUE_HEADER',\n  pac: 'PAC_VALUE_HEADER',\n  method: 'METHOD_HEADER',\n  clientIp: 'CLIENT_IP_HEADER',\n  clientPort: 'CLIENT_PORT_HEADER',\n  _isUIRequest: 'UI_REQUEST_HEADER',\n  hostIp: 'HOST_IP_HEADER',\n  _pluginVarsValue: 'GLOBAL_VALUE_HEADER',\n  serverName: 'SERVER_NAME_HEADER',\n  commonName: 'COMMON_NAME_HEADER',\n  isPluginReq: 'PLUGIN_REQUEST_HEADER',\n  hasCertCache: 'CERT_CACHE_INFO',\n  fromComposer: 'REQ_FROM_HEADER'\n};\n\nvar specValues = {\n  fromComposer: 'W2COMPOSER'\n};\n\nvar constsMap = {\n  CUSTOM_CERT_HEADER: 'x-whistle-exists-custom-cert',\n  ENABLE_CAPTURE_HEADER: 'x-whistle-enable-capture',\n  RULE_VALUE_HEADER: 'x-whistle-rule-value',\n  RULE_PROTO_HEADER: 'x-whistle-rule-proto',\n  SNI_VALUE_HEADER: 'x-whistle-sni-value',\n  RULE_URL_HEADER: 'x-whistle-rule-url',\n  FULL_URL_HEADER: 'x-whistle-full-url',\n  REAL_URL_HEADER: 'x-whistle-real-url',\n  EXTRA_URL_HEADER: 'x-whistle-extra-url',\n  RELATIVE_URL_HEADER: 'x-whistle-relative-url',\n  REQ_ID_HEADER: 'x-whistle-req-id',\n  PIPE_VALUE_HEADER: 'x-whistle-pipe-value',\n  CUSTOM_PARSER_HEADER: 'x-whistle-frame-parser',\n  STATUS_CODE_HEADER: 'x-whistle-status-code',\n  PLUGIN_REQUEST_HEADER: 'x-whistle-plugin-request',\n  LOCAL_HOST_HEADER: 'x-whistle-local-host',\n  HOST_VALUE_HEADER: 'x-whistle-local-host',\n  PROXY_VALUE_HEADER: 'x-whistle-proxy-value',\n  PAC_VALUE_HEADER: 'x-whistle-pac-value',\n  METHOD_HEADER: 'x-whistle-method',\n  CLIENT_IP_HEADER: 'x-forwarded-for',\n  UI_REQUEST_HEADER:  'x-whistle-auth-ui-request',\n  CERT_CACHE_INFO: 'x-whistle-cert-cache-info',\n  HOST_IP_HEADER: 'x-whistle-host-ip',\n  REQ_FROM_HEADER: 'x-whistle-request-from',\n  CLIENT_PORT_HEADER: 'x-whistle-client-port',\n  GLOBAL_VALUE_HEADER: 'x-whistle-global-value',\n  SERVER_NAME_HEADER: 'x-whistle-server-name',\n  COMMON_NAME_HEADER: 'x-whistle-common-name',\n  CLIENT_PORT_HEAD: 'x-whistle-client-port',\n  GLOBAL_VALUE_HEAD: 'x-whistle-global-value',\n  SERVER_NAME_HEAD: 'x-whistle-server-name',\n  COMMON_NAME_HEAD: 'x-whistle-common-name'\n};\n\nfunction compat(headers, key, value) {\n  Object.defineProperty(headers, key, {\n    enumerable: false,\n    configurable: true,\n    get: function() {\n      return value;\n    },\n    set: function(val) {\n      Object.defineProperty(headers, key, {\n        enumerable: true,\n        writable: true,\n        configurable: true,\n        value: val\n      });\n    }\n  });\n}\n\n\nexports.compatHeaders = function(req, sessionInfo) {\n  var headers = req.headers;\n  var rawData = sessionInfo._;\n  Object.keys(propsMap).forEach(function(key) {\n    var name = constsMap[propsMap[key]];\n    var value = rawData[key] || sessionInfo[key];\n    value = (value && specValues[key]) || value;\n    compat(headers, name, value);\n  });\n  return sessionInfo;\n};\n\nexports.compatKeys = function(obj) {\n  extend(obj, constsMap);\n};\n"
  },
  {
    "path": "lib/plugins/get-plugins-sync.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar util = require('../util');\nvar pluginUtil = require('./util');\nvar mp = require('./module-paths');\nvar config = require('../config');\nvar common = require('../util/common');\n\nvar CUSTOM_PLUGIN_PATH = config.CUSTOM_PLUGIN_PATH;\nvar customPluginPaths = config.customPluginPaths || [];\nvar notUninstallPluginPaths = config.notUninstallPluginPaths || [];\nvar projectPluginPaths = config.projectPluginPaths || [];\nvar accountPluginsPath = config.accountPluginsPath || [];\nvar paths = mp.getPaths();\n\nfunction getMTimeSync(filePath) {\n  var stats = common.getStatSync(filePath);\n  return stats &&stats.isFile() ? stats.mtime.getTime() : null;\n}\n\nfunction readPluginModulesSync(dir, plugins) {\n  var isAccount = accountPluginsPath.indexOf(dir) !== -1;\n  var account = isAccount ? config.account : undefined;\n  var isSys = isAccount || CUSTOM_PLUGIN_PATH === dir || customPluginPaths.indexOf(dir) !== -1;\n  var isProj = projectPluginPaths.indexOf(dir) !== -1;\n  var notUn = notUninstallPluginPaths.indexOf(dir) !== -1;\n\n  try {\n    var list = fs.readdirSync(dir).filter(function (name) {\n      if (pluginUtil.isWhistleModule(name)) {\n        return true;\n      }\n\n      if (pluginUtil.isOrgModule(name)) {\n        try {\n          var _dir = path.join(dir, name);\n          var org = name;\n          fs.readdirSync(_dir).forEach(function (name) {\n            if (pluginUtil.isWhistleModule(name)) {\n              var root = isSys ? pluginUtil.getSysPathSync(_dir, name, org) : path.join(_dir, name);\n              var mtime = getMTimeSync(path.join(root, 'package.json'));\n              if (mtime != null) {\n                var old = plugins[name];\n                if (!old || (mtime > old.mtime && root === old.root)) {\n                  plugins[name] = {\n                    mtime: mtime,\n                    root: root,\n                    account: account,\n                    dir: config.whistleName,\n                    isSys: isSys,\n                    notUn: notUn,\n                    isProj: isProj\n                  };\n                }\n              }\n            }\n          });\n        } catch (e) {}\n      }\n      return false;\n    });\n\n    list.forEach(function (name) {\n      var root = isSys ? pluginUtil.getSysPathSync(dir, name) : path.join(dir, name);\n      var mtime = getMTimeSync(path.join(root, 'package.json'));\n      if (mtime != null) {\n        var old = plugins[name];\n        if (!old || (mtime > old.mtime && root === old.root)) {\n          plugins[name] = {\n            root: root,\n            mtime: mtime,\n            account: account,\n            dir: config.whistleName,\n            isSys: isSys,\n            notUn: notUn,\n            isProj: isProj\n          };\n        }\n      }\n    });\n  } catch (e) {}\n\n  return plugins;\n}\n\nmodule.exports = function () {\n  var plugins = pluginUtil.readDevPluginsSync();\n  paths.forEach(function (dir) {\n    readPluginModulesSync(dir, plugins);\n  });\n\n  var _plugins = {};\n  Object.keys(plugins).forEach(function (name) {\n    var simpleName = pluginUtil.getPluginName(name);\n    if (pluginUtil.excludePlugin(simpleName)) {\n      return;\n    }\n    var dir = plugins[name];\n    var account = dir.account;\n    var isSys = dir.isSys;\n    var isProj = dir.isProj;\n    var notUn = dir.notUn;\n    var isDev = dir.isDev;\n    var dirName = dir.dir;\n    var mtime = dir.mtime;\n    dir = dir.root;\n    var pkgPath = path.join(dir, 'package.json');\n    var pkg = common.readJsonSync(pkgPath);\n    if (!pkg) {\n      return;\n    }\n    if (!pkg.version || !pluginUtil.isPluginName(pkg.name)) {\n      try {\n        fs.renameSync(pkgPath, pkgPath + '.' + Date.now());\n      } catch (e) {}\n      return;\n    }\n    try {\n      var conf = pkg.whistleConfig || '';\n      var tabs = util.getInspectorTabs(conf);\n      var hintList = util.getHintList(conf);\n      var plugin = {\n        account: account,\n        dir: dirName,\n        isSys: isSys,\n        isDev: isDev,\n        isProj: isProj,\n        noOpt: !!(conf.noOption || conf.notOption || conf.disableOption|| conf.disabledOption),\n        notUn: notUn,\n        moduleName: pkg.name,\n        enableAuthUI: !!conf.enableAuthUI,\n        inheritAuth: !!conf.inheritAuth,\n        updateUrl: util.getUpdateUrl(conf),\n        tunnelKey: util.getTunnelKey(conf),\n        staticDir: util.getStaticDir(conf),\n        pluginVars: util.getPluginVarsConf(conf),\n        networkColumn: util.getNetworkColumn(conf),\n        webWorker: pluginUtil.readWorkerSync(dir, conf, simpleName),\n        networkMenus: util.getPluginMenu(\n            conf.networkMenus || conf.networkMenu,\n            simpleName\n          ),\n        rulesMenus: util.getPluginMenu(\n            conf.rulesMenus || conf.rulesMenu,\n            simpleName\n          ),\n        valuesMenus: util.getPluginMenu(\n            conf.valuesMenus || conf.valuesMenu,\n            simpleName\n          ),\n        pluginsMenus:  util.getPluginMenu(\n            conf.pluginsMenus || conf.pluginsMenu,\n            simpleName\n          ),\n        reqTab: util.getCustomTab(tabs.req, simpleName),\n        resTab: util.getCustomTab(tabs.res, simpleName),\n        tab: util.getCustomTab(tabs, simpleName),\n        toolTab: util.getCustomTab(conf.toolsTab || conf.toolTab, simpleName),\n        comTab: util.getCustomTab(conf.composerTab, simpleName),\n        pluginHomepage: pluginUtil.getPluginHomepage(pkg) || pluginUtil.getPluginHomepage(conf),\n        openInPlugins: conf.openInPlugins ? 1 : undefined,\n        openInModal: util.getPluginModal(conf),\n        openExternal: conf.openExternal ? 1 : undefined,\n        priority: parseInt(conf.priority, 10) || parseInt(pkg.pluginPriority, 10) || 0,\n        rulesUrl: util.getCgiUrl(conf.rulesUrl),\n        valuesUrl: util.getCgiUrl(conf.valuesUrl),\n        installUrl: util.getCgiUrl(conf.installUrl),\n        favicon: util.getCgiUrl(conf.favicon),\n        installRegistry: util.getInstallRegistry(conf.installRegistry),\n        hintUrl: hintList ? undefined : util.getCgiUrl(conf.hintUrl),\n        hideShortProtocol: !!conf.hideShortProtocol,\n        hideLongProtocol: !!conf.hideLongProtocol,\n        hintList: hintList,\n        registry: util.getRegistry(pkg),\n        path: dir,\n        mtime: mtime,\n        version: pkg.version,\n        description: pkg.description,\n        homepage: pluginUtil.getHomePageFromPackage(pkg) || pluginUtil.getHomePageFromPackage(conf),\n        rules: util.renderPluginRules(\n            util.trim(util.readFileSync(path.join(dir, 'rules.txt'))),\n            pkg,\n            simpleName\n          ),\n        _rules: util.renderPluginRules(\n            util.trim(util.readFileSync(path.join(dir, '_rules.txt'))) ||\n              util.trim(util.readFileSync(path.join(dir, 'reqRules.txt'))),\n            pkg,\n            simpleName\n          ),\n        resRules: util.renderPluginRules(\n            util.trim(util.readFileSync(path.join(dir, 'resRules.txt'))),\n            pkg,\n            simpleName\n          )\n      };\n\n      plugin[util.PLUGIN_VALUES] = pluginUtil.parseValues(\n          util.renderPluginRules(\n            util.readFileSync(path.join(dir, '_values.txt')),\n            pkg,\n            simpleName\n          ), simpleName);\n      plugin[util.PLUGIN_MENU_CONFIG] = util.getPluginMenuConfig(conf);\n      plugin[util.PLUGIN_INSPECTOR_CONFIG] =\n          util.getPluginInspectorConfig(conf);\n      _plugins[simpleName + ':'] = plugin;\n\n    } catch (e) {}\n  });\n\n  return _plugins;\n};\n"
  },
  {
    "path": "lib/plugins/get-plugins.js",
    "content": "var path = require('path');\nvar pluginUtil = require('./util');\nvar mp = require('./module-paths');\nvar config = require('../config');\nvar common = require('../util/common');\n\nvar CUSTOM_PLUGIN_PATH = config.CUSTOM_PLUGIN_PATH;\nvar customPluginPaths = config.customPluginPaths || [];\nvar notUninstallPluginPaths = config.notUninstallPluginPaths || [];\nvar projectPluginPaths = config.projectPluginPaths || [];\nvar accountPluginsPath = config.accountPluginsPath || [];\n\nfunction readPluginRootList(dir, callback) {\n  var roots = [];\n  pluginUtil.readDir(dir, function (_, list) {\n    if (!list || !list.length) {\n      return callback(roots);\n    }\n    var handleCallback = function () {\n      var orgRoots = [];\n      roots = roots.filter(function (obj) {\n        if (!Array.isArray(obj)) {\n          return obj;\n        }\n        orgRoots.push.apply(orgRoots, obj);\n      });\n      callback(orgRoots.concat(roots));\n    };\n    var count = 0;\n    list.forEach(function (name, i) {\n      if (pluginUtil.isWhistleModule(name)) {\n        roots[i] = {\n          name: name,\n          dir: dir\n        };\n      } else if (pluginUtil.isOrgModule(name)) {\n        ++count;\n        pluginUtil.readDir(path.join(dir, name), function (_, list2) {\n          if (list2 && list2.length) {\n            var orgList;\n            list2.forEach(function (pluginName) {\n              if (pluginUtil.isWhistleModule(pluginName)) {\n                orgList = orgList || [];\n                orgList.push({\n                  org: name,\n                  name: pluginName,\n                  dir: dir\n                });\n              }\n            });\n            roots[i] = orgList;\n          }\n          if (--count === 0) {\n            count = -1;\n            handleCallback(roots);\n          }\n        });\n      }\n    });\n    if (count === 0) {\n      count = -1;\n      handleCallback(roots);\n    }\n  });\n}\n\nfunction readPluginModules(dir, callback, plugins, isSys) {\n  if (typeof dir !== 'string') {\n    return callback(plugins);\n  }\n  readPluginRootList(dir, function (list) {\n    var len = list.length;\n    if (!len) {\n      return callback(plugins);\n    }\n    list.forEach(function (obj) {\n      var dir = obj.dir;\n      var dirName = obj.org ? obj.org + '/' + obj.name : obj.name;\n      pluginUtil.getSysPath(dir, dirName, isSys, function(root) {\n        common.getStat(path.join(root, 'package.json'), function (_, stats) {\n          if (stats && stats.isFile()) {\n            obj.root = root;\n            obj.mtime = stats.mtime.getTime();\n          }\n          if (--len === 0) {\n            list.forEach(function (obj) {\n              var name = pluginUtil.getPluginName(obj.name);\n              if (obj.root) {\n                var old = plugins[name];\n                if (!old || (obj.mtime > old.mtime && obj.root === old.root)) {\n                  plugins[name] = obj;\n                }\n              }\n            });\n            callback(plugins);\n          }\n        });\n      });\n    });\n  });\n}\n\nmodule.exports = function (callback) {\n  pluginUtil.readDevPlugins(function(plugins) {\n    var result = {};\n    var paths = mp.getPaths();\n    var count = paths.length;\n    if (!count && !Object.keys(plugins).length) {\n      return callback(result);\n    }\n\n    var loadPlugins = function (dir, cb) {\n      var isAccount = accountPluginsPath.indexOf(dir) !== -1;\n      var account = isAccount ? config.account : undefined;\n      var isSys = isAccount || CUSTOM_PLUGIN_PATH === dir || customPluginPaths.indexOf(dir) !== -1;\n      var isProj = projectPluginPaths.indexOf(dir) !== -1;\n      var notUn = notUninstallPluginPaths.indexOf(dir) !== -1;\n\n      readPluginModules(dir, function () {\n        Object.keys(plugins).forEach(function (name) {\n          if (pluginUtil.excludePlugin(name)) {\n            return;\n          }\n          var old = result[name + ':'];\n          var obj = plugins[name];\n          if (!old || (obj.mtime > old.mtime && obj.root === old.path)) {\n            result[name + ':'] = {\n              account: account,\n              dir: config.whistleName,\n              isSys: isSys,\n              isProj: isProj || obj.isProj,\n              notUn: notUn,\n              isDev: obj.isDev,\n              path: obj.root,\n              mtime: obj.mtime\n            };\n          }\n        });\n        cb();\n      }, plugins, isSys);\n    };\n    var index = 0;\n    var callbackHandler = function () {\n      var dir = paths[++index];\n      if (dir) {\n        return loadPlugins(dir, callbackHandler);\n      }\n      callback(result);\n    };\n    loadPlugins(paths[index], callbackHandler);\n  });\n};\n"
  },
  {
    "path": "lib/plugins/index.js",
    "content": "var path = require('path');\nvar p = require('pfork');\nvar http = require('http');\nvar LRU = require('lru-cache');\nvar extend = require('extend');\nvar EventEmitter = require('events').EventEmitter;\nvar pluginMgr = new EventEmitter();\nvar colors = require('colors/safe');\nvar util = require('../util');\nvar logger = require('../util/logger');\nvar pluginUtil = require('./util');\nvar config = require('../config');\nvar getPluginsSync = require('./get-plugins-sync');\nvar getPlugin = require('./get-plugins');\nvar rulesMgr = require('../rules');\nvar properties = require('../rules/util').properties;\nvar httpMgr = require('../util/http-mgr');\nvar protocols = require('../rules/protocols');\nvar common = require('../util/common');\nvar mp = require('./module-paths');\nvar createSharedStorage = require('../plugins/shared-storage');\n\nvar Rules = rulesMgr.Rules;\nvar getPluginName = pluginUtil.getPluginName;\nvar encodeURIComponent = util.encodeURIComponent;\nvar RULES_TPL_RE = /^[^\\n\\r\\S]*(``)[^\\n\\r\\S]*((?:_?var(\\.[^\\s=]+)?|whistle|rule)\\.tpl)[^\\n\\r\\S]*[\\r\\n]([\\s\\S]+?)[\\r\\n][^\\n\\r\\S]*\\1\\s*$/gm;\nvar REMOTE_RULES_RE =\n  /^\\s*@(`?)([\\w.-]+(?:[\\\\/][^\\s#]*)?|(?:https?:\\/\\/|[a-z]:[\\\\/]|~?\\/)[^\\s#]+|\\$(?:whistle\\.)?[a-z\\d_-]+[/:][^\\s#]+)\\s*?\\1(?:#.*)?$/gim;\nvar PLUGIN_MAIN = path.join(__dirname, './load-plugin');\nvar PIPE_PLUGIN_RE =\n  /^pipe:\\/\\/(?:whistle\\.|plugin\\.)?([a-z\\d_\\-]+)(?:\\(([\\s\\S]*)\\))?$/;\nvar JSON_RE = /^\\{[\\s\\S]+\\}$/;\nvar ETAG_HEADER = 'x-whistle-etag';\nvar MAX_AGE_HEADER = 'x-whistle-max-age';\nvar STATUS_ERR = new Error('Non 200');\nvar INTERVAL = 6000;\nvar CHECK_INTERVAL = 1000 * 60 * 60 * 2;\nvar MAX_CERT_SIZE = 72 * 1024;\nvar portsField = Symbol('ports');\nvar notLoadPlugins = config.networkMode || config.rulesOnlyMode;\nvar allPlugins = notLoadPlugins ? {} : getPluginsSync();\nvar authPlugins = [];\nvar tunnelKeys = [];\nvar LOCALHOST = '127.0.0.1';\nvar MAX_RULES_LENGTH = 1024 * 256;\nvar rulesCache = new LRU({ max: 36 });\nvar PLUGIN_HOOKS = config.PLUGIN_HOOKS;\nvar PLUGIN_HOOK_NAME_HEADER = config.PLUGIN_HOOK_NAME_HEADER;\nvar conf = {};\nvar EXCLUDE_CONF_KEYS = {\n  uid: 1,\n  WEBUI_HEAD: 1,\n  COMPOSER_CLIENT_ID_HEADER: 1,\n  DISABLE_RULES_HEADER: 1,\n  WHISTLE_POLICY_HEADER: 1,\n  CLIENT_IP_HEADER: 1,\n  HTTPS_FIELD: 1,\n  CLIENT_PORT_HEADER: 1,\n  CLIENT_ID_HEADER: 1,\n  FROM_COM_HEADER: 1,\n  ALPN_PROTOCOL_HEADER: 1\n};\nvar EXCLUDE_NAMES = {\n  password: 1,\n  shadowRules: 1,\n  rules: 1,\n  values: 1\n};\nvar debugMode = config.debugMode;\nvar proxy;\nvar REGISTRY_LIST = path.join(common.getDefaultWhistlePath(), '.registry.list');\nvar MAX_REG_COUNT = 100;\nvar storage = createSharedStorage(REGISTRY_LIST);\nvar registryList = filterRegList(common.readJsonSync(REGISTRY_LIST));\n\nfunction filterRegList(l) {\n  if (!Array.isArray(l)) {\n    l = [];\n  }\n  return l.filter(function(url) {\n    return common.getRegistry(url);\n  }).slice(0, MAX_REG_COUNT);\n}\n\nfunction readRegList() {\n  common.readJson(REGISTRY_LIST, function(e, list) {\n    if (!e) {\n      registryList = filterRegList(list);\n    }\n  });\n}\n\nif (notLoadPlugins) {\n  getPlugin = function (cb) {\n    cb({});\n  };\n}\n\npluginMgr.addRegistry = function(registry) {\n  registry = common.getRegistry(registry);\n  if (registry && registryList.indexOf(registry) === -1) {\n    registryList.push(registry);\n    storage.setAll(registryList);\n  }\n};\n\n/*eslint no-console: \"off\"*/\nObject.keys(config).forEach(function (name) {\n  if (EXCLUDE_NAMES[name]) {\n    return;\n  }\n  var value = config[name];\n  if (name === 'passwordHash') {\n    conf.password = value;\n  }\n  if (name === 'globalData') {\n    conf.globalData = value;\n  } else if (!EXCLUDE_CONF_KEYS[name]) {\n    var type = typeof value;\n    if (type == 'string' || type == 'number' || type === 'boolean') {\n      conf[name] = value;\n    }\n  }\n});\nconf.client = config.client;\nconf.PLUGIN_HOOKS = config.PLUGIN_HOOKS;\nconf.uiHostList = config.uiHostList;\nvar pluginHostMap = config.pluginHostMap;\nvar pluginHosts = (conf.pluginHosts = {});\nif (pluginHostMap) {\n  Object.keys(pluginHostMap).forEach(function (host) {\n    var name = pluginHostMap[host];\n    var list = (pluginHosts[name] = pluginHosts[name] || []);\n    list.push(host);\n  });\n}\n\npluginMgr.REGISTRY_LIST = REGISTRY_LIST;\n\npluginMgr.getPluginsPath = mp.getPaths;\n\npluginMgr.getRegistryList = function() {\n  return registryList;\n};\n\npluginMgr.disableAllPlugins = function(disabled) {\n  properties.set('disabledAllPlugins', disabled);\n  pluginMgr.emit('updateRules', 'disableAllPlugins');\n};\n\npluginMgr.getTunnelKeys = function () {\n  return tunnelKeys;\n};\n\nvar MAX_REMOTE_RULES_COUNT = 3;\n\npluginMgr.on('updateRules', function (byParse) {\n  rulesMgr.clearAppend();\n  authPlugins = [];\n  tunnelKeys = [];\n  var ruleTpls;\n  Object.keys(allPlugins)\n    .sort(function (a, b) {\n      var p1 = allPlugins[a];\n      var p2 = allPlugins[b];\n      return (\n        util.compare(p1.priority, p2.priority) ||\n        util.compare(p2.mtime, p1.mtime) ||\n        (a > b ? 1 : 0)\n      );\n    })\n    .forEach(function (name) {\n      if (pluginIsDisabled(name.slice(0, -1))) {\n        return;\n      }\n      var plugin = allPlugins[name];\n      var rules = plugin.rules;\n      if (plugin.enableAuthUI) {\n        authPlugins.push(plugin);\n      }\n      if (plugin.tunnelKey) {\n        plugin.tunnelKey.forEach(function (key) {\n          if (tunnelKeys.indexOf(key) === -1) {\n            tunnelKeys.push(key);\n          }\n        });\n      }\n      if (rules) {\n        var root = plugin.path;\n        var index = 0;\n        rules = rules.replace(REMOTE_RULES_RE, function (_, apo, rulesUrl) {\n          if (index >= MAX_REMOTE_RULES_COUNT) {\n            return '';\n          }\n          ++index;\n          return util.getRemoteRules(apo, rulesUrl, root);\n        });\n        rules = rules.replace(RULES_TPL_RE, function (_, __, key, subVar, value) {\n          ruleTpls = ruleTpls || {};\n          var simpleName = name.slice(0, -1);\n          var isPrivate = key[0] === '_';\n          if (isPrivate || key[0] === 'v') {\n            key = (isPrivate ? '_' : '') + '%' + simpleName + (subVar || '');\n          } else if (key === 'whistle.tpl') {\n            key = 'whistle.' + simpleName;\n          } else {\n            key = simpleName;\n          }\n          if (!ruleTpls[key]) {\n            ruleTpls[key] = value;\n          }\n          return '';\n        });\n        rulesMgr.append(rules, plugin.path, true);\n      }\n    });\n  pluginMgr.ruleTpls = ruleTpls;\n  if (byParse !== true) {\n    httpMgr.triggerChange();\n    proxy.emit('pluginsChange');\n  }\n});\n\npluginMgr.updateRules = function() {\n  pluginMgr.emit('updateRules');\n};\n\npluginMgr.loadCert = function (req, plugin, callback) {\n  loadPlugin(plugin, function (err, ports) {\n    if (err || !ports || !ports.sniPort) {\n      return callback();\n    }\n    if (!req.useSNI || req.isHttpsServer) {\n      req._sniType = req.isHttpsServer ? '1' : '0';\n    }\n    var options = getOptions(req);\n    options.maxLength = MAX_CERT_SIZE;\n    options.headers[PLUGIN_HOOK_NAME_HEADER] = PLUGIN_HOOKS.SNI;\n    options.port = ports.sniPort;\n    addPluginVars(req, options.headers, {\n      plugin: plugin,\n      value: ''\n    });\n    requestPlugin(options, function (err, body, res) {\n      if (err || res.statusCode !== 200) {\n        return callback(err || STATUS_ERR);\n      }\n      if (!body) {\n        return callback();\n      }\n      if (body === 'false') {\n        return callback(false);\n      }\n      if (body === 'true') {\n        return callback(true);\n      }\n      try {\n        body = JSON.parse(body);\n        if (\n          !body ||\n          !body.name ||\n          !util.isString(body.key) ||\n          !util.isString(body.cert)\n        ) {\n          return callback();\n        }\n        callback({\n          key: body.key,\n          cert: body.cert,\n          name: body.name,\n          mtime: body.mtime > 0 ? body.mtime : undefined\n        });\n      } catch (e) {\n        callback(e);\n      }\n    });\n  });\n};\n\npluginMgr.on('update', function (result) {\n  Object.keys(result).forEach(function (name) {\n    pluginMgr.stopPlugin(result[name]);\n  });\n  readRegList();\n});\npluginMgr.on('uninstall', function (result) {\n  Object.keys(result).forEach(function (name) {\n    pluginMgr.stopPlugin(result[name]);\n  });\n});\n\nfunction formatMTime(mtime) {\n  return '[' + (new Date(mtime).toLocaleTimeString()) + ']';\n}\n\nfunction showVerbose(oldData, newData) {\n  if (!debugMode) {\n    return;\n  }\n  var uninstallData, installData, updateData;\n  Object.keys(oldData).forEach(function (name) {\n    var oldItem = oldData[name];\n    var newItem = newData[name];\n    if (!newItem) {\n      uninstallData = uninstallData || {};\n      uninstallData[name] = oldItem;\n    } else if (newItem.path != oldItem.path || newItem.mtime != oldItem.mtime) {\n      updateData = updateData || {};\n      updateData[name] = newItem;\n    }\n  });\n\n  Object.keys(newData).forEach(function (name) {\n    if (!oldData[name]) {\n      installData = installData || {};\n      installData[name] = newData[name];\n    }\n  });\n\n  uninstallData &&\n    Object.keys(uninstallData).forEach(function (name) {\n      console.log(\n        colors.red(\n          formatMTime(uninstallData[name].mtime) +\n            ' UNINSTALL ' + name.slice(0, -1)\n        )\n      );\n    });\n  installData &&\n    Object.keys(installData).forEach(function (name) {\n      console.log(\n        colors.green(\n          formatMTime(installData[name].mtime) +\n            ' INSTALL ' + name.slice(0, -1)\n        )\n      );\n    });\n  updateData &&\n    Object.keys(updateData).forEach(function (name) {\n      console.log(\n        colors.yellow(\n          formatMTime(updateData[name].mtime) +\n            ' UPDATE ' + name.slice(0, -1)\n        )\n      );\n    });\n}\n\nfunction readReqRules(dir, callback) {\n  pluginUtil.readFile(path.join(path.join(dir, '_rules.txt')), function (err, rulesText) {\n    if (err) {\n      pluginUtil.readFile(\n        path.join(path.join(dir, 'reqRules.txt')),\n        function (_, rulesText) {\n          callback(util.trim(rulesText));\n        }\n      );\n      return;\n    }\n    callback(util.trim(rulesText));\n  });\n}\n\nfunction readPackages(obj, callback) {\n  var _plugins = {};\n  var count = 0;\n  var callbackHandler = function () {\n    if (--count <= 0) {\n      callback(_plugins);\n    }\n  };\n  Object.keys(obj).forEach(function (name) {\n    var pkg = allPlugins[name];\n    var newPkg = obj[name];\n    if (!pkg || pkg.path != newPkg.path || pkg.mtime != newPkg.mtime) {\n      ++count;\n      common.readJson(path.join(newPkg.path, 'package.json'), function (_, result) {\n        if (result && result.version && pluginUtil.isPluginName(result.name)) {\n          var conf = result.whistleConfig || '';\n          var tabs = util.getInspectorTabs(conf);\n          var hintList = util.getHintList(conf);\n          var simpleName = name.slice(0, -1);\n          newPkg.enableAuthUI = !!conf.enableAuthUI;\n          newPkg.noOpt = !!(conf.noOption || conf.notOption || conf.disableOption|| conf.disabledOption);\n          newPkg.updateUrl = util.getUpdateUrl(conf);\n          newPkg.inheritAuth = !!conf.inheritAuth;\n          newPkg.tunnelKey = util.getTunnelKey(conf);\n          newPkg.version = result.version;\n          newPkg.staticDir = util.getStaticDir(conf);\n          newPkg.priority =\n            parseInt(conf.priority, 10) ||\n            parseInt(result.pluginPriority, 10) ||\n            0;\n          newPkg.rulesUrl = util.getCgiUrl(conf.rulesUrl);\n          newPkg.valuesUrl = util.getCgiUrl(conf.valuesUrl);\n          newPkg.installUrl = util.getCgiUrl(conf.installUrl);\n          newPkg.favicon = util.getCgiUrl(conf.favicon);\n          newPkg.installRegistry = util.getInstallRegistry(conf.installRegistry);\n          newPkg.networkColumn = util.getNetworkColumn(conf);\n          newPkg.networkMenus = util.getPluginMenu(\n            conf.networkMenus || conf.networkMenu,\n            simpleName\n          );\n          newPkg.rulesMenus = util.getPluginMenu(\n            conf.rulesMenus || conf.rulesMenu,\n            simpleName\n          );\n          newPkg.valuesMenus = util.getPluginMenu(\n            conf.valuesMenus || conf.valuesMenu,\n            simpleName\n          );\n          newPkg.pluginsMenus = util.getPluginMenu(\n            conf.pluginsMenus || conf.pluginsMenu,\n            simpleName\n          );\n          newPkg.reqTab = util.getCustomTab(tabs.req, simpleName);\n          newPkg.resTab = util.getCustomTab(tabs.res, simpleName);\n          newPkg.tab = util.getCustomTab(tabs, simpleName);\n          newPkg.toolTab = util.getCustomTab(conf.toolsTab || conf.toolTab, simpleName);\n          newPkg.comTab = util.getCustomTab(conf.composerTab, simpleName);\n          newPkg[util.PLUGIN_MENU_CONFIG] = util.getPluginMenuConfig(conf);\n          newPkg[util.PLUGIN_INSPECTOR_CONFIG] =\n            util.getPluginInspectorConfig(conf);\n          newPkg.hintUrl = hintList ? undefined : util.getCgiUrl(conf.hintUrl);\n          newPkg.hintList = hintList;\n          newPkg.pluginVars = util.getPluginVarsConf(conf);\n          newPkg.hideShortProtocol = !!conf.hideShortProtocol;\n          newPkg.hideLongProtocol = !!conf.hideLongProtocol;\n          newPkg.homepage = pluginUtil.getHomePageFromPackage(result) || pluginUtil.getHomePageFromPackage(conf);\n          newPkg.description = result.description;\n          newPkg.moduleName = result.name;\n          newPkg.pluginHomepage = pluginUtil.getPluginHomepage(result) || pluginUtil.getPluginHomepage(conf);\n          newPkg.openInPlugins = conf.openInPlugins ? 1 : undefined;\n          newPkg.openInModal = util.getPluginModal(conf);\n          newPkg.openExternal = conf.openExternal ? 1 : undefined;\n          newPkg.registry = util.getRegistry(result);\n          newPkg.latest = pkg && pkg.latest;\n          _plugins[name] = newPkg;\n          pluginUtil.readFile(\n            path.join(path.join(newPkg.path, 'rules.txt')),\n            function (err, rulesText) {\n              newPkg.rules = util.renderPluginRules(\n                util.trim(rulesText),\n                result,\n                simpleName\n              );\n              readReqRules(newPkg.path, function (rulesText) {\n                newPkg._rules = util.renderPluginRules(\n                  util.trim(rulesText),\n                  result,\n                  simpleName\n                );\n                pluginUtil.readFile(\n                  path.join(path.join(newPkg.path, '_values.txt')),\n                  function (err, rulesText) {\n                    newPkg[util.PLUGIN_VALUES] = pluginUtil.parseValues(util.renderPluginRules(rulesText, result, simpleName), simpleName);\n                    pluginUtil.readFile(\n                      path.join(path.join(newPkg.path, 'resRules.txt')),\n                      function (err, rulesText) {\n                        newPkg.resRules = util.renderPluginRules(\n                          util.trim(rulesText),\n                          result,\n                          simpleName\n                        );\n                        pluginUtil.readWorker(newPkg.path, conf, function(hash) {\n                          newPkg.webWorker = hash;\n                          callbackHandler();\n                        }, simpleName);\n                      }\n                    );\n                  }\n                );\n              });\n            }\n          );\n        } else {\n          callbackHandler();\n        }\n      });\n    } else {\n      _plugins[name] = pkg;\n    }\n  });\n\n  if (count <= 0) {\n    callback(_plugins);\n  }\n}\n\nfunction checkUpdate(pluginNames) {\n  pluginNames = pluginNames || Object.keys(allPlugins);\n  var name = pluginNames.shift();\n  var plugin;\n  while (name) {\n    if ((plugin = allPlugins[name]) && !plugin.isProj) {\n      break;\n    }\n    name = pluginNames.shift();\n  }\n  if (name) {\n    util.getLatestVersion(plugin, function (ver) {\n      if (ver && plugin.version !== ver) {\n        plugin.latest = ver;\n      }\n      checkUpdate(pluginNames);\n    });\n  } else {\n    setTimeout(checkUpdate, CHECK_INTERVAL);\n  }\n}\nsetTimeout(checkUpdate, 5000);\n\nvar updating;\nvar updateTimer;\nvar delayTimer;\n\nfunction refreshPlugins() {\n  updating = true;\n  delayTimer && clearTimeout(delayTimer);\n  updateTimer && clearTimeout(updateTimer);\n  delayTimer = null;\n  updateTimer = null;\n  getPlugin(function (result) {\n    readPackages(result, function (_plugins) {\n      var updatePlugins, uninstallPlugins;\n      var pluginNames = Object.keys(allPlugins);\n      pluginNames.forEach(function (name) {\n        var plugin = allPlugins[name];\n        var newPlugin = _plugins[name];\n        if (!newPlugin) {\n          uninstallPlugins = uninstallPlugins || {};\n          uninstallPlugins[name] = plugin;\n        } else if (\n            newPlugin.path != plugin.path ||\n            newPlugin.mtime != plugin.mtime\n          ) {\n          updatePlugins = updatePlugins || {};\n          updatePlugins[name] = newPlugin;\n        }\n      });\n      showVerbose(allPlugins, _plugins);\n      allPlugins = _plugins;\n      if (\n          uninstallPlugins ||\n          updatePlugins ||\n          Object.keys(_plugins).length !== pluginNames.length\n        ) {\n        uninstallPlugins && pluginMgr.emit('uninstall', uninstallPlugins);\n        updatePlugins && pluginMgr.emit('update', updatePlugins);\n        pluginMgr.emit('updateRules');\n        pluginUtil.resetWorkers(allPlugins);\n      }\n      updating = false;\n      update();\n    });\n  });\n}\n\nfunction update() {\n  if (!config.inspectMode && !updating) {\n    updateTimer = setTimeout(refreshPlugins, INTERVAL);\n  }\n}\n\nupdate();\n\npluginMgr.refreshPlugins = function() {\n  delayTimer = delayTimer || setTimeout(refreshPlugins, 200);\n};\n\nfunction addRealUrl(req) {\n  var realUrl = req._realUrl;\n  if (!realUrl) {\n    var href = req.options && req.options.href;\n    realUrl = util.isUrl(href) ? href : null;\n  }\n  if (realUrl && realUrl != req.fullUrl) {\n    req._finalUrl = realUrl;\n  }\n  var rule = req.rules && req.rules.rule;\n  if (rule) {\n    if (rule.url !== rule.matcher) {\n      req._relativeUrl = rule.url.substring(rule.matcher.length);\n    }\n    req._ruleUrl = rule.url;\n  }\n}\n\nfunction getPluginVars(vars, name) {\n  var value = vars && vars[name];\n  if (value) {\n    try {\n      value = JSON.stringify(value);\n      return Buffer.from(value).toString('base64');\n    } catch (e) {}\n  }\n}\n\nfunction addPluginVars(req, headers, rule) {\n  if (rule) {\n    var plugin = rule.plugin;\n    var name;\n    var value;\n    if (plugin) {\n      name = getPluginName(plugin.moduleName);\n      value = rule.value;\n    } else {\n      name = rule.matcher.split(':', 1)[0];\n      value = util.getMatcherValue(rule);\n    }\n    req._ruleValue = value;\n    if (rule.rawPattern) {\n      req._rawPattern = (rule.isRegExp ? 1 : 0) + ',' + rule.rawPattern;\n    }\n    if (rule.url) {\n      var extraUrl = rule.url;\n      if (value) {\n        extraUrl = rule.url.substring(value.length);\n      }\n      req._extraUrl = extraUrl;\n    }\n    req._globalPluginVarsValue = getPluginVars(req._globalPluginVars, name);\n    req._pluginVarsValue = getPluginVars(req._pluginVars, name);\n  }\n  addRealUrl(req);\n  common.setSessionInfo(req, headers);\n}\n\npluginMgr.addSessionInfo = function(req) {\n  var headers = req.headers;\n  var rules = req.rules;\n  addPluginVars(req, headers, rules && rules.rule);\n};\n\nfunction loadPlugin(plugin, callback) {\n  if (!plugin) {\n    return callback(null, '');\n  }\n  var ports = plugin[portsField];\n  if (ports) {\n    return callback(null, ports);\n  }\n  util.getBoundIp(config.host, function (host) {\n    conf.host = host || LOCALHOST;\n    var moduleName = plugin.moduleName;\n    var name = moduleName.substring(moduleName.indexOf('/') + 1);\n    var isInline = config.inspectMode || process.env.PFORK_MODE === 'inline';\n    p.fork(\n      {\n        data: config.getPluginData(moduleName),\n        _inspect: config.inspectMode,\n        name: moduleName,\n        script: PLUGIN_MAIN,\n        value: plugin.path,\n        isDev: plugin.isDev,\n        version: plugin.version,\n        staticDir: plugin.staticDir,\n        MAX_AGE_HEADER: MAX_AGE_HEADER,\n        ETAG_HEADER: ETAG_HEADER,\n        debugMode: debugMode,\n        config: isInline ? extend(true, {}, conf) : conf // 防止 inline 时，子进程删除 conf\n      },\n      function (err, ports, child, first) {\n        callback(err, ports);\n        if (!first) {\n          return;\n        }\n        if (err) {\n          proxy.emit('pluginLoadError', err, name, moduleName);\n          logger.error(err);\n          var mode = process.env.PFORK_MODE;\n          if (debugMode || mode === 'inline' || mode === 'bind') {\n            console.log(err);\n          }\n        } else {\n          proxy.emit('pluginLoad', child, name, moduleName);\n          plugin[portsField] = ports;\n          child.on('close', function () {\n            delete plugin[portsField];\n          });\n        }\n      }\n    );\n  });\n}\n\npluginMgr.loadPlugin = loadPlugin;\n\npluginMgr.loadPluginByName = function (name, callback) {\n  loadPlugin(getActivePluginByName(name), callback);\n};\n\npluginMgr.stopPlugin = function (plugin) {\n  p.kill(\n    {\n      script: PLUGIN_MAIN,\n      value: plugin.path\n    },\n    10000\n  );\n};\n\npluginMgr.getPlugins = function () {\n  return allPlugins;\n};\n\nfunction pluginIsDisabled(name) {\n  if (config.notAllowedDisablePlugins) {\n    return false;\n  }\n  if (properties.get('disabledAllPlugins')) {\n    return true;\n  }\n  var disabledPlugins = properties.get('disabledPlugins') || {};\n  return disabledPlugins[name];\n}\n\nfunction _getPlugin(protocol) {\n  return protocol && pluginIsDisabled(protocol.slice(0, -1)) ? null : allPlugins[protocol];\n}\n\npluginMgr.isDisabled = pluginIsDisabled;\npluginMgr.getPlugin = _getPlugin;\n\nfunction getActivePluginByName(name) {\n  return pluginIsDisabled(name) ? null : allPlugins[name + ':'];\n}\n\nrulesMgr.getPlugin = getActivePluginByName;\n\nfunction getPluginByName(name) {\n  return name && allPlugins[name + ':'];\n}\n\npluginMgr.getPluginByName = getPluginByName;\n\npluginMgr.getModifiablePlugin = function(name) {\n  name = getPluginByName(name);\n  return name && !name.isProj && !name.notUn ? name : null;\n};\n\nfunction getPluginByRuleUrl(ruleUrl) {\n  if (!ruleUrl || typeof ruleUrl != 'string') {\n    return;\n  }\n  var index = ruleUrl.indexOf(':');\n  if (index == -1) {\n    return null;\n  }\n  var protocol = ruleUrl.substring(0, index + 1);\n  return pluginIsDisabled(protocol.slice(0, -1)) ? null : allPlugins[protocol];\n}\n\npluginMgr.getPluginByRuleUrl = getPluginByRuleUrl;\n\nfunction _loadPlugins(plugins, callback) {\n  var rest = plugins.length;\n  var results = [];\n  var execCallback = function () {\n    --rest === 0 && callback(results);\n  };\n  plugins.forEach(function (plugin, i) {\n    loadPlugin(plugin, function (err, ports) {\n      plugin.ports = ports;\n      results[i] = ports || null;\n      execCallback();\n    });\n  });\n}\n\nfunction loadPlugins(plugins, callback) {\n  plugins = plugins.map(function (plugin) {\n    return plugin.plugin;\n  });\n  _loadPlugins(plugins, callback);\n}\n\npluginMgr.loadAuthPlugins = function (req, callback) {\n  if (config.disableAuthUI || !authPlugins.length) {\n    return callback();\n  }\n  req._isUIRequest = true;\n  _loadPlugins(authPlugins, function (ports) {\n    ports = ports.map(function (port, i) {\n      return {\n        authPort: port && port.authPort,\n        plugin: authPlugins[i]\n      };\n    });\n    var rest = ports.length;\n    if (!rest) {\n      return callback();\n    }\n    var options = getOptions(req);\n    authReq(true, ports, req, options, function (forbidden) {\n      if (forbidden) {\n        if (req._redirectUrl || req._authHtmlUrl) {\n          return callback(req._redirectUrl, null, req._authHtmlUrl);\n        }\n        var status = req._authStatus ? (req._showLoginBox ? 401 : 403) : 502;\n        return callback(status, forbidden);\n      }\n      return callback();\n    });\n  });\n};\n\nfunction parseRulesList(req, results, isResRules) {\n  var values = {};\n  results = results.filter(emptyFilter);\n  results.reverse().forEach(function (item) {\n    extend(values, item.values);\n  });\n  var pluginRulesMgr = new Rules(values);\n  pluginRulesMgr.parse(results);\n  if (isResRules) {\n    req.curUrl = req.fullUrl;\n    util.mergeRules(req, pluginRulesMgr.resolveRules(req), true);\n  }\n  return pluginRulesMgr;\n}\n\nfunction getPluginReqOpts(item, req, options, port) {\n  var opts = extend({}, options);\n  opts.headers = extend({}, options.headers);\n  opts.port = port;\n  addPluginVars(req, opts.headers, item);\n  return opts;\n}\n\nfunction authReq(isReq, ports, req, options, callback) {\n  if (!isReq) {\n    return callback();\n  }\n  var rest = ports.length;\n  var forbidden;\n  var execCallback = function () {\n    if (--rest === 0) {\n      callback(forbidden);\n    }\n  };\n  ports.forEach(function (item) {\n    if (!item.authPort) {\n      return execCallback();\n    }\n    options.headers[PLUGIN_HOOK_NAME_HEADER] = PLUGIN_HOOKS.AUTH;\n    var opts = getPluginReqOpts(item, req, options, item.authPort);\n    opts.maxLength = MAX_RULES_LENGTH;\n    requestPlugin(opts, function (err, body, res) {\n      var headers = res && res.headers;\n      if (err || body) {\n        if (!forbidden) {\n          if (debugMode) {\n            forbidden = err ? err.message || 'Error' : body;\n          } else {\n            forbidden = err ? 'Error' : body;\n          }\n          var file = item.plugin && getPluginName(item.plugin.moduleName);\n          req._statusFile = file;\n          if (headers) {\n            var authHtmlUrl = headers[common.AUTH_URL];\n            req._authStatus = headers[common.AUTH_STATUS];\n            if (authHtmlUrl) {\n              try {\n                authHtmlUrl = decodeURIComponent(authHtmlUrl);\n                req._authHtmlUrl = util.isUrl(authHtmlUrl) ? authHtmlUrl : 'file://' + authHtmlUrl;\n                req._htmlFile = file;\n              } catch (e) {}\n            } else if (headers.location) {\n              req._redirectUrl = headers.location;\n              req._redirectFile = file;\n            } else if (headers[config.SHOW_LOGIN_BOX]) {\n              req._showLoginBox = true;\n            }\n          }\n        }\n        err && logger.error(err);\n      }\n      if (!forbidden && headers) {\n        Object.keys(headers).forEach(function (key) {\n          if (\n            key.indexOf('x-whistle-') === 0 ||\n            key === 'proxy-authorization'\n          ) {\n            var value = headers[key];\n            if (key === config.WHISTLE_POLICY_HEADER &&\n              value === 'enableCaptureByAuth') {\n              req._forceCapture = true;\n            } else if (key === config.CLIENT_ID_HEADER) {\n              req._customClientId = value;\n            }\n            req.headers[key] = value;\n            options.headers[key] = value;\n          }\n        });\n      }\n      execCallback();\n    });\n  });\n}\n\nfunction getSrcName(file) {\n  return file ? util.getPluginFile(file) : 'Plugin Auth';\n}\n\nfunction getRulesFromPlugins(type, req, res, callback) {\n  var plugins = req.whistlePlugins;\n  loadPlugins(plugins, function (ports) {\n    ports = ports.map(function (port, i) {\n      var plugin = plugins[i];\n      return {\n        port: port && port[type + 'Port'],\n        authPort: port && port.authPort,\n        plugin: plugin.plugin,\n        isRegExp: plugin.isRegExp,\n        rawPattern: plugin.rawPattern,\n        value: plugin.value,\n        url: plugin.url\n      };\n    });\n\n    var rest = ports.length;\n    if (!rest) {\n      return callback();\n    }\n\n    var results = [];\n    var options = getOptions(req, res, type);\n    var isResRules = type == 'resRules';\n    var enableAuth =\n      !isResRules &&\n      req.justAuth !== false &&\n      (!req.isPluginReq || req._isProxyReq) &&\n      (!req.fromTunnel || !util.isAuthCapture(req));\n    authReq(enableAuth, ports, req, options, function (forbidden) {\n      if (forbidden) {\n        var noTunnel = !req.isTunnel;\n        req._authForbidden = true;\n        var file = getSrcName(req._statusFile);\n        var mgr = new Rules(util.toPrivateValues({ msg: forbidden }, file));\n        if (noTunnel && req._authHtmlUrl) {\n          mgr._file = getSrcName(req._htmlFile);\n          mgr.parse(\n            '* ignore://!method|!file|!http|!https method://get ' +\n              req._authHtmlUrl\n          );\n        } else if (noTunnel && req._redirectUrl) {\n          mgr._file = getSrcName(req._redirectFile);\n          mgr.parse('* ignore://!redirect redirect://' + req._redirectUrl);\n        } else {\n          var status = req._authStatus ? (req._showLoginBox ? (noTunnel ? 401 : 407) : 403) : 502;\n          mgr._file = file;\n          mgr.parse(\n            '* ignore://!statusCode|!resBody|!resType|!resCharset status://' +\n              status +\n              ' resBody://{msg} resType://html resCharset://utf8'\n          );\n        }\n        return callback(mgr);\n      }\n      if (req.justAuth) {\n        return callback();\n      }\n      var hookName = isResRules ? 'RES_RULES' : type === 'tunnelRules' ? 'TUNNEL_RULES' : 'REQ_RULES';\n      options.headers[PLUGIN_HOOK_NAME_HEADER] = PLUGIN_HOOKS[hookName];\n      var execCallback = function () {\n        if (--rest <= 0) {\n          if (req.resScriptRules) {\n            results = results.concat(req.resScriptRules);\n          }\n          callback(parseRulesList(req, results, isResRules));\n        }\n      };\n      ports.forEach(function (item, i) {\n        var plugin = item.plugin;\n        if (!item.port) {\n          var rulesText = isResRules ? plugin.resRules : plugin._rules;\n          if (rulesText) {\n            results[i] = {\n              text: rulesText,\n              values: plugin[util.PLUGIN_VALUES],\n              root: plugin.path\n            };\n          }\n          return execCallback();\n        }\n\n        var opts = getPluginReqOpts(item, req, options, item.port);\n        var cacheKey = plugin.moduleName + '\\n' + type;\n        var data = rulesCache.get(cacheKey);\n        var updateMaxAge = function (obj, age) {\n          if (age >= 0) {\n            obj.maxAge = age;\n            obj.now = Date.now();\n          }\n        };\n        var handleRules = function (err, body, values, raw, res) {\n          if (err === false && data) {\n            body = data.body;\n            values = data.values;\n            raw = data.raw;\n            updateMaxAge(data, res && res.headers[MAX_AGE_HEADER]);\n          } else {\n            values = util.toPrivateValues(values, getSrcName(getPluginName(plugin.moduleName)));\n            if (res) {\n              var etag = res.headers[ETAG_HEADER];\n              var maxAge = res.headers[MAX_AGE_HEADER];\n              var newData;\n              if (maxAge >= 0) {\n                newData = newData || {};\n                updateMaxAge(newData, maxAge);\n              }\n              if (etag) {\n                newData = newData || {};\n                newData.etag = etag;\n              }\n              if (newData) {\n                newData.body = body;\n                newData.values = values;\n                newData.raw = raw;\n                rulesCache.set(cacheKey, newData);\n              } else {\n                rulesCache.del(cacheKey);\n              }\n            }\n          }\n          var pendingCallbacks = data && data.pendingCallbacks;\n          if (pendingCallbacks) {\n            delete data.pendingCallbacks;\n            pendingCallbacks.forEach(function (cb) {\n              cb(err, body, values, raw);\n            });\n          }\n          body = body || '';\n          if (isResRules) {\n            body += plugin.resRules ? '\\n' + plugin.resRules : '';\n          } else {\n            body += plugin._rules ? '\\n' + plugin._rules : '';\n          }\n          if (body || values) {\n            var pluginVals = plugin[util.PLUGIN_VALUES];\n            if (values && pluginVals) {\n              var vals = extend({}, pluginVals);\n              values = extend(vals, values);\n            } else {\n              values = values || pluginVals;\n            }\n            results[i] = {\n              text: body,\n              values: values,\n              root: plugin.path\n            };\n          }\n          execCallback();\n        };\n        delete opts.headers[ETAG_HEADER];\n        if (data) {\n          if (Date.now() - data.now <= data.maxAge) {\n            return handleRules(false);\n          }\n          if (data.etag) {\n            opts.headers[ETAG_HEADER] = data.etag;\n          }\n          if (data.maxAge >= 0) {\n            if (data.pendingCallbacks) {\n              data.pendingCallbacks.push(handleRules);\n              return;\n            }\n            data.pendingCallbacks = [];\n          }\n        }\n        opts.ignoreExceedError = true;\n        opts.maxLength = MAX_RULES_LENGTH;\n        requestRules(opts, handleRules);\n      });\n    });\n  });\n}\n\nfunction removeInvalidHeaders(headers) {\n  delete headers.upgrade;\n  delete headers.connection;\n  delete headers['content-length'];\n}\n\nfunction getOptions(req, res, type) {\n  var fullUrl = req.fullUrl || util.getFullUrl(req);\n  var options = util.parseUrl(fullUrl);\n  var isResRules = res && type === 'resRules';\n  var headers = extend({}, isResRules ? res.headers : req.headers);\n\n  if (isResRules) {\n    headers.host = req.headers.host;\n    req.hostIp = req.hostIp || LOCALHOST;\n    req._statusCode = res.statusCode;\n    if (req.headers.cookie) {\n      headers.cookie = req.headers.cookie;\n    } else {\n      delete headers.cookie;\n    }\n  }\n  options.headers = headers;\n  removeInvalidHeaders(headers);\n  options.protocol = 'http:';\n  options.host = LOCALHOST;\n  options.hostname = null;\n  options.agent = false;\n\n  return options;\n}\n\nfunction requestPlugin(options, callback, retryCount) {\n  retryCount = retryCount || 0;\n  util.request(options, function (err, body, res) {\n    if (err && retryCount < 5) {\n      return requestPlugin(options, callback, ++retryCount);\n    }\n    if (res && res.statusCode == 304) {\n      return callback(false, null, res);\n    }\n    callback(null, body && body.trim(), res);\n  });\n}\n\nfunction requestRules(options, callback) {\n  requestPlugin(options, function (err, body, res) {\n    if (err === false) {\n      return callback(false, null, null, null, res);\n    }\n    var rules = body;\n    var values = null;\n    var data = !err && rulesToJson(body);\n    if (data) {\n      rules = data.text;\n      values = data.values;\n    }\n    callback(err, rules, values, body, res);\n  });\n}\n\nfunction rulesToJson(body) {\n  if (body && JSON_RE.test(body)) {\n    try {\n      body = JSON.parse(body);\n      return {\n        root: typeof body.root === 'string' ? body.root : null,\n        text: typeof body.rules === 'string' ? body.rules : '',\n        values: body.values\n      };\n    } catch (e) {}\n  }\n}\n\nfunction emptyFilter(val) {\n  return !!val;\n}\n\nfunction getRulesMgr(type, req, res, callback) {\n  var plugins = req.whistlePlugins;\n  if (!plugins) {\n    return callback();\n  }\n  getRulesFromPlugins(type, req, res, callback);\n}\n\nfunction getPluginRulesCallback(req, callback) {\n  return function (pluginRules) {\n    req.pluginRules = pluginRules;\n    callback(pluginRules);\n  };\n}\n\nfunction resolvePipePlugin(req, callback) {\n  if (req._pipePlugin == null) {\n    var pipe;\n    var hRules = req.headerRulesMgr;\n    if (config.multiEnv) {\n      pipe = (hRules && hRules.resolvePipe(req)) || rulesMgr.resolvePipe(req);\n    } else {\n      pipe = rulesMgr.resolvePipe(req) || (hRules && hRules.resolvePipe(req));\n    }\n    var plugin;\n    req._pipeRule = pipe;\n    if (pipe && PIPE_PLUGIN_RE.test(pipe.matcher)) {\n      req._pipeValue = RegExp.$2;\n      plugin = _getPlugin(RegExp.$1 + ':');\n    }\n    req._pipePlugin = plugin || '';\n  }\n  loadPlugin(req._pipePlugin, function (_, ports) {\n    req._pipePluginPorts = ports || '';\n    callback(ports);\n  });\n}\n\npluginMgr.resolvePipePlugin = resolvePipePlugin;\n\nfunction getPipe(type, hookName) {\n  var isRes = type.toLowerCase().indexOf('res') !== -1;\n  hookName = PLUGIN_HOOKS[hookName];\n  return function (req, res, callback) {\n    if (!isRes) {\n      callback = res;\n      res = null;\n    }\n    resolvePipePlugin(req, function (ports) {\n      var port = ports && ports[type + 'Port'];\n      if (!port || req._hasClosed || req._hasError) {\n        return callback();\n      }\n      var options = getOptions(req, res, isRes && 'resRules');\n      var rule = req.rules && req.rules.rule;\n      var item;\n      if (req._pipePlugin) {\n        rule = rule || rulesMgr.resolveRule(req);\n        item = { plugin: req._pipePlugin };\n        if (rule) {\n          var name = getPluginName(req._pipePlugin.moduleName) + '://';\n          if (rule.matcher.indexOf(name) === 0)\n            item.value = util.getMatcherValue(rule);\n        }\n      }\n      addPluginVars(req, options.headers, item);\n      options.headers[PLUGIN_HOOK_NAME_HEADER] = hookName;\n      options.headers[common.ACK_HEADER] = 1;\n      options.proxyHost = LOCALHOST;\n      options.proxyPort = port;\n      if (req._websocketExtensions !== null) {\n        if (req._websocketExtensions) {\n          req.headers['sec-websocket-extensions'] = req._websocketExtensions;\n        } else {\n          delete req.headers['sec-websocket-extensions'];\n        }\n      }\n      var client;\n      var done;\n      var handleConnect = function (socket, _res) {\n        if (!done) {\n          done = true;\n          if (req._hasError) {\n            return socket.destroy();\n          }\n          callback(socket);\n        }\n      };\n      var destroy = function () {\n        if (client) {\n          client.destroy();\n          client.socket && client.socket.destroy();\n          client = null;\n          handleConnect();\n        }\n      };\n      var handleError = function (err) {\n        if (client) {\n          destroy();\n          if (err) {\n            debugMode && console.log(req._pipeRule.matcher, type);\n            (res || req).emit('error', err);\n          }\n        }\n      };\n      client = config.connect(options, handleConnect);\n      client.on('error', function () {\n        if (client) {\n          destroy();\n          client = !req._hasError && config.connect(options, handleConnect);\n          client && util.onSocketEnd(client, handleError);\n        }\n      });\n      req.once('_closed', function() {\n        handleConnect();\n        destroy();\n      });\n    });\n  };\n}\n\npluginMgr.getReqReadPipe = getPipe('reqRead', 'REQ_READ');\npluginMgr.getReqWritePipe = getPipe('reqWrite', 'REQ_WRITE');\npluginMgr.getResReadPipe = getPipe('resRead', 'RES_READ');\npluginMgr.getResWritePipe = getPipe('resWrite', 'RES_WRITE');\n\nvar getWsReqReadPipe = getPipe('wsReqRead', 'WS_REQ_READ');\nvar getWsReqWritePipe = getPipe('wsReqWrite', 'WS_REQ_WRITE');\nvar getWsResReadPipe = getPipe('wsResRead', 'WS_RES_READ');\nvar getWsResWritePipe = getPipe('wsResWrite', 'WS_RES_WRITE');\nvar getTunnelReqReadPipe = getPipe('tunnelReqRead', 'TUNNEL_REQ_READ');\nvar getTunnelReqWritePipe = getPipe('tunnelReqWrite', 'TUNNEL_REQ_WRITE');\nvar getTunnelResReadPipe = getPipe('tunnelResRead', 'TUNNEL_RES_READ');\nvar getTunnelResWritePipe = getPipe('tunnelResWrite', 'TUNNEL_RES_WRITE');\n\npluginMgr.getWsPipe = function (req, res, callback) {\n  req._websocketExtensions = res.headers['sec-websocket-extensions'] || '';\n  getWsReqReadPipe(req, function (reqRead) {\n    getWsReqWritePipe(req, function (reqWrite) {\n      getWsResReadPipe(req, res, function (resReadStream) {\n        getWsResWritePipe(req, res, function (resWriteStream) {\n          callback(reqRead, reqWrite, resReadStream, resWriteStream);\n        });\n      });\n    });\n  });\n};\n\npluginMgr.getTunnelPipe = function (req, res, callback) {\n  getTunnelReqReadPipe(req, function (reqRead) {\n    getTunnelReqWritePipe(req, function (reqWrite) {\n      getTunnelResReadPipe(req, res, function (resRead) {\n        getTunnelResWritePipe(req, res, function (resWrite) {\n          callback(reqRead, reqWrite, resRead, resWrite);\n        });\n      });\n    });\n  });\n};\n\npluginMgr.getRules = function (req, callback) {\n  getRulesMgr('rules', req, null, getPluginRulesCallback(req, callback));\n};\n\npluginMgr.getResRules = function (req, res, callback) {\n  req.curUrl = req.fullUrl;\n  if (!req.resHeaders && res) {\n    req.resHeaders = res.headers;\n  }\n  var resRules = rulesMgr.resolveResRules(req, true);\n  var pRules = req.pluginRules && req.pluginRules.resolveResRules(req, true);\n  var fRules = req.rulesFileMgr && req.rulesFileMgr.resolveResRules(req, true);\n  var hRules =\n    req.headerRulesMgr && req.headerRulesMgr.resolveResRules(req, true);\n  fRules && util.mergeRules(req, fRules, true);\n  config.multiEnv && util.mergeRules(req, resRules, true);\n  hRules && util.mergeRules(req, hRules, true);\n  pRules && util.mergeRules(req, pRules, true);\n  !config.multiEnv && util.mergeRules(req, resRules, true);\n  var resScriptRules;\n  var resHeaderRules = res.headers[config.RES_RULES_HEAD];\n  if (resHeaderRules) {\n    try {\n      resHeaderRules = rulesToJson(decodeURIComponent(resHeaderRules));\n      if (resHeaderRules) {\n        resScriptRules = resScriptRules || [];\n        resScriptRules.push(resHeaderRules);\n      }\n    } catch (e) {}\n  }\n  delete res.headers[config.RES_RULES_HEAD];\n  rulesMgr.resolveResRulesFile(req, res, function (result) {\n    if (result) {\n      resScriptRules = resScriptRules || [];\n      resScriptRules.push(result);\n    }\n    req.resScriptRules = resScriptRules;\n    getRulesMgr('resRules', req, res, function (pluginRulesMgr) {\n      if (!pluginRulesMgr && resScriptRules) {\n        pluginRulesMgr = parseRulesList(req, resScriptRules, true);\n      }\n      req.resScriptRules = resScriptRules = null;\n      callback(pluginRulesMgr);\n    });\n  });\n};\n\npluginMgr.getTunnelRules = function (req, callback) {\n  getRulesMgr('tunnelRules', req, null, getPluginRulesCallback(req, callback));\n};\n\nfunction postStats(req, res) {\n  var plugins = req.whistlePlugins;\n  var type = res ? '_postResStats' : '_postReqStats';\n  if (!plugins || req.isPluginReq || req[type]) {\n    return;\n  }\n  req[type] = true;\n  loadPlugins(plugins, function (ports) {\n    ports = ports\n      .map(function (port, i) {\n        var plugin = plugins[i];\n        var statsPort = port && (res ? port.resStatsPort : port.statsPort);\n        if (!statsPort) {\n          return;\n        }\n        return {\n          plugin: plugin.plugin,\n          port: statsPort,\n          value: plugin.value,\n          url: plugin.url\n        };\n      })\n      .filter(emptyFilter);\n\n    if (!ports.length) {\n      return;\n    }\n    var options = getOptions(req, res, 'resRules');\n    options.headers[PLUGIN_HOOK_NAME_HEADER] = PLUGIN_HOOKS[res ? 'RES_STATS' : 'REQ_STATS'];\n    ports.forEach(function (item) {\n      var opts = getPluginReqOpts(item, req, options, item.port);\n      var request = http.request(opts, function (response) {\n        response.on('error', util.noop);\n        response.on('data', util.noop);\n      });\n      request.on('error', util.noop);\n      request.end();\n    });\n  });\n}\n\npluginMgr.postStats = postStats;\n\nvar PLUGIN_RULE_RE = /^([a-z\\d_\\-]+)(?:\\(([\\s\\S]*)\\))?$/;\nvar PLUGIN_RULE_RE2 = /^(?:\\w+\\.)?([a-z\\d_\\-]+)(?:\\:\\/\\/([\\s\\S]*))?$/;\nvar PLUGIN_RE = /^plugin:\\/\\//;\n\nfunction getPluginByPluginRule(pluginRule) {\n  if (!pluginRule) {\n    return;\n  }\n\n  var value = pluginRule.matcher;\n  if (PLUGIN_RE.test(value)) {\n    value = util.getMatcherValue(pluginRule);\n  } else {\n    value = util.rule.getMatcher(pluginRule);\n  }\n\n  if (PLUGIN_RULE_RE.test(value) || PLUGIN_RULE_RE2.test(value)) {\n    value = RegExp.$2;\n    var plugin = _getPlugin(RegExp.$1 + ':');\n    if (!plugin) {\n      return;\n    }\n    var ruleUrl = util.getUrlValue(pluginRule);\n    return (\n      plugin && {\n        plugin: plugin,\n        value: value,\n        url: ruleUrl == value ? undefined : ruleUrl\n      }\n    );\n  }\n}\n\nfunction resolveWhistlePlugins(req) {\n  var rules = req.rules;\n  var plugins = [];\n  var plugin = (req.pluginMgr = getPluginByRuleUrl(\n    util.rule.getUrl(rules.rule)\n  ));\n  if (plugin) {\n    rules._pluginRule = rules.rule;\n    var ruleValue = util.getMatcherValue(rules.rule);\n    var ruleUrl = util.getUrlValue(rules.rule);\n    plugins.push({\n      plugin: plugin,\n      value: ruleValue,\n      isRegExp: rules.rule.isRegExp,\n      rawPattern: rules.rule.rawPattern,\n      url: ruleUrl == ruleValue ? undefined : ruleUrl\n    });\n  }\n  if (rules.plugin) {\n    var _plugins = [plugin];\n    rules.plugin.list.forEach(function (rule) {\n      var info = getPluginByPluginRule(rule);\n      if (info && _plugins.indexOf(info.plugin) == -1) {\n        info.isRegExp = rule.isRegExp;\n        info.rawPattern = rule.rawPattern;\n        _plugins.push(info.plugin);\n        plugins.push(info);\n      }\n    });\n  }\n  if (plugins.length) {\n    req.whistlePlugins = plugins;\n  }\n  return plugin;\n}\n\npluginMgr.resolveWhistlePlugins = resolveWhistlePlugins;\n\npluginMgr.updatePluginRules = function (name) {\n  name &&\n    httpMgr.forceUpdate(name + '/' );\n};\n\npluginMgr.setProxy = function (p) {\n  proxy = p;\n};\n\nhttpMgr.setPluginMgr(pluginMgr);\n\nvar PLUGIN_KEY_RE =/^\\$(?:whistle\\.)?([a-z\\d_-]+)[/:]([\\S\\s]+)$/;\nvar MAX_VALUE_LEN = 1024 * 1024 * 16;\nvar MAX_URL_VAL_LEN = 1024 * 256;\n\nfunction requestValue(options, callback, isBin) {\n  options.needRawData = isBin;\n  var handleCallback = function(err, body, res) {\n    var code = res && res.statusCode;\n    if (code != 200) {\n      body = '';\n      if (!err) {\n        err = new Error('Error: response ' + code);\n        err.code = code || 500;\n      }\n    }\n    err && logger.error(err);\n    callback(body, err, res);\n  };\n  util.request(extend({}, options), function(err, body, res) {\n    if (err) {\n      return  util.request(options, handleCallback);\n    }\n    handleCallback(err, body, res);\n  });\n  return options;\n}\n\npluginMgr.resolveKey = function(url, rule, req) {\n  if (util.isUrl(url)) {\n    return {\n      url: url,\n      isInternalReq: req && req._isInternalReq,\n      originalKey: url,\n      maxLength: MAX_URL_VAL_LEN\n    };\n  }\n  if (!PLUGIN_KEY_RE.test(url)) {\n    return;\n  }\n  var name = RegExp.$1;\n  var key = RegExp.$2;\n  if (!getActivePluginByName(name)) {\n    return;\n  }\n  var headers = extend({}, req && req.headers, config.pluginHeaders);\n  if (req) {\n    req._ruleProtocol = protocols.getRuleProto(rule);\n    common.setSessionInfo(req, headers);\n    removeInvalidHeaders(headers);\n  }\n  return {\n    originalKey: url,\n    pluginName: name,\n    maxLength: MAX_VALUE_LEN,\n    url: name + '/api/key/value?key=' + encodeURIComponent(key),\n    headers: headers\n  };\n};\n\npluginMgr.requestText = function(options, callback) {\n  return requestValue(options, callback);\n};\n\npluginMgr.requestBin =function(options, callback) {\n  return requestValue(options, callback, true);\n};\n\nutil.setPluginMgr(pluginMgr);\n\nmodule.exports = pluginMgr;\n\nRules.setPluginMgr(pluginMgr);\n"
  },
  {
    "path": "lib/plugins/load-plugin.js",
    "content": "var listenerCount = require('../util/patch').listenerCount;\nvar path = require('path');\nvar fs = require('fs');\nvar net = require('net');\nvar qs = require('querystring');\nvar express = require('express');\nvar os = require('os');\nvar format = require('util').format;\nvar LRU = require('lru-cache');\nvar wsParser = require('ws-parser');\nvar http = require('http');\nvar https = require('https');\nvar extend = require('extend');\nvar gzip = require('zlib').gzip;\nvar request = require('../util/http-mgr').request;\nvar Storage = require('../rules/storage');\nvar getServer = require('hagent').create(null, 40500);\nvar parseUrl = require('../util/parse-url');\nvar hparser = require('hparser');\nvar transproto = require('../util/transproto');\nvar common = require('../util/common');\nvar getProxy = require('./proxy');\nvar rootRequire = require('../../require');\nvar extractSaz = require('../service/extract-saz');\nvar generateSaz = require('../service/generate-saz');\nvar getSharedStorage = require('./shared-storage');\nvar compat = require('./compat');\n\nvar getEncodeTransform = transproto.getEncodeTransform;\nvar getDecodeTransform = transproto.getDecodeTransform;\nvar setInternalOptions = common.setInternalOptions;\nvar toBuffer = common.toBuffer;\nvar readStream = common.readStream;\n\nvar LEVELS = ['log', 'error', 'warn', 'info', 'debug', 'trace'];\nvar HTTPS_RE = /^(?:https|wss):\\/\\//;\nvar MAX_BODY_SIZE = 1024 * 256;\nvar PING_INTERVAL = 22000;\nvar LOCALHOST = '127.0.0.1';\nvar sessionStorage = new LRU({\n  maxAge: 1000 * 60 * 12,\n  max: 1600\n});\nvar createServer = http.createServer;\nvar httpRequest = http.request;\nvar httpsRequest = https.request;\nvar formatHeaders = hparser.formatHeaders;\nvar getRawHeaderNames = hparser.getRawHeaderNames;\nvar getRawHeaders = hparser.getRawHeaders;\nvar STATUS_CODES = http.STATUS_CODES || {};\nvar clientIpKey = Symbol('clientIp');\nvar clientPortKey = Symbol('clientPort');\nvar remoteAddrKey = Symbol('remoteAddr');\nvar remotePortKey = Symbol('remotePort');\nvar pluginName;\n\nvar QUERY_RE = /\\?.*$/;\nvar REQ_ID_RE = /^\\d{13,15}-\\d{1,5}$/;\nvar sessionOpts, sessionTimer, sessionPending;\nvar framesOpts, framesTimer, framesPending;\nvar customParserOpts, customParserTimer, customParserPending;\nvar reqCallbacks = {};\nvar resCallbacks = {};\nvar parserCallbacks = {};\nvar framesList = [];\nvar framesCallbacks = [];\nvar MAX_LENGTH = 100;\nvar MAX_BUF_LEN = 1024 * 1024;\nvar TIMEOUT = 300;\nvar REQ_INTERVAL = 16;\nvar pluginOpts, storage, sharedStorage;\nvar MASK_OPTIONS = { mask: true };\nvar BINARY_MASK_OPTIONS = { mask: true, binary: true };\nvar BINARY_OPTIONS = { binary: true };\n/* eslint-disable no-undef */\nvar REQ_ID_KEY = Symbol('reqId');\nvar SESSION_KEY = Symbol('session');\nvar FRAME_KEY = Symbol('frame');\nvar REQ_KEY = Symbol('req');\nvar CLOSED = Symbol('closed');\nvar NOT_NAME_RE = /[^\\w.-]/;\n/* eslint-enable no-undef */\nvar index = 1000;\nvar pluginVersion = '';\nvar noop = common.noop;\nvar certsCache = new LRU({ max: 256 });\nvar certsCallbacks = {};\nvar ctx;\nvar PLUGIN_HOOK_NAME_HEADER;\nvar CLIENT_INFO_HEADER;\nvar PROXY_ID_HEADER;\nvar debugMode;\nvar pluginInited;\nvar writeDevLog;\n\nvar addRemoteInfo = function(req, headers) {\n  headers[CLIENT_INFO_HEADER] = [req[clientIpKey], req[clientPortKey], req[remoteAddrKey], req[remotePortKey]].join();\n};\n\nprocess._handlePforkUncaughtException = function (msg, e) {\n  msg = [\n    'From: ' + pluginName + pluginVersion,\n    'Node: ' + process.version,\n    'Host: ' + os.hostname(),\n    'Date: ' + new Date().toLocaleString(),\n    msg\n  ].join('\\n');\n  pluginInited && debugMode && console.error(msg); // eslint-disable-line\n  common.writeLogSync('\\r\\n' + msg + '\\r\\n');\n  writeDevLog && writeDevLog('\\n' + msg);\n  if (typeof process.handleUncaughtPluginErrorMessage === 'function') {\n    return process.handleUncaughtPluginErrorMessage(msg, e);\n  }\n};\n\nvar appendTrailers = function (_res, res, newTrailers, req) {\n  if (res.disableTrailer || res.disableTrailers) {\n    return;\n  }\n  common.addTrailerNames(_res, newTrailers, null, null, req);\n  common.onResEnd(_res, function () {\n    var trailers = _res.trailers;\n    if (\n      !res.chunkedEncoding ||\n      (common.isEmptyObject(trailers) && common.isEmptyObject(newTrailers))\n    ) {\n      return;\n    }\n    var rawHeaderNames = _res.rawTrailers\n      ? getRawHeaderNames(_res.rawTrailers)\n      : {};\n    if (newTrailers) {\n      newTrailers = common.lowerCaseify(newTrailers, rawHeaderNames);\n      if (trailers) {\n        extend(trailers, newTrailers);\n      } else {\n        trailers = newTrailers;\n      }\n    }\n    try {\n      common.removeIllegalTrailers(trailers);\n      res.addTrailers(formatHeaders(trailers, rawHeaderNames));\n    } catch (e) {}\n  });\n};\n\nvar requestData = function (options, callback) {\n  request(options, function (err, body) {\n    if (err) {\n      return callback(err);\n    }\n    try {\n      return callback(null, JSON.parse(body));\n    } catch (e) {\n      return callback(e);\n    }\n  });\n};\n\nvar setContext = function (req) {\n  if (ctx) {\n    req.ctx = ctx;\n  }\n  req.localStorage = storage;\n  req.Storage = Storage;\n  req.sharedStorage = sharedStorage;\n  // 原始请求信息 (Original request information)\n  var oReq = (req.originalReq = {});\n  var oRes = (req.originalRes = {});\n  var headers = req.headers;\n  var sessionInfo = common.getSessionInfo(headers);\n  if (!sessionInfo) {\n    var clientIp = headers[common.CLIENT_IP_HEADER];\n    req.clientIp = net.isIP(clientIp) ? clientIp : LOCALHOST;\n    req.clientPort = +headers[common.CLIENT_PORT_HEADER] || 0;\n    return;\n  }\n  var fullUrl = sessionInfo.fullUrl;\n  oReq.url = oReq.fullUrl = req.fullUrl = fullUrl;\n  oReq.extraUrl = sessionInfo._extraUrl;\n  oReq.ruleProtocol = sessionInfo._ruleProtocol;\n  req.isHttps = oReq.isHttps = HTTPS_RE.test(fullUrl);\n  req._isUpgrade = sessionInfo._isUpgrade === '1';\n  req.notDecompressed = oReq.notDecompressed = sessionInfo._noDecompress === '1';\n  req.fromTunnel = oReq.fromTunnel = sessionInfo.fromTunnel === '1';\n  req.fromComposer = oReq.fromComposer = sessionInfo.fromComposer === '1';\n  oReq.existsCustomCert = sessionInfo._existsCustomCert == '1';\n  oReq.isUIRequest = req.isUIRequest = sessionInfo._isUIRequest == '1';\n  oReq.enableCapture = sessionInfo._enableCapture == '1';\n  oReq.isFromPlugin = req.fromPlugin = oReq.fromPlugin = sessionInfo.isPluginReq == '1';\n  req[clientIpKey] = req.clientIp = oReq.clientIp = sessionInfo.clientIp || LOCALHOST;\n  req[clientPortKey] = req.clientPort = oReq.clientPort = +sessionInfo.clientPort || 0;\n  req[remoteAddrKey] = oReq.remoteAddress = sessionInfo._remoteAddr || LOCALHOST;\n  req[remotePortKey] = oReq.remotePort = +sessionInfo._remotePort || 0;\n  oReq.isHttp2 = oReq.isH2 = !!headers[common.ALPN_PROTOCOL_HEADER];\n  oReq.relativeUrl = sessionInfo._relativeUrl;\n  oReq.ruleUrl = sessionInfo._ruleUrl;\n  oReq.realUrl = sessionInfo._finalUrl || oReq.url;\n  oReq.sniValue = sessionInfo.sniRuleValue;\n  oReq.pipeValue = sessionInfo._pipeValue;\n  oReq.hostValue = sessionInfo.host;\n  oReq.proxyValue = sessionInfo.proxy;\n  oReq.pacValue = sessionInfo.pac;\n  oReq.globalValue = sessionInfo.globalValue;\n  oReq.servername = oReq.serverName = sessionInfo.serverName;\n  oReq.commonName = sessionInfo.commonName;\n  oReq.method = sessionInfo.method || 'GET';\n  oReq.serverIp = oRes.serverIp = sessionInfo.hostIp;\n  oReq.statusCode = oRes.statusCode = sessionInfo._statusCode;\n  oReq.ruleValue = sessionInfo._ruleValue;\n  oReq.pluginVars = getPluginVars(sessionInfo._pluginVarsValue);\n  oReq.globalPluginVars = getPluginVars(sessionInfo._globalPluginVarsValue);\n\n  var originHost = headers[common.ORIGIN_HOST_HEADER]; // for web ui;\n  oReq.originHost = common.isString(originHost) ? originHost : '';\n\n  var pattern = sessionInfo._rawPattern;\n  if (pattern && pattern[1] === ',') {\n    oReq.isRexExp = oReq.isRegExp = pattern[0] === '1';\n    oReq.pattern = pattern.substring(2);\n  }\n  var sniType = sessionInfo._sniType;\n  var isSNI = !sniType;\n  if (sniType && sniType === '1') {\n    isSNI = true;\n    req.isHttpsServer = true;\n  }\n  req.useSNI = oReq.useSNI = req.isSNI = oReq.isSNI = isSNI;\n  oReq.headers = headers;\n\n  var certCacheInfo = sessionInfo.hasCertCache;\n  oReq.certCacheName = certCacheInfo;\n  oReq.certCacheTime = 0;\n  if (certCacheInfo) {\n    var sepIndex = certCacheInfo.indexOf('+');\n    if (sepIndex !== -1) {\n      oReq.certCacheName = certCacheInfo.substring(0, sepIndex);\n      oReq.certCacheTime = parseInt(certCacheInfo.substring(sepIndex + 1)) || 0;\n    }\n  }\n  return compat.compatHeaders(req, sessionInfo);\n};\n\nvar initState = function (req, name) {\n  switch (name) {\n  case 'pauseSend':\n    req.curSendState = 'pause';\n    return;\n  case 'ignoreSend':\n    req.curSendState = 'ignore';\n    return;\n  case 'pauseReceive':\n    req.curReceiveState = 'pause';\n    return;\n  case 'ignoreReceive':\n    req.curReceiveState = 'ignore';\n    return;\n  }\n};\n\nvar getFrameId = function () {\n  ++index;\n  if (index > 9990) {\n    index = 1000;\n  }\n  if (index > 99) {\n    return Date.now() + '-' + index;\n  }\n  if (index > 9) {\n    return Date.now() + '-0' + index;\n  }\n  return Date.now() + '-00' + index;\n};\n\nvar addFrame = function (frame) {\n  framesList.push(frame);\n  if (framesList.length > 720) {\n    framesList.splice(0, 80);\n  }\n};\n\nvar getFrameOpts = function (opts) {\n  if (!opts) {\n    return {};\n  }\n  if (opts === true) {\n    return { ignore: true };\n  }\n  var result = {};\n  if (opts.ignore === true) {\n    result.ignore = true;\n  }\n  if (opts.compressed === true) {\n    result.compressed = true;\n  }\n  if (opts.opcode > 0) {\n    result.opcode = opts.opcode == 1 ? 1 : 2;\n  }\n  if (opts.isError) {\n    result.isError = true;\n  }\n  if (typeof opts.charset === 'string') {\n    result.charset = opts.charset;\n  }\n  return result;\n};\nvar pushFrame = function (reqId, data, opts, isClient) {\n  if (data == null) {\n    return;\n  }\n  if (!Buffer.isBuffer(data)) {\n    try {\n      if (typeof data !== 'string') {\n        data = JSON.stringify(data);\n      }\n      data = data && Buffer.from(data);\n    } catch (e) {\n      data = null;\n    }\n  }\n  if (!data) {\n    return;\n  }\n  opts = getFrameOpts(opts);\n  opts.reqId = reqId;\n  opts.frameId = getFrameId();\n  opts.isClient = isClient;\n  opts.length = data.length;\n  if (opts.length > MAX_BUF_LEN) {\n    data = data.slice(0, MAX_BUF_LEN);\n  }\n  opts.base64 = data.toString('base64');\n  addFrame(opts);\n};\nvar addParserApi = function (req, conn, state, reqId) {\n  state = state.split(',').forEach(function (name) {\n    initState(req, name);\n  });\n  req.on('clientFrame', function (data, opts) {\n    pushFrame(reqId, data, opts, true);\n  });\n  req.on('serverFrame', function (data, opts) {\n    pushFrame(reqId, data, opts);\n  });\n  var on = req.on;\n  req.on = function (eventName) {\n    on.apply(this, arguments);\n    var curState, prevState;\n    if (eventName === 'sendStateChange') {\n      curState = req.curSendState;\n      prevState = req.prevSendState;\n    } else if (eventName === 'receiveStateChange') {\n      curState = req.curReceiveState;\n      prevState = req.prevReceiveState;\n    }\n    if (curState || prevState) {\n      req.emit(eventName, curState, prevState);\n    }\n  };\n  var disconnected;\n  var emitDisconnect = function (err) {\n    if (disconnected) {\n      return;\n    }\n    req.isDisconnected = disconnected = true;\n    addFrame({\n      reqId: reqId,\n      frameId: getFrameId(),\n      closed: !err,\n      err: err && err.message,\n      bin: ''\n    });\n    delete parserCallbacks[reqId];\n    req.emit('disconnect', err);\n  };\n  conn.on('error', emitDisconnect);\n  conn.on('close', emitDisconnect);\n  parserCallbacks[reqId] = function (data) {\n    if (!data) {\n      return conn.destroy();\n    }\n    var sendState, receiveState;\n    if (data.sendStatus === 1) {\n      sendState = 'pause';\n    } else if (data.sendStatus === 2) {\n      sendState = 'ignore';\n    }\n    if (data.receiveStatus === 1) {\n      receiveState = 'pause';\n    } else if (data.receiveStatus === 2) {\n      receiveState = 'ignore';\n    }\n    var curSendState = req.curSendState;\n    if (curSendState != sendState) {\n      req.prevSendState = req.curSendState;\n      req.curSendState = sendState;\n      try {\n        req.emit('sendStateChange', req.curSendState, req.prevSendState);\n      } catch (e) {}\n    }\n    var curReceiveState = req.curReceiveState;\n    if (curReceiveState != receiveState) {\n      req.prevReceiveState = req.curReceiveState;\n      req.curReceiveState = receiveState;\n      try {\n        req.emit(\n          'receiveStateChange',\n          req.curReceiveState,\n          req.prevReceiveState\n        );\n      } catch (e) {}\n    }\n    if (Array.isArray(data.toClient)) {\n      data.toClient.forEach(function (frame) {\n        var buf = base64ToBuffer(frame.base64);\n        try {\n          buf && req.emit('sendToClient', buf, frame.binary);\n        } catch (e) {}\n      });\n    }\n    if (Array.isArray(data.toServer)) {\n      data.toServer.forEach(function (frame) {\n        var buf = base64ToBuffer(frame.base64);\n        try {\n          buf && req.emit('sendToServer', buf, frame.binary);\n        } catch (e) {}\n      });\n    }\n  };\n  retryCustomParser();\n};\n\nvar addSessionStorage = function (req, id) {\n  req.sessionStorage = {\n    set: function (key, value) {\n      var cache = sessionStorage.get(id);\n      if (!cache) {\n        cache = {};\n        sessionStorage.set(id, cache);\n      }\n      cache[key] = value;\n      return value;\n    },\n    get: function (key) {\n      var cache = sessionStorage.get(id);\n      return cache && cache[key];\n    },\n    remove: function (key) {\n      var cache = sessionStorage.peek(id);\n      if (cache) {\n        delete cache[key];\n      }\n    }\n  };\n};\n\nvar ADDITIONAL_FIELDS = [\n  'headers',\n  'rawHeaders',\n  'trailers',\n  'rawTrailers',\n  'url',\n  'method',\n  'statusCode',\n  'statusMessage',\n  'sendEstablished',\n  'unsafe_getReqSession',\n  'unsafe_getSession',\n  'unsafe_getFrames',\n  'getReqSession',\n  'getSession',\n  'getFrames',\n  'request',\n  'originalReq',\n  'response',\n  'originalRes',\n  'localStorage',\n  'Storage',\n  'clientIp',\n  'sessionStorage'\n];\n\nfunction getPluginVars(value) {\n  value = base64ToBuffer(value);\n  if (value) {\n    try {\n      value = JSON.parse(value.toString());\n      if (Array.isArray(value)) {\n        return value;\n      }\n    } catch (e) {}\n  }\n  return [];\n}\n\nvar initReq = function (req, res, isServer) {\n  if (req.originalReq && req.originalRes) {\n    return;\n  }\n  var destroy = function () {\n    if (!req._hasError) {\n      req._hasError = true;\n      req.destroy && req.destroy();\n      res.destroy && res.destroy();\n    }\n  };\n  req.on('error', destroy);\n  res.on('error', destroy);\n  req.getReqSession = req.unsafe_getReqSession = function (cb) {\n    return getSession(req, cb, true);\n  };\n  req.getSession = req.unsafe_getSession = function (cb) {\n    return getSession(req, cb);\n  };\n  req.getFrames = req.unsafe_getFrames = function (cb) {\n    return getFrames(req, cb);\n  };\n\n  var sessionInfo = setContext(req) || {};\n  var oReq = req.originalReq;\n  var reqId = sessionInfo.reqId;\n  if (isServer) {\n    var parseStatus = sessionInfo.customParser;\n    req.customParser = oReq.customParser = !!parseStatus;\n    req.customParser && addParserApi(req, res, parseStatus, reqId);\n  }\n  req[REQ_ID_KEY] = oReq.id = reqId;\n  addSessionStorage(req, reqId);\n  oReq.clientId = String(req.headers[common.CLIENT_ID_HEADER] || '');\n};\nvar getOptions = function (opts, binary, toServer) {\n  if (opts) {\n    opts.mask = toServer;\n    opts.binary = opts.binary || opts.opcode == 2;\n    return opts;\n  }\n  if (toServer) {\n    return binary ? BINARY_MASK_OPTIONS : MASK_OPTIONS;\n  }\n  return binary ? BINARY_OPTIONS : '';\n};\nvar base64ToBuffer = function (base64) {\n  if (base64) {\n    try {\n      return new Buffer(base64, 'base64');\n    } catch (e) {}\n  }\n};\nvar getBuffer = function (item) {\n  return base64ToBuffer(item.base64);\n};\nvar getText = function (item) {\n  var body = base64ToBuffer(item.base64) || '';\n  return common.bufferToString(body);\n};\n\nvar defineProps = function (obj) {\n  if (!obj) {\n    return;\n  }\n  if (Object.defineProperties) {\n    Object.defineProperties(obj, {\n      body: {\n        get: function () {\n          return getText(obj);\n        }\n      },\n      buffer: {\n        get: function () {\n          return getBuffer(obj);\n        }\n      }\n    });\n  } else {\n    obj.body = getText(obj);\n    obj.buffer = getBuffer(obj);\n  }\n};\n\nvar execCallback = function (id, cbs, item) {\n  var cbList = cbs[id];\n  if (cbList && (cbs === reqCallbacks || !item || item.endTime)) {\n    item = item || '';\n    defineProps(item.req);\n    defineProps(item.res);\n    delete cbs[id];\n    cbList.forEach(function (cb) {\n      try {\n        cb(item);\n      } catch (e) {}\n    });\n  }\n};\n\nvar retryRequestSession = function (time) {\n  if (!sessionTimer) {\n    sessionTimer = setTimeout(requestSessions, time || TIMEOUT);\n  }\n};\n\nvar requestSessions = function () {\n  clearTimeout(sessionTimer);\n  sessionTimer = null;\n  if (sessionPending) {\n    return;\n  }\n  var reqList = Object.keys(reqCallbacks);\n  var resList = Object.keys(resCallbacks);\n  if (!reqList.length && !resList.length) {\n    return;\n  }\n  sessionPending = true;\n  var _reqList = reqList.slice(0, MAX_LENGTH);\n  var _resList = resList.slice(0, MAX_LENGTH);\n  var query =\n    '?reqList=' + JSON.stringify(_reqList) +\n    '&resList=' + JSON.stringify(_resList);\n  sessionOpts.path = sessionOpts.path.replace(QUERY_RE, query);\n  sessionOpts.search = query;\n  requestData(sessionOpts, function (err, result) {\n    sessionPending = false;\n    if (err || !result) {\n      return retryRequestSession();\n    }\n    Object.keys(result).forEach(function (id) {\n      var item = result[id];\n      execCallback(id, reqCallbacks, item);\n      execCallback(id, resCallbacks, item);\n    });\n    retryRequestSession(REQ_INTERVAL);\n  });\n};\n\nvar retryRequestFrames = function (time) {\n  if (!framesTimer) {\n    framesTimer = setTimeout(requestFrames, time || TIMEOUT);\n  }\n};\nvar requestFrames = function () {\n  clearTimeout(framesTimer);\n  framesTimer = null;\n  if (framesPending) {\n    return;\n  }\n  var cb = framesCallbacks.shift();\n  if (!cb) {\n    return;\n  }\n  var req = cb[REQ_KEY];\n  if (req[CLOSED]) {\n    return cb('');\n  }\n  framesPending = true;\n  var query =\n    '?curReqId=' + req[REQ_ID_KEY] + '&lastFrameId=' + (req[FRAME_KEY] || '');\n  framesOpts.path = framesOpts.path.replace(QUERY_RE, query);\n  framesOpts.search = query;\n  requestData(framesOpts, function (err, result) {\n    framesPending = false;\n    if (err || !result) {\n      framesCallbacks.push(cb);\n      return retryRequestFrames();\n    }\n    var frames = result.frames;\n    var closed;\n    if (Array.isArray(frames)) {\n      var last = frames[frames.length - 1];\n      var frameId = last && last.frameId;\n      if (frameId) {\n        req[FRAME_KEY] = frameId;\n        frames.forEach(defineProps);\n        closed = !!(last.closed || last.err);\n      }\n    } else {\n      closed = !frames;\n    }\n    if (closed || frames.length) {\n      req[CLOSED] = closed;\n      try {\n        cb(frames || '');\n      } catch (e) {}\n    } else {\n      framesCallbacks.push(cb);\n    }\n    retryRequestFrames(REQ_INTERVAL);\n  });\n};\n\nvar retryCustomParser = function (time) {\n  if (!customParserTimer) {\n    customParserTimer = setTimeout(customParser, time || TIMEOUT);\n  }\n};\n\nvar customParser = function () {\n  clearTimeout(customParserTimer);\n  customParserTimer = null;\n  if (customParserPending) {\n    return;\n  }\n  var idList = Object.keys(parserCallbacks);\n  if (!idList.length && !framesList.length) {\n    return;\n  }\n  customParserPending = true;\n  customParserOpts.body = {\n    idList: idList,\n    frames: framesList.splice(0, 10)\n  };\n  requestData(customParserOpts, function (err, result) {\n    customParserPending = false;\n    customParserOpts.body = undefined;\n    if (err || !result) {\n      return retryCustomParser();\n    }\n    idList.forEach(function (reqId) {\n      var cb = parserCallbacks[reqId];\n      cb && cb(result[reqId]);\n    });\n    retryCustomParser(framesList.length > 0 ? 20 : 300);\n  });\n};\n\nfunction isFrames(item) {\n  if (/^wss?:\\/\\//.test(item.url)) {\n    return item.res.statusCode == 101;\n  }\n  return item.inspect || item.useFrames;\n}\n\nvar getFrames = function (req, cb) {\n  var reqId = req[REQ_ID_KEY];\n  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {\n    return;\n  }\n  if (req[CLOSED]) {\n    return cb('');\n  }\n  cb[REQ_KEY] = req;\n  getSession(req, function (session) {\n    if (\n      !session ||\n      session.reqError ||\n      session.resError ||\n      !isFrames(session)\n    ) {\n      req[CLOSED] = 1;\n      return cb('');\n    }\n    framesCallbacks.push(cb);\n    requestFrames();\n  });\n};\n\nvar getSession = function (req, cb, isReq) {\n  var reqId = req[REQ_ID_KEY];\n  if (!REQ_ID_RE.test(reqId) || typeof cb !== 'function') {\n    return;\n  }\n  var session = req[SESSION_KEY];\n  if (session != null) {\n    if (isReq) {\n      return cb(session);\n    }\n    if (!session || session.endTime) {\n      return cb(session);\n    }\n  }\n  var cbList = isReq ? reqCallbacks[reqId] : resCallbacks[reqId];\n  if (cbList) {\n    if (cbList.indexOf(cb) === -1) {\n      cbList.push(cb);\n    }\n  } else {\n    cbList = [\n      function (s) {\n        req[SESSION_KEY] = s;\n        cb(s);\n      }\n    ];\n  }\n  if (isReq) {\n    reqCallbacks[reqId] = cbList;\n  } else {\n    resCallbacks[reqId] = cbList;\n  }\n  retryRequestSession();\n};\n\nvar initWsReq = function (req, res) {\n  initReq(req, res, true);\n};\nvar initConnectReq = function (req, res) {\n  if (req.originalReq && req.originalRes) {\n    return;\n  }\n  var established;\n  initWsReq(req, res);\n  req.sendEstablished = function (err, cb) {\n    if (established) {\n      return;\n    }\n    if (typeof err === 'function') {\n      cb = err;\n      err = null;\n    }\n    req.isEstablished = true;\n    established = true;\n    var msg = err ? 'Bad Gateway' : 'Connection Established';\n    var body = String((err && err.stack) || '');\n    var length = Buffer.byteLength(body);\n    var resCtn = [\n      'HTTP/1.1 ' + (err ? 502 : 200) + ' ' + msg,\n      'Content-Length: ' + length,\n      'Proxy-Agent: ' + pluginOpts.shortName\n    ];\n    if (err || !cb || !req.headers[common.ACK_HEADER]) {\n      resCtn.push('\\r\\n', body);\n      res.write(resCtn.join('\\r\\n'));\n      return cb && cb();\n    }\n    resCtn.push('x-whistle-allow-tunnel-ack: 1');\n    resCtn.push('\\r\\n', body);\n    res.once('data', function (chunk) {\n      if (!req._hasError) {\n        res.pause();\n        var on = res.on;\n        res.on = function () {\n          res.on = on;\n          res.resume();\n          return on.apply(this, arguments);\n        };\n        chunk.length > 1 && res.unshift(chunk.slice(1));\n        cb();\n      }\n    });\n    return res.write(resCtn.join('\\r\\n'));\n  };\n};\n\nvar loadModule = function (filepath) {\n  try {\n    return require(filepath);\n  } catch (e) {}\n};\n\nfunction getFunction(fn) {\n  return typeof fn === 'function' ? fn : null;\n}\n\nfunction notEmptyStr(str) {\n  return str && typeof str === 'string';\n}\n\nfunction getHookName(req) {\n  var name = req.headers[PLUGIN_HOOK_NAME_HEADER];\n  delete req.headers[PLUGIN_HOOK_NAME_HEADER];\n  return typeof name === 'string' ? name : null;\n}\n\nfunction handleError(socket, sender, receiver) {\n  var emitError = function (err) {\n    if (socket._emittedError) {\n      return;\n    }\n    socket._emittedError = true;\n    socket.emit('error', err);\n  };\n  sender && sender.on('error', emitError);\n  receiver && (receiver.onerror = emitError);\n}\n\nvar GZIP_RE = /^\\s*gzip\\s*$/i;\n\nfunction getCustomBody(body, req, cb) {\n  var headers = req.headers;\n  if (body && typeof body.pipe === 'function') {\n    delete headers['content-length'];\n    return cb(body);\n  }\n  var handleCb = function() {\n    if (headers['content-length'] != null) {\n      headers['content-length'] = body.length;\n      delete headers['transfer-encoding'];\n    }\n    cb(body);\n  };\n  if (Buffer.isBuffer(body)) {\n    return handleCb();\n  }\n  var encoding = headers['content-encoding'];\n  if (body && typeof body === 'object' && common.isUrlEncoded(req)) {\n    body = qs.stringify(body);\n  }\n  body = toBuffer(body) || '';\n  if (!GZIP_RE.test(encoding)) {\n    return handleCb();\n  }\n  gzip(body, function(_, result) {\n    body = result || body;\n    handleCb();\n  });\n}\n\nfunction wrapTunnelWriter(socket, toServer) {\n  var write = socket.write;\n  var end = socket.end;\n  var sender = wsParser.getSender(socket, toServer);\n  handleError(socket, sender);\n  socket.write = function (chunk, encoding, cb) {\n    if ((chunk = toBuffer(chunk))) {\n      if (encoding === 'binary') {\n        return write.call(this, chunk, encoding, cb);\n      }\n      if (toServer) {\n        sender.send(chunk, extend({ mask: true }, encoding));\n      } else {\n        sender.send(chunk);\n      }\n    }\n  };\n  if (toServer) {\n    socket.write = function (chunk, opts, cb) {\n      if ((chunk = toBuffer(chunk))) {\n        if (opts === 'binary') {\n          return write.call(this, chunk, opts, cb);\n        }\n        sender.send(chunk, getOptions(opts, false, true));\n      }\n    };\n    socket.writeText = function (chunk) {\n      if ((chunk = toBuffer(chunk))) {\n        sender.send(chunk, getOptions(null, false, true));\n      }\n    };\n    socket.writeBin = function (chunk) {\n      if ((chunk = toBuffer(chunk))) {\n        sender.send(chunk, getOptions(null, true, true));\n      }\n    };\n    socket.closeWebSocket = function (code, data, cb) {\n      sender.close(code || 1000, data, true, cb);\n    };\n    socket.ping = function (data) {\n      return sender.ping(data, { mask: true });\n    };\n  } else {\n    socket.write = function (chunk, encoding, cb) {\n      if ((chunk = toBuffer(chunk))) {\n        if (encoding === 'binary') {\n          return write.call(this, chunk, encoding, cb);\n        }\n        sender.send(chunk);\n      }\n    };\n  }\n  socket.end = function (chunk, encoding, cb) {\n    chunk && socket.write(chunk, encoding, cb);\n    return end.call(this);\n  };\n  return socket;\n}\n\nfunction wrapTunnelReader(socket, fromServer, maxPayload) {\n  socket.wsExts = '';\n  var receiver = wsParser.getReceiver(socket, fromServer, maxPayload);\n  var emit = socket.emit;\n  handleError(socket, null, receiver);\n  socket.emit = function (type, chunk) {\n    if (type === 'data' && chunk) {\n      return receiver.add(chunk);\n    }\n    return emit.apply(this, arguments);\n  };\n  receiver.onData = function (chunk, opts) {\n    emit.call(socket, 'data', chunk, opts);\n  };\n  receiver.onpong = function (data, opts) {\n    socket.emit('pong', data, opts);\n  };\n  return socket;\n}\n\nfunction getReqRules(opts, reqRules) {\n  var rules = opts && opts.rules;\n  if (!notEmptyStr(rules)) {\n    return reqRules;\n  }\n  return reqRules ? rules + '\\n' + reqRules : rules;\n}\n\nfunction setReqRules(uri, reqRules) {\n  if (!reqRules) {\n    return;\n  }\n  var rules = formatRules(reqRules);\n  var values = reqRules.values;\n  if (!rules) {\n    return;\n  }\n  uri.headers = uri.headers || {};\n  uri.headers['x-whistle-rule-value'] = encodeURIComponent(rules);\n  if (!values) {\n    return;\n  }\n  if (typeof values !== 'string') {\n    try {\n      values = JSON.stringify(values);\n    } catch (e) {\n      return;\n    }\n  }\n  uri.headers['x-whistle-key-value'] = encodeURIComponent(values);\n}\n\nfunction addFrameHandler(req, socket, maxWsPayload, fromClient, toServer) {\n  socket.wsExts = req.headers['sec-websocket-extensions'] || '';\n  var receiver = wsParser.getReceiver(socket, !fromClient, maxWsPayload, req.notDecompressed);\n  var emit = socket.emit;\n  var write = socket.write;\n  var end = socket.end;\n  var lastOpts;\n  var sender = wsParser.getSender(socket, toServer);\n\n  handleError(socket, sender, receiver);\n  socket.emit = function (type, chunk) {\n    if (type === 'data' && chunk) {\n      return receiver.add(chunk);\n    }\n    return emit.apply(this, arguments);\n  };\n  receiver.onData = function (chunk, opts) {\n    if (opts && opts.opcode == 2) {\n      opts.binary = true;\n    }\n    lastOpts = opts;\n    emit.call(socket, 'data', chunk, opts);\n  };\n  socket.write = function (chunk, opts, cb) {\n    if ((chunk = toBuffer(chunk))) {\n      if (opts === 'binary') {\n        return write.call(this, chunk, opts, cb);\n      }\n      sender.send(chunk, getOptions(opts || lastOpts, false, toServer));\n    }\n  };\n  socket.writeText = function (chunk) {\n    if ((chunk = toBuffer(chunk))) {\n      sender.send(chunk, getOptions(null, false, toServer));\n    }\n  };\n  socket.writeBin = function (chunk) {\n    if ((chunk = toBuffer(chunk))) {\n      sender.send(chunk, getOptions(null, true, toServer));\n    }\n  };\n  socket.end = function (chunk, opts, cb) {\n    chunk && socket.write(chunk, opts, cb);\n    return end.call(this);\n  };\n  if (fromClient === toServer) {\n    return handleWsSignal(receiver, sender);\n  }\n  return {\n    receiver: receiver,\n    sender: sender\n  };\n}\n\nfunction formatRawHeaders(headers, req) {\n  var rawHeaders = headers && (req.rawHeaders || req);\n  if (!Array.isArray(rawHeaders)) {\n    return headers;\n  }\n  var rawNames = getRawHeaderNames(rawHeaders);\n  if (headers.trailer && !rawNames.trailer) {\n    rawNames.trailer = 'Trailer';\n  }\n  return formatHeaders(headers, rawNames);\n}\n\nfunction addErrorHandler(req, client) {\n  var done;\n  client.on('error', function (err) {\n    if (!done) {\n      done = true;\n      req.destroy && req.destroy(err);\n      client.abort && client.abort();\n    }\n  });\n}\n\nfunction handleWsSignal(receiver, sender) {\n  receiver.onping = sender.ping.bind(sender);\n  receiver.onpong = sender.pong.bind(sender);\n  receiver.onclose = function (code, message, opts) {\n    sender.close(code, message, opts.masked);\n  };\n}\n\nfunction destroySocket() {\n  this.destroy();\n}\n\nfunction formatRules(rules) {\n  if (Array.isArray(rules)) {\n    return rules.join('\\n');\n  }\n  rules = rules.rules || rules;\n  return typeof rules === 'string' ? rules : undefined;\n}\n\nfunction isBodyData(result) {\n  return typeof result === 'string' || Buffer.isBuffer(result) || typeof result.pipe === 'function';\n}\n\nmodule.exports = async function (options, callback) {\n  var root = options.value;\n  options.CLIENT_ID_HEADER = common.CLIENT_ID_HEADER;\n  options.extractSaz = extractSaz;\n  options.generateSaz = generateSaz;\n  options.zipBody = getCustomBody;\n  if (options.isDev) {\n    var devLogFile = path.join(root, '.console.log');\n    var devLogOptions = { flag: 'a+' };\n    writeDevLog = function(log) {\n      fs.writeFile(devLogFile, log, devLogOptions, common.noop);\n    };\n    LEVELS.forEach(function (level) {\n      var originalFn = console[level]; // eslint-disable-line\n      if (originalFn) {\n        console[level] = function() { // eslint-disable-line\n          try {\n            writeDevLog(format.apply(null, arguments) + '\\n');\n          } catch (e) {}\n          return originalFn.apply(this, arguments);\n        };\n      }\n    });\n  }\n  options.Storage = Storage;\n  options.parseUrl = parseUrl;\n  options.formatHeaders = formatRawHeaders;\n  options.wsParser = wsParser;\n  pluginVersion = '@' + options.version;\n  var wrapWsReader = function (socket, maxPayload) {\n    wrapTunnelReader(socket, true, maxPayload);\n    return socket;\n  };\n  var wrapWsWriter = function (socket) {\n    wrapTunnelWriter(socket, true);\n    var timer = setInterval(function () {\n      socket.ping();\n    }, PING_INTERVAL);\n    var handleClose = function () {\n      if (timer) {\n        clearInterval(timer);\n        timer = null;\n      }\n    };\n    socket.once('error', handleClose);\n    socket.once('close', handleClose);\n    socket.stopPing = function () {\n      timer && clearInterval(timer);\n    };\n    socket.startPing = function () {\n      if (timer) {\n        socket.ping();\n        timer = setInterval(function () {\n          socket.ping();\n        }, PING_INTERVAL);\n      }\n    };\n    return socket;\n  };\n  options.wrapWsReader = wrapWsReader;\n  options.wrapWsWriter = wrapWsWriter;\n  options.require = rootRequire;\n  options.whistleRequire = rootRequire;\n  options.whistleRequirePath = rootRequire.resolve('./require');\n  debugMode = options.debugMode;\n  pluginName = options.name;\n  var config = options.config;\n  var boundIp = config.host;\n  var boundPort = config.port;\n  var PLUGIN_HOOKS = config.PLUGIN_HOOKS;\n  var RES_RULES_HEAD = config.RES_RULES_HEAD;\n  var SHOW_LOGIN_BOX = config.SHOW_LOGIN_BOX;\n  var setResRules = function(headers, obj) {\n    var rules = formatRules(obj);\n    if (!rules) {\n      return;\n    }\n    obj = JSON.stringify({\n      rules: rules,\n      values: obj.values,\n      root: root\n    });\n    headers[RES_RULES_HEAD] =  encodeURIComponent(obj);\n  };\n  CLIENT_INFO_HEADER = config.CLIENT_INFO_HEADER;\n  PROXY_ID_HEADER = config.PROXY_ID_HEADER;\n  options.getTempFilePath = function(filePath) {\n    if (common.TEMP_PATH_RE.test(filePath)) {\n      return path.join(config.TEMP_FILES_PATH, RegExp.$1);\n    }\n  };\n\n  delete config.PLUGIN_HOOKS;\n  delete config.PROXY_ID_HEADER;\n  delete config.CLIENT_INFO_HEADER;\n  delete config.RES_RULES_HEAD;\n  delete config.SHOW_LOGIN_BOX;\n\n  PLUGIN_HOOK_NAME_HEADER = config.PLUGIN_HOOK_NAME_HEADER;\n  options.shortName = pluginName.substring(pluginName.indexOf('/') + 1);\n  var sharedFilePath = path.join(config.baseDir, '.shared_storage', options.name + '.txt');\n  var pluginDataDir = (config.pluginBaseDir = path.join(\n    config.baseDir,\n    '.plugins',\n    options.name\n  ));\n  if (config.storage) {\n    pluginDataDir += encodeURIComponent('/' + config.storage);\n  }\n  config.pluginDataDir = pluginDataDir;\n  storage = new Storage(pluginDataDir);\n  options.storage = options.localStorage = storage;\n  sharedStorage = getSharedStorage(sharedFilePath);\n  options.sharedStorage = sharedStorage;\n  pluginOpts = options;\n  var authKey = config.authKey;\n  delete config.authKey;\n  var headers = {\n    'x-whistle-auth-key': authKey,\n    'content-type': 'application/json'\n  };\n  var baseUrl = 'http://' + common.joinIpPort(boundIp, boundPort);\n  options.baseUrl = baseUrl;\n  baseUrl += '/cgi-bin/';\n  sessionOpts = parseUrl(baseUrl + 'get-session?');\n  sessionOpts.headers = headers;\n  framesOpts = parseUrl(baseUrl + 'get-frames?');\n  framesOpts.headers = headers;\n  customParserOpts = parseUrl(baseUrl + 'custom-frames?');\n  customParserOpts.headers = headers;\n  customParserOpts.method = 'POST';\n  var normalizeArgs = function (\n    uri,\n    cb,\n    req,\n    curUrl,\n    isWs,\n    opts,\n    alpnProtocol\n  ) {\n    var type = uri && typeof uri;\n    var headers, method, body;\n    if (type !== 'string') {\n      if (type === 'object') {\n        if (uri.headers) {\n          headers = extend({}, uri.headers);\n        }\n        method = typeof uri.method === 'string' ? uri.method : null;\n        opts = opts || uri;\n        body = common.hasRequestBody(req) ? toBuffer(uri.body || uri.data) : null;\n        uri = uri.uri || uri.url || uri.href || curUrl;\n      } else {\n        if (type === 'function') {\n          opts = cb;\n          cb = uri;\n        } else if (cb && typeof cb !== 'function') {\n          opts = cb;\n          cb = null;\n        }\n        uri = curUrl;\n      }\n    }\n    uri = parseUrl(uri);\n    headers = headers || req.headers;\n    if (isWs) {\n      headers.upgrade = headers.upgrade || 'websocket';\n      headers.connection = 'Upgrade';\n      uri.method = 'GET';\n    } else {\n      uri.method = method || req.method;\n    }\n    var isHttps = uri.protocol === 'https:' || uri.protocol === 'wss:';\n    headers.host = uri.host;\n    uri.protocol = null;\n    if (!uri.rejectUnauthorized) {\n      uri.rejectUnauthorized = false;\n    }\n    if (!opts || !notEmptyStr(opts.host)) {\n      headers[PROXY_ID_HEADER] = 1;\n      uri.host = boundIp;\n      uri.port = boundPort;\n      addRemoteInfo(req, headers);\n      if (isHttps) {\n        headers[common.HTTPS_FIELD] = 1;\n        if (alpnProtocol) {\n          headers[common.ALPN_PROTOCOL_HEADER] = alpnProtocol;\n        }\n      }\n      isHttps = false;\n    } else {\n      uri.host = opts.host;\n      uri.port = opts.port > 0 ? opts.port : isHttps ? 443 : 80;\n      if (isHttps) {\n        if (opts.internalRequest) {\n          isHttps = false;\n          headers[common.HTTPS_FIELD] = 1;\n        } else {\n          uri.protocol = 'https:';\n        }\n      }\n    }\n    delete uri.hostname;\n    uri.headers = formatRawHeaders(headers, req);\n    uri.agent = false;\n    return [uri, cb, isHttps, opts, body];\n  };\n\n  var authPort,\n    sniPort,\n    port,\n    statsPort,\n    resStatsPort,\n    uiPort,\n    rulesPort,\n    resRulesPort,\n    tunnelRulesPort,\n    tunnelPort;\n  var reqWritePort, reqReadPort, resWritePort, resReadPort;\n  var wsReqWritePort, wsReqReadPort, wsResWritePort, wsResReadPort;\n  var tunnelReqWritePort,\n    tunnelReqReadPort,\n    tunnelResWritePort,\n    tunnelResReadPort;\n  var upgrade;\n  var callbackHandler = function () {\n    pluginInited = true;\n    callback(null, {\n      authPort,\n      sniPort,\n      port: port,\n      upgrade: upgrade,\n      statsPort: statsPort,\n      resStatsPort: resStatsPort,\n      uiPort: uiPort,\n      rulesPort: rulesPort,\n      resRulesPort: resRulesPort,\n      tunnelRulesPort: tunnelRulesPort,\n      tunnelPort: tunnelPort,\n      reqWritePort: reqWritePort,\n      reqReadPort: reqReadPort,\n      resWritePort: resWritePort,\n      resReadPort: resReadPort,\n      wsReqWritePort: wsReqWritePort,\n      wsReqReadPort: wsReqReadPort,\n      wsResWritePort: wsResWritePort,\n      wsResReadPort: wsResReadPort,\n      tunnelReqWritePort: tunnelReqWritePort,\n      tunnelReqReadPort: tunnelReqReadPort,\n      tunnelResWritePort: tunnelResWritePort,\n      tunnelResReadPort: tunnelResReadPort\n    });\n  };\n\n  try {\n    require.resolve(options.value);\n  } catch (e) {\n    return callbackHandler();\n  }\n  options.LRU = LRU;\n  var cgiHeaders = {};\n  cgiHeaders[PROXY_ID_HEADER] = options.shortName.substring(8);\n  cgiHeaders['x-whistle-auth-key'] = authKey;\n  var parseCgiUrl = function (url) {\n    var opts = parseUrl(url);\n    opts.headers = cgiHeaders;\n    return opts;\n  };\n  var topOpts = parseCgiUrl(baseUrl + 'top');\n  var rulesUrlOpts = parseCgiUrl(baseUrl + 'rules/list2');\n  var valuesUrlOpts = parseCgiUrl(baseUrl + 'values/list2');\n  var rulesListUrlOpts = parseCgiUrl(baseUrl + 'rules/list2?order=1');\n  var valuesListUrlOpts = parseCgiUrl(baseUrl + 'values/list2?order=1');\n  var pluginsOpts = parseCgiUrl(baseUrl + 'plugins/get-plugins?');\n  var composeOpts = parseCgiUrl(baseUrl + 'composer');\n  var certsInfoUrlOpts = parseCgiUrl(baseUrl + 'get-custom-certs-info');\n  var enableOpts = parseCgiUrl(baseUrl + 'plugins/is-enable');\n  var updateRulesOpts = parseCgiUrl(baseUrl + 'plugins/update-rules');\n  var httpsStatusOpts = parseCgiUrl(baseUrl + 'https-status');\n  composeOpts.method = 'POST';\n  composeOpts.headers['Content-Type'] = 'application/json';\n  var requestCgi = function (opts, cb) {\n    if (typeof cb !== 'function') {\n      return;\n    }\n    request(opts, function (err, body, res) {\n      if (body && res.statusCode == 200) {\n        try {\n          return cb(JSON.parse(body) || '', res);\n        } catch (e) {}\n      }\n      cb('', res, err);\n    });\n  };\n\n  var getValue = function (key, cb) {\n    if (typeof cb !== 'function') {\n      return;\n    }\n    if (!key || typeof key !== 'string') {\n      return cb();\n    }\n    var valueOpts = parseCgiUrl(\n      baseUrl + 'values/value?key=' + encodeURIComponent(key)\n    );\n    requestCgi(valueOpts, function (data) {\n      if (!data) {\n        return getValue(key, cb);\n      }\n      cb(data.value);\n    });\n  };\n\n  var getCert = function (domain, callback) {\n    if (!domain || typeof domain !== 'string') {\n      return callback('');\n    }\n    var index = domain.indexOf(':');\n    if (index !== -1) {\n      domain = domain.substring(0, index);\n      if (!domain) {\n        return callback('');\n      }\n    }\n    if (domain !== 'rootCA') {\n      domain = domain.toLowerCase();\n    }\n    var cert = certsCache.get(domain);\n    if (cert) {\n      return callback(cert);\n    }\n    var cbs = certsCallbacks[domain];\n    if (cbs) {\n      return cbs.push(callback);\n    }\n    cbs = [callback];\n    certsCallbacks[domain] = cbs;\n    var opts = parseCgiUrl(baseUrl + 'get-cert?domain=' + domain);\n    requestCgi(opts, function (cert) {\n      cert && certsCache.set(domain, cert);\n      delete certsCallbacks[domain];\n      cbs.forEach(function (cb) {\n        cb(cert);\n      });\n    });\n  };\n\n  options.getValue = getValue;\n  options.getCert = getCert;\n  options.getRootCA = function (callback) {\n    return getCert('rootCA', callback);\n  };\n  options.getHttpsStatus = function (callback) {\n    requestCgi(httpsStatusOpts, callback);\n  };\n  options.getTop =\n    options.getRuntimeInfo =\n    options.getProcessData =\n    options.getPerfData =\n      function (callback) {\n        requestCgi(topOpts, callback);\n      };\n\n  var waitingUpdateRules;\n  var updateTimer;\n  var updateRules = function () {\n    requestCgi(updateRulesOpts, function(data) {\n      if (!data) {\n        if (updateTimer) {\n          clearTimeout(updateTimer);\n          updateTimer = null;\n          waitingUpdateRules = false;\n        }\n        requestCgi(updateRulesOpts, noop);\n      }\n    });\n    if (waitingUpdateRules) {\n      waitingUpdateRules = false;\n      updateTimer = setTimeout(updateRules, 300);\n    } else {\n      updateTimer = null;\n    }\n  };\n  options.updateRules = function () {\n    if (updateTimer) {\n      waitingUpdateRules = true;\n    } else {\n      updateTimer = setTimeout(updateRules, 600);\n    }\n  };\n  options.composer = options.compose = function (data, cb) {\n    var needResponse = typeof cb === 'function';\n    if (!data) {\n      return needResponse ? cb() : '';\n    }\n    data.needResponse = needResponse;\n    if (data.headers && typeof data.headers !== 'string') {\n      data.headers = JSON.stringify(data.headers);\n    }\n    if (data.base64) {\n      delete data.body;\n      if (Buffer.isBuffer(data.base64)) {\n        data.base64 = data.base64.toString('base64');\n      }\n    } else if (Buffer.isBuffer(data.body)) {\n      data.base64 = data.body.toString('base64');\n      delete data.body;\n    }\n    composeOpts.body = data;\n    request(composeOpts, function (err, body, res) {\n      if (!needResponse) {\n        return;\n      }\n      if (!err && res.statusCode == 200) {\n        try {\n          return cb(err, JSON.parse(body));\n        } catch (e) {}\n      }\n      cb(err || new Error(res.statusCode || 'unknown'));\n    });\n  };\n\n  function getData(cb, isList, opt1, opt2) {\n    if (typeof cb !== 'function') {\n      var temp = cb;\n      cb = isList;\n      isList = temp;\n    }\n    requestCgi(isList === true ? opt1 : opt2, cb);\n  }\n\n  options.getRules = function (cb, isList) {\n    getData(cb, isList, rulesListUrlOpts, rulesUrlOpts);\n  };\n  options.getValues = function (cb, isList) {\n    getData(cb, isList, valuesListUrlOpts, valuesUrlOpts);\n  };\n  options.getPlugins = function(cb) {\n    requestCgi(pluginsOpts, cb);\n  };\n  var certsInfo;\n  options.getCustomCertsInfo = function (cb) {\n    if (typeof cb !== 'function') {\n      return;\n    }\n    if (certsInfo) {\n      return cb(certsInfo);\n    }\n    requestCgi(certsInfoUrlOpts, function (data) {\n      certsInfo = certsInfo || data;\n      cb(certsInfo);\n    });\n  };\n  var enableCallbacks = [];\n  options.isEnable = options.isActive = function (cb) {\n    if (typeof cb !== 'function') {\n      return;\n    }\n    enableCallbacks.push(cb);\n    if (enableCallbacks.length > 1) {\n      return;\n    }\n    var checkEnable = function () {\n      requestCgi(enableOpts, function (data, r) {\n        if (!data) {\n          return setTimeout(checkEnable, 600);\n        }\n        enableCallbacks.forEach(function (callback) {\n          callback(data.enable);\n        });\n        enableCallbacks = [];\n      });\n    };\n    checkEnable();\n  };\n  var initProxy = function(cb) {\n    getProxy({\n      PROXY_ID_HEADER: PROXY_ID_HEADER,\n      proxyIp: boundIp,\n      proxyPort: boundPort,\n      pluginName: pluginName,\n      wrapWsReader: wrapWsReader,\n      wrapWsWriter: wrapWsWriter\n    }, cb);\n  };\n\n  var initServers = function (_ctx) {\n    ctx = _ctx || ctx;\n    var execPlugin = require(options.value) || '';\n    var execAuth =\n      getFunction(execPlugin.auth) || getFunction(execPlugin.verify);\n    var sniCallback =\n      getFunction(execPlugin.sniCallback) ||\n      getFunction(execPlugin.SNICallback);\n    var startServer = getFunction(\n      execPlugin.pluginServer || execPlugin.server || execPlugin\n    );\n    var startStatsServer = getFunction(\n      execPlugin.statServer ||\n        execPlugin.statsServer ||\n        execPlugin.reqStatServer ||\n        execPlugin.reqStatsServer\n    );\n    var startResStatsServer = getFunction(\n      execPlugin.resStatServer || execPlugin.resStatsServer\n    );\n    var startUIServer = getFunction(\n      execPlugin.uiServer || execPlugin.innerServer || execPlugin.internalServer\n    );\n    var startRulesServer = getFunction(\n      execPlugin.pluginRulesServer ||\n        execPlugin.rulesServer ||\n        execPlugin.reqRulesServer\n    );\n    var startResRulesServer = getFunction(execPlugin.resRulesServer);\n    var startTunnelRulesServer = getFunction(\n      execPlugin.pluginRulesServer || execPlugin.tunnelRulesServer\n    );\n    var startTunnelServer =\n      getFunction(\n        execPlugin.pluginServer ||\n          execPlugin.tunnelServer ||\n          execPlugin.connectServer\n      ) || startServer;\n    var startReqRead = getFunction(\n      execPlugin.reqRead || execPlugin.reqReadServer\n    );\n    var startReqWrite = getFunction(\n      execPlugin.reqWrite || execPlugin.reqWriteServer\n    );\n    var startResRead = getFunction(\n      execPlugin.resRead || execPlugin.resReadServer\n    );\n    var startResWrite = getFunction(\n      execPlugin.resWrite || execPlugin.resWriteServer\n    );\n    var startWsReqRead = getFunction(\n      execPlugin.wsReqRead || execPlugin.wsReqReadServer\n    );\n    var startWsReqWrite = getFunction(\n      execPlugin.wsReqWrite || execPlugin.wsReqWriteServer\n    );\n    var startWsResRead = getFunction(\n      execPlugin.wsResRead || execPlugin.wsResReadServer\n    );\n    var startWsResWrite = getFunction(\n      execPlugin.wsResWrite || execPlugin.wsResWriteServer\n    );\n    var startTunnelReqRead = getFunction(\n      execPlugin.tunnelReqRead || execPlugin.tunnelReqReadServer\n    );\n    var startTunnelReqWrite = getFunction(\n      execPlugin.tunnelReqWrite || execPlugin.tunnelReqWriteServer\n    );\n    var startTunnelResRead = getFunction(\n      execPlugin.tunnelResRead || execPlugin.tunnelResReadServer\n    );\n    var startTunnelResWrite = getFunction(\n      execPlugin.tunnelResWrite || execPlugin.tunnelResWriteServer\n    );\n    var staticDir = !startUIServer && options.staticDir;\n    if (staticDir) {\n      var app = express();\n      startUIServer = function (server) {\n        app.use(\n          express.static(path.join(options.value, staticDir), {\n            maxAge: 60000\n          })\n        );\n        server.on('request', app);\n      };\n    }\n    var hasServer =\n      execAuth ||\n      sniCallback ||\n      startServer ||\n      startStatsServer ||\n      startResStatsServer ||\n      startUIServer ||\n      startRulesServer ||\n      startResRulesServer ||\n      startTunnelRulesServer ||\n      startTunnelServer ||\n      startReqRead ||\n      startReqWrite ||\n      startResRead ||\n      startResWrite ||\n      startWsReqRead ||\n      startWsReqWrite ||\n      startWsResRead ||\n      startWsResWrite ||\n      startTunnelReqRead ||\n      startTunnelReqWrite ||\n      startTunnelResRead ||\n      startTunnelResWrite;\n\n    if (!hasServer) {\n      return callbackHandler();\n    }\n    getServer(async function (server, _port) {\n      var maxWsPayload;\n      var authServer,\n        sniServer,\n        uiServer,\n        httpServer,\n        statsServer,\n        resStatsServer;\n      var rulesServer, resRulesServer, tunnelRulesServer, tunnelServer;\n      var reqRead, reqWrite, resWrite, resRead;\n      var wsReqRead, wsReqWrite, wsResWrite, wsResRead;\n      var tunnelReqRead, tunnelReqWrite, tunnelResWrite, tunnelResRead;\n      var setMaxWsPayload = function (payload) {\n        maxWsPayload = parseInt(payload, 10) || 0;\n      };\n      options.ctx = ctx;\n      if (startUIServer) {\n        uiServer = createServer();\n        await startUIServer(uiServer, options);\n        uiPort = _port;\n      }\n\n      var transferError = function (req, res) {\n        res.once('error', function (err) {\n          req.emit('error', err);\n        });\n        res.once('close', function (err) {\n          req.emit('close', err);\n        });\n      };\n\n      if (execAuth) {\n        authServer = createServer();\n        authServer.on('request', async function (req, res) {\n          initReq(req, res);\n          transferError(req, res);\n          var customHeaders = {};\n          var htmlBody, htmlUrl, location;\n          var setHeader = function (key, value) {\n            if (\n                !notEmptyStr(key) ||\n                NOT_NAME_RE.test(key) ||\n                typeof value !== 'string'\n              ) {\n              return;\n            }\n            key = key.toLowerCase();\n            if (\n                key.indexOf('x-whistle-') === 0 ||\n                key === 'proxy-authorization'\n              ) {\n              customHeaders[key] = value;\n            }\n          };\n          req.setHtml = function (html) {\n            if (!html || html == null) {\n              htmlBody = null;\n            } else if (typeof html === 'string' || Buffer.isBuffer(html)) {\n              htmlBody = html;\n              location = null;\n              htmlUrl = null;\n            }\n          };\n          req.setUrl = req.setFile = function (url) {\n            if (!url || /\\s/.test(url)) {\n              htmlUrl = null;\n            } else {\n              htmlUrl = url;\n              location = null;\n              htmlBody = null;\n              req.showLoginBox = false;\n            }\n          };\n          req.set = req.setHeader = setHeader;\n          req.setRedirect = function (url) {\n            if (!url) {\n              location = null;\n            } else if (common.isUrl(url)) {\n              location = url;\n              htmlBody = null;\n              htmlUrl = null;\n              req.showLoginBox = false;\n            }\n          };\n          req.setLogin = function (login) {\n            req.showLoginBox = login !== false;\n            if (req.showLoginBox) {\n              location = null;\n              htmlUrl = null;\n            }\n          };\n          try {\n            var result = await execAuth(req, options);\n            if (req.showLoginBox) {\n              customHeaders[SHOW_LOGIN_BOX] = '1';\n            }\n            if (result === false) {\n              customHeaders[common.AUTH_STATUS] = '1';\n              if (location) {\n                customHeaders.location = location;\n              } else if (htmlUrl) {\n                customHeaders[common.AUTH_URL] = encodeURIComponent(htmlUrl);\n              } else if (typeof htmlBody === 'string') {\n                htmlBody = Buffer.from(htmlBody);\n                if (htmlBody.length > MAX_BODY_SIZE) {\n                  htmlBody = htmlBody.slice(0, MAX_BODY_SIZE);\n                }\n              }\n              htmlBody = htmlBody || 'Forbidden';\n            } else {\n              htmlBody = null;\n            }\n            res.writeHead(200, customHeaders);\n            res.end(htmlBody);\n          } catch (e) {\n            res.end(\n                e && e.message && typeof e.message === 'string'\n                  ? e.message\n                  : 'Error'\n              );\n          }\n          htmlBody = null;\n        });\n        authPort = _port;\n      }\n\n      if (sniCallback) {\n        sniServer = createServer();\n        sniServer.on('request', async function (req, res) {\n          initReq(req, res);\n          transferError(req, res);\n          try {\n            var result = await sniCallback(req, options);\n            if (result === false || result === true) {\n              res.end(result + '');\n            } else if (\n                result &&\n                notEmptyStr(result.key) &&\n                notEmptyStr(result.cert)\n              ) {\n              res.end(\n                  JSON.stringify({\n                    key: result.key,\n                    cert: result.cert,\n                    mtime: result.mtime > 0 ? result.mtime : undefined,\n                    name: pluginName\n                  })\n                );\n            } else {\n              res.end();\n            }\n          } catch (e) {\n            res.end(\n                e && e.message && typeof e.message === 'string'\n                  ? e.message\n                  : 'Error'\n              );\n          }\n        });\n        sniPort = _port;\n      }\n\n      if (startServer) {\n        httpServer = createServer();\n        await startServer(httpServer, options);\n        upgrade = listenerCount(httpServer, 'upgrade');\n        port = _port;\n      }\n\n      if (startStatsServer) {\n        statsServer = createServer();\n        await startStatsServer(statsServer, options);\n        statsPort = _port;\n      }\n\n      if (startResStatsServer) {\n        resStatsServer = createServer();\n        await startResStatsServer(resStatsServer, options);\n        resStatsPort = _port;\n      }\n\n      if (startRulesServer) {\n        rulesServer = createServer();\n        await startRulesServer(rulesServer, options);\n        rulesPort = _port;\n      }\n\n      if (startResRulesServer) {\n        resRulesServer = createServer();\n        await startResRulesServer(resRulesServer, options);\n        resRulesPort = _port;\n      }\n\n      if (startTunnelRulesServer) {\n        if (startTunnelRulesServer === startRulesServer) {\n          tunnelRulesServer = rulesServer;\n        } else {\n          tunnelRulesServer = createServer();\n          await startTunnelRulesServer(tunnelRulesServer, options);\n        }\n        tunnelRulesPort = _port;\n      }\n\n      if (startTunnelServer) {\n        if (startTunnelServer === startServer) {\n          if (listenerCount(httpServer, 'connect')) {\n            tunnelServer = httpServer;\n            tunnelPort = _port;\n          }\n        } else {\n          tunnelServer = createServer();\n          await startTunnelServer(tunnelServer, options);\n          tunnelPort = _port;\n        }\n      }\n\n      if (startReqRead) {\n        reqRead = createServer();\n        await startReqRead(reqRead, options);\n        reqReadPort = _port;\n      }\n\n      if (startReqWrite) {\n        reqWrite = createServer();\n        await startReqWrite(reqWrite, options);\n        reqWritePort = _port;\n      }\n\n      if (startResRead) {\n        resRead = createServer();\n        await startResRead(resRead, options);\n        resReadPort = _port;\n      }\n\n      if (startResWrite) {\n        resWrite = createServer();\n        await startResWrite(resWrite, options);\n        resWritePort = _port;\n      }\n\n      if (startWsReqRead) {\n        wsReqRead = createServer();\n        wsReqRead.setMaxWsPayload = setMaxWsPayload;\n        await startWsReqRead(wsReqRead, options);\n        wsReqReadPort = _port;\n      }\n\n      if (startWsReqWrite) {\n        wsReqWrite = createServer();\n        wsReqWrite.setMaxWsPayload = setMaxWsPayload;\n        await startWsReqWrite(wsReqWrite, options);\n        wsReqWritePort = _port;\n      }\n\n      if (startWsResRead) {\n        wsResRead = createServer();\n        wsResRead.setMaxWsPayload = setMaxWsPayload;\n        await startWsResRead(wsResRead, options);\n        wsResReadPort = _port;\n      }\n\n      if (startWsResWrite) {\n        wsResWrite = createServer();\n        wsResWrite.setMaxWsPayload = setMaxWsPayload;\n        await startWsResWrite(wsResWrite, options);\n        wsResWritePort = _port;\n      }\n\n      if (startTunnelReqRead) {\n        tunnelReqRead = createServer();\n        await startTunnelReqRead(tunnelReqRead, options);\n        tunnelReqReadPort = _port;\n      }\n\n      if (startTunnelReqWrite) {\n        tunnelReqWrite = createServer();\n        await startTunnelReqWrite(tunnelReqWrite, options);\n        tunnelReqWritePort = _port;\n      }\n\n      if (startTunnelResRead) {\n        tunnelResRead = createServer();\n        await startTunnelResRead(tunnelResRead, options);\n        tunnelResReadPort = _port;\n      }\n\n      if (startTunnelResWrite) {\n        tunnelResWrite = createServer();\n        await startTunnelResWrite(tunnelResWrite, options);\n        tunnelResWritePort = _port;\n      }\n      server.on('request', function (req, res) {\n        delete req.headers['x-whistle-internal-id'];\n        switch (getHookName(req)) {\n        case PLUGIN_HOOKS.AUTH:\n          if (authServer) {\n            authServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.SNI:\n          if (sniServer) {\n            sniServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.UI:\n          if (uiServer) {\n            setContext(req);\n            uiServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.HTTP:\n          if (httpServer) {\n            initReq(req, res, true);\n            var alpnProtocol = req.headers[common.ALPN_PROTOCOL_HEADER];\n            var curUrl = req.originalReq.realUrl;\n            var svrRes = '';\n            var reqRules, resRules;\n            var writeHead = res.writeHead;\n            req.setRules = req.setReqRules = res.setReqRules = function (rules) {\n              reqRules = rules;\n              return true;\n            };\n            res.setRules = req.setResRules = res.setResRules = function (rules) {\n              resRules = rules;\n              return true;\n            };\n            req.writeHead = res.writeHead = function (code, msg, headers) {\n              code = code > 0 ? code : svrRes.statusCode || 101;\n              if (msg && typeof msg !== 'string') {\n                headers = headers || msg;\n                msg = null;\n              }\n              msg = msg || STATUS_CODES[code] || '';\n              headers = headers || svrRes.headers;\n              if (resRules) {\n                headers = headers || {};\n                setResRules(headers, resRules);\n                res.setRules = req.setResRules = res.setResRules = noop;\n                resRules = null;\n              }\n              headers = formatRawHeaders(headers, svrRes);\n              writeHead.call(res, code, msg, headers);\n              res.emit('_wroteHead');\n            };\n            req.request = function (uri, cb, opts) {\n              var args = normalizeArgs(\n                    uri,\n                    cb,\n                    req,\n                    curUrl,\n                    false,\n                    opts,\n                    alpnProtocol\n                  );\n              uri = args[0];\n              cb = args[1];\n              opts = args[3];\n              var reqBuff = args[4];\n              req.setRules = req.setReqRules = res.setReqRules = noop;\n              setReqRules(uri, getReqRules(opts, reqRules));\n              reqRules = null;\n              if (reqBuff && (!uri.headers || uri.headers['content-length'] != null)) {\n                uri.headers = uri.headers || {};\n                uri.headers['content-length'] = reqBuff.length;\n              }\n              var request = args[2] ? httpsRequest : httpRequest;\n              var client = request(uri, function (_res) {\n                svrRes = _res;\n                res.once('_wroteHead', function () {\n                  appendTrailers(_res, res, opts && opts.trailers, req);\n                });\n                if (typeof cb === 'function') {\n                  cb.call(this, _res);\n                } else {\n                  res.writeHead();\n                  _res.pipe(res);\n                }\n              }\n                  );\n              addErrorHandler(req, client);\n              reqBuff && client.end(reqBuff);\n              return client;\n            };\n            req.passThrough = function (uri, newTrailers) {\n              var handleReq;\n              var handleRes;\n              var transformReq;\n              var transformRes;\n              var reqBuf;\n              var resBuf;\n              if (typeof uri === 'function') {\n                handleReq = uri;\n                uri = null;\n              } else if (uri && typeof uri.transformReq === 'function') {\n                transformReq = uri.transformReq;\n              }\n              if (typeof newTrailers === 'function') {\n                handleRes = newTrailers;\n                newTrailers = null;\n              } else if (uri && typeof uri.transformRes === 'function') {\n                transformRes = uri.transformRes;\n              }\n              var _request = function(reqBody, _uri) {\n                var client = req.request(_uri || uri, function (_res) {\n                  var _response = function(resBody, trailers) {\n                    res.writeHead(_res.statusCode,  _res.statusMessage, _res.headers);\n                    appendTrailers(_res, res, trailers || newTrailers, req);\n                    if (resBody === undefined) {\n                      _res.pipe(res);\n                    } else if (resBody && typeof resBody.pipe === 'function') {\n                      resBody.pipe(res);\n                    } else {\n                      res.end(resBody || '');\n                    }\n                  };\n\n                  var handleNextRes = function(result) {\n                    if (result) {\n                      req.setResRules(result.rules);\n                      getCustomBody(isBodyData(result) ? result : result.body, _res, function(body) {\n                        _response(body, result.trailers);\n                      });\n                    } else {\n                      _response(result === undefined && (resBuf || _res._readRawBuffer_));\n                    }\n                  };\n                  if (transformRes) {\n                    transformRes(common.wrapReadStream(_res), handleNextRes);\n                  } else if (handleRes) {\n                    readStream(_res, function(rawBuffer) {\n                      resBuf = rawBuffer;\n                      handleRes(rawBuffer, handleNextRes, _res);\n                    });\n                  } else {\n                    _response();\n                  }\n                });\n                if (reqBody === undefined) {\n                  req.pipe(client);\n                } else if (reqBody && typeof reqBody.pipe === 'function') {\n                  reqBody.pipe(client);\n                } else {\n                  client.end(reqBody || '');\n                }\n              };\n              var handleNextReq = function(result) {\n                if (result) {\n                  req.setReqRules(result.rules);\n                  getCustomBody(isBodyData(result) ? result : result.body, req, function(body) {\n                    _request(body, result.uri || result.url);\n                  });\n                } else {\n                  _request(result === undefined && (reqBuf || req._readRawBuffer_));\n                }\n              };\n              if (transformReq) {\n                transformReq(common.wrapReadStream(req), handleNextReq);\n              } else if (handleReq) {\n                readStream(req, function(rawBuffer) {\n                  reqBuf = rawBuffer;\n                  handleReq(rawBuffer, handleNextReq, req);\n                });\n              } else {\n                _request();\n              }\n            };\n            httpServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.REQ_STATS:\n          if (statsServer) {\n            initReq(req, res);\n            statsServer.emit('request', req, res);\n            res.end();\n          }\n          break;\n        case PLUGIN_HOOKS.RES_STATS:\n          if (resStatsServer) {\n            initReq(req, res);\n            resStatsServer.emit('request', req, res);\n            res.end();\n          }\n          break;\n        case PLUGIN_HOOKS.REQ_RULES:\n          if (rulesServer) {\n            initReq(req, res);\n            rulesServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.RES_RULES:\n          if (resRulesServer) {\n            initReq(req, res);\n            resRulesServer.emit('request', req, res);\n          }\n          break;\n        case PLUGIN_HOOKS.TUNNEL_RULES:\n          if (tunnelRulesServer) {\n            initReq(req, res);\n            tunnelRulesServer.emit('request', req, res);\n          }\n          break;\n        default:\n          res.destroy();\n        }\n      });\n      server.on('upgrade', function (req, socket, head) {\n        switch (getHookName(req)) {\n        case PLUGIN_HOOKS.UI:\n          if (uiServer) {\n            setContext(req);\n            uiServer.emit('upgrade', req, socket, head);\n          }\n          break;\n        case PLUGIN_HOOKS.HTTP:\n          if (httpServer) {\n            initWsReq(req, socket);\n            var curUrl = req.originalReq.realUrl;\n            httpServer.setMaxWsPayload = setMaxWsPayload;\n            var svrRes = '';\n            var reqRules, resRules;\n            req.setRules = req.setReqRules = socket.setReqRules = function (rules) {\n              reqRules = rules;\n              return true;\n            };\n            socket.setRules = req.setResRules = socket.setResRules = function (rules) {\n              resRules = rules;\n              return true;\n            };\n            req.writeHead = socket.writeHead = function (code, msg, headers) {\n              code = code > 0 ? code : svrRes.statusCode || 101;\n              if (msg && typeof msg !== 'string') {\n                headers = headers || msg;\n                msg = null;\n              }\n              headers = headers || svrRes.headers;\n              if (resRules) {\n                headers = headers || {};\n                setResRules(headers, resRules);\n                socket.setRules = req.setResRules = socket.setResRules = noop;\n                resRules = null;\n              }\n              var rawData = 'HTTP/1.1 ' + code + ' ' + (msg || svrRes.statusMessage || STATUS_CODES[code] || '');\n              if (headers = headers && getRawHeaders(formatRawHeaders(headers, svrRes))) {\n                rawData += '\\r\\n' + headers;\n              }\n              socket.write(rawData + '\\r\\n\\r\\n', 'binary');\n            };\n            req.request = function (uri, cb, opts) {\n              var args = normalizeArgs(uri, cb, req, curUrl, true, opts);\n              uri = args[0];\n              cb = args[1];\n              opts = args[3];\n              req.setRules = req.setReqRules = socket.setReqRules = noop;\n              setReqRules(uri, getReqRules(opts, reqRules));\n              reqRules = null;\n              var client = (args[2] ? httpsRequest : httpRequest)(uri);\n              var rawFrame;\n              if (opts === true) {\n                rawFrame = true;\n              } else if (opts) {\n                rawFrame = opts.rawFrame;\n              }\n              client.once('response', function(_res) {\n                svrRes = _res;\n                socket.writeHead();\n                _res.pipe(socket);\n              });\n              client.once('upgrade', function (_res, resSocket) {\n                svrRes = _res;\n                if (rawFrame !== true && common.isWebSocket(svrRes.headers)) {\n                  var clientParser = addFrameHandler(\n                        _res,\n                        socket,\n                        maxWsPayload,\n                        true,\n                        false\n                      );\n                  var serverParser = addFrameHandler(\n                        _res,\n                        resSocket,\n                        maxWsPayload,\n                        false,\n                        true\n                      );\n                  handleWsSignal(\n                        clientParser.receiver,\n                        serverParser.sender\n                      );\n                  handleWsSignal(\n                        serverParser.receiver,\n                        clientParser.sender\n                      );\n                }\n                if (typeof cb === 'function') {\n                  resSocket.headers = _res.headers;\n                  resSocket.statusCode = _res.statusCode;\n                  resSocket.statusMessage = _res.statusMessage;\n                  cb(resSocket);\n                } else {\n                  socket.writeHead();\n                  socket.pipe(resSocket).pipe(socket);\n                }\n              });\n              addErrorHandler(req, client);\n              client.end();\n              return client;\n            };\n            req.passThrough = function (uri) {\n              req.request(\n                    uri,\n                    function (resSock) {\n                      socket.writeHead(\n                        resSock.statusCode,\n                        resSock.statusMessage,\n                        resSock.headers\n                      );\n                      resSock.pipe(socket).pipe(resSock);\n                    },\n                    true\n                  );\n            };\n            httpServer.emit('upgrade', req, socket, head);\n          }\n          break;\n        default:\n          socket.destroy();\n        }\n      });\n\n      var emitHttpPipe = function (httpServer, req, socket) {\n        if (httpServer) {\n          initConnectReq(req, socket);\n          req.sendEstablished(function () {\n            var decoder = getDecodeTransform();\n            var encoder = getEncodeTransform();\n            var done;\n            var handleError = function (err) {\n              if (!done) {\n                done = true;\n                socket.emit('error', err || new Error('destroyed'));\n              }\n            };\n            socket.on('error', function (err) {\n              if (!done) {\n                done = true;\n                decoder.emit('error', err);\n                encoder.emit('error', err);\n              }\n            });\n            encoder.once('finish', function () {\n              done = true;\n            });\n            socket.on('close', function () {\n              done = true;\n              decoder.emit('close');\n              encoder.emit('close');\n            });\n            decoder.destroy = encoder.destroy = handleError;\n            decoder.on('error', handleError);\n            encoder.on('error', handleError);\n            ADDITIONAL_FIELDS.forEach(function (key) {\n              decoder[key] = req[key];\n            });\n            socket.pipe(decoder);\n            encoder.pipe(socket);\n            socket.on('end', destroySocket);\n            httpServer.emit('request', decoder, encoder);\n          });\n        }\n      };\n\n      function pipeUpgrade(pipeSvr, req, socket, head, fromClient, toServer) {\n        if (pipeSvr) {\n          initConnectReq(req, socket);\n          req.sendEstablished(function () {\n            addFrameHandler(req, socket, maxWsPayload, fromClient, toServer);\n            pipeSvr.emit('connect', req, socket, head);\n          });\n        }\n      }\n\n      server.on('connect', function (req, socket, head) {\n        var pipeServer;\n        switch (getHookName(req)) {\n        case PLUGIN_HOOKS.REQ_READ:\n          emitHttpPipe(reqRead, req, socket);\n          break;\n        case PLUGIN_HOOKS.REQ_WRITE:\n          emitHttpPipe(reqWrite, req, socket);\n          break;\n        case PLUGIN_HOOKS.RES_READ:\n          emitHttpPipe(resRead, req, socket);\n          break;\n        case PLUGIN_HOOKS.RES_WRITE:\n          emitHttpPipe(resWrite, req, socket);\n          break;\n        case PLUGIN_HOOKS.WS_REQ_READ:\n          pipeUpgrade(wsReqRead, req, socket, head, true, true);\n          break;\n        case PLUGIN_HOOKS.WS_REQ_WRITE:\n          pipeUpgrade(wsReqWrite, req, socket, head, true, true);\n          break;\n        case PLUGIN_HOOKS.WS_RES_READ:\n          pipeUpgrade(wsResRead, req, socket, head);\n          break;\n        case PLUGIN_HOOKS.WS_RES_WRITE:\n          pipeUpgrade(wsResWrite, req, socket, head);\n          break;\n        case PLUGIN_HOOKS.TUNNEL:\n          if (tunnelServer) {\n            initConnectReq(req, socket);\n            req.sendEstablished(function () {\n              var reqRules;\n              req.setRules = req.setReqRules = socket.setReqRules = function (rules) {\n                reqRules = rules;\n                return true;\n              };\n              socket.setRules = req.setResRules = socket.setResRules = noop;\n              req.writeHead = socket.writeHead = noop;\n              req.connect = function (uri, cb, opts) {\n                var headers = req.headers;\n                var policy = headers['x-whistle-policy'];\n                if (common.isTunnelHost(uri)) {\n                  headers.host = uri;\n                } else if (typeof uri === 'function') {\n                  opts = cb;\n                  cb = uri;\n                } else if (uri) {\n                  if (common.isTunnelHost(uri.url)) {\n                    if (uri.headers) {\n                      headers = extend({}, uri.headers);\n                      if (policy && !headers['x-whistle-policy']) {\n                        headers['x-whistle-policy'] = policy;\n                      }\n                    }\n                    headers.host = uri.url;\n                    if (cb && typeof cb !== 'function') {\n                      opts = cb;\n                      cb = null;\n                    }\n                  }\n                  opts = opts || uri;\n                }\n                uri = {\n                  method: 'CONNECT',\n                  agent: false,\n                  path: headers.host,\n                  headers: headers\n                };\n                req.setRules = req.setReqRules = socket.setReqRules = noop;\n                setReqRules(uri, getReqRules(opts, reqRules));\n                reqRules = null;\n                if (!opts || !notEmptyStr(opts.host)) {\n                  headers[PROXY_ID_HEADER] = req[REQ_ID_KEY];\n                  uri.host = boundIp;\n                  uri.port = boundPort;\n                  addRemoteInfo(req, headers);\n                } else {\n                  uri.host = opts.host;\n                  uri.port = opts.port > 0 ? opts.port : 80;\n                }\n                uri.headers = formatRawHeaders(headers, req);\n                var client = httpRequest(uri);\n                client.on('connect', function (_res, svrSock) {\n                  if (_res.statusCode !== 200) {\n                    var err = new Error(\n                          'Tunneling socket could not be established, statusCode=' +\n                            _res.statusCode\n                        );\n                    err.statusCode = _res.statusCode;\n                    svrSock.destroy();\n                    process.nextTick(function () {\n                      client.emit('error', err);\n                    });\n                    return;\n                  }\n                  if (_res.headers[common.ALLOW_ACK]) {\n                    svrSock.write('1');\n                  }\n                  if (typeof cb === 'function') {\n                    svrSock.headers = _res.headers;\n                    svrSock.statusCode = 200;\n                    cb(svrSock);\n                  } else {\n                    svrSock.pipe(socket).pipe(svrSock);\n                  }\n                });\n                addErrorHandler(req, client);\n                client.end();\n                return client;\n              };\n              req.passThrough = function (uri) {\n                req.connect(uri, function (svrSock) {\n                  svrSock.pipe(socket).pipe(svrSock);\n                });\n              };\n              tunnelServer.emit('connect', req, socket, head);\n            });\n          }\n          break;\n        case PLUGIN_HOOKS.TUNNEL_REQ_READ:\n          initConnectReq(req, socket);\n          pipeServer = req._isUpgrade ? wsReqRead : tunnelReqRead;\n          if (pipeServer) {\n            req.sendEstablished(function () {\n              socket.headers = req.headers;\n              if (req._isUpgrade ? wsReqWrite : tunnelReqWrite) {\n                wrapTunnelWriter(socket);\n              }\n              pipeServer.emit('connect', req, socket, head);\n            });\n          }\n          break;\n        case PLUGIN_HOOKS.TUNNEL_REQ_WRITE:\n          initConnectReq(req, socket);\n          pipeServer = req._isUpgrade ? wsReqWrite : tunnelReqWrite;\n          if (pipeServer) {\n            req.sendEstablished(function () {\n              if (req._isUpgrade ? wsReqRead : tunnelReqRead) {\n                wrapTunnelReader(socket);\n              }\n              pipeServer.emit('connect', req, socket, head);\n            });\n          }\n          break;\n        case PLUGIN_HOOKS.TUNNEL_RES_READ:\n          initConnectReq(req, socket);\n          pipeServer = req._isUpgrade ? wsResRead : tunnelResRead;\n          if (pipeServer) {\n            req.sendEstablished(function () {\n              socket.headers = req.headers;\n              if (req._isUpgrade ? wsResWrite : tunnelResWrite) {\n                wrapTunnelWriter(socket);\n              }\n              pipeServer.emit('connect', req, socket, head);\n            });\n          }\n          break;\n        case PLUGIN_HOOKS.TUNNEL_RES_WRITE:\n          initConnectReq(req, socket);\n          pipeServer = req._isUpgrade ? wsResWrite : tunnelResWrite;\n          if (pipeServer) {\n            req.sendEstablished(function () {\n              if (req._isUpgrade ? wsResRead : tunnelResRead) {\n                wrapTunnelReader(socket);\n              }\n              pipeServer.emit('connect', req, socket, head);\n            });\n          }\n          break;\n        default:\n          socket.destroy();\n        }\n      });\n      callbackHandler();\n    }\n    );\n  };\n  initProxy(async function(proxy) {\n    options.connect = proxy.connect;\n    options.request = proxy.request;\n    options.streamUtils = {\n      readRawBuffer: readStream,\n      readBuffer: function(stream, cb) {\n        readStream(stream, function() {\n          stream.getBuffer(function(err, buffer) {\n            if (err) {\n              return stream.emit('error', err);\n            }\n            cb(buffer);\n          });\n        });\n      },\n      readText: function(stream, cb) {\n        readStream(stream, function() {\n          stream.getText(function(err, buffer) {\n            if (err) {\n              return stream.emit('error', err);\n            }\n            cb(buffer);\n          });\n        });\n      },\n      readJson: function(stream, cb) {\n        readStream(stream, function() {\n          stream.getJson(function(err, data) {\n            if (err) {\n              return stream.emit('error', err);\n            }\n            cb(data);\n          });\n        });\n      }\n    };\n    options.readStream = readStream;\n    options.requestInternal = function(options, cb) {\n      if (typeof options === 'string') {\n        options = { url: options };\n      } else if (options.headers) {\n        options = extend({}, options);\n        options.headers = extend({}, options.headers);\n      }\n      options.pluginName = null;\n      return request(setInternalOptions(options, {\n        PROXY_ID_HEADER: PROXY_ID_HEADER,\n        host: config.host,\n        port: config.port\n      }), cb);\n    };\n    compat.compatKeys(options);\n    compat.compatKeys(config);\n    var initial = loadModule(path.join(options.value, 'initial.js')) ||\n    loadModule(path.join(options.value, 'initialize.js'));\n    if (initial && typeof initial !== 'function') {\n      initial = initial.default;\n    }\n    if (typeof initial === 'function') {\n      if (initial.length === 2) {\n        ctx = await initial(options, initServers);\n        return ctx;\n      }\n      ctx = await initial(options);\n    }\n    initServers();\n  });\n};\n"
  },
  {
    "path": "lib/plugins/module-paths.js",
    "content": "var path = require('path');\nvar common = require('../util/common');\nvar config = require('../config');\n\nvar PLUGIN_DIR_RE = /[/\\\\]whistle\\.[a-z\\d_-]+[/\\\\]?$/;\n\nvar uniqueArr = function(list) {\n  var result = [];\n  list.forEach(function(item) {\n    if (result.indexOf(item) === -1) {\n      result.push(item);\n    }\n  });\n  return result;\n};\nvar resolvePath = function (str) {\n  return path.resolve(str);\n};\nvar prePlugins = (config.prePluginsPath || []).map(resolvePath);\nvar addon = (config.addon || []).map(resolvePath);\naddon = addon.concat(addon.map(formatPath));\n\nfunction addDebugPaths(plugins) {\n  if (config.debugMode) {\n    var cwd = process.cwd();\n    plugins.unshift(cwd);\n    config.projectPluginPaths = config.projectPluginPaths || [];\n    config.projectPluginPaths.push(cwd);\n    if (PLUGIN_DIR_RE.test(cwd)) {\n      plugins.unshift(cwd.replace(PLUGIN_DIR_RE, '/'));\n    }\n  }\n}\n\nvar pluginPaths = config.pluginPaths;\nif (pluginPaths) {\n  pluginPaths = pluginPaths.concat(pluginPaths.map(formatPath).concat(addon));\n  pluginPaths = prePlugins.concat(pluginPaths);\n  addDebugPaths(pluginPaths);\n  pluginPaths = uniqueArr(pluginPaths);\n  exports.getPaths = function () {\n    return pluginPaths;\n  };\n  return;\n}\n\naddon = prePlugins.concat(addon);\nif (config.noGlobalPlugins) {\n  addDebugPaths(addon);\n  addon = uniqueArr(addon);\n  exports.getPaths = function () {\n    return addon;\n  };\n  return;\n}\n\nvar env = process.env || {};\nvar execPath = process.execPath;\nvar isWin = process.platform === 'win32';\nvar paths = module.paths.map(formatPath);\nvar globalDir = formatPath(getGlobalDir());\nvar appDataDir = formatPath(env.APPDATA, 'npm');\nvar pluginsPath = formatPath(config.baseDir);\n\nif (typeof execPath !== 'string') {\n  execPath = '';\n}\n\npaths = paths.filter(function (p) {\n  return p;\n});\n\nif (paths.indexOf(pluginsPath) == -1) {\n  paths.unshift(pluginsPath);\n}\n\npluginsPath = formatPath(env.WHISTLE_PLUGINS_PATH);\nif (pluginsPath && paths.indexOf(pluginsPath) == -1) {\n  paths.unshift(pluginsPath);\n}\nif (!config.customPluginPaths || !config.customPluginPaths.length) {\n  paths.unshift(config.CUSTOM_PLUGIN_PATH);\n}\npaths = addon.concat(paths);\naddDebugPaths(paths);\n\nvar nvmBin = env.NVM_BIN;\nif (nvmBin && typeof nvmBin === 'string') {\n  nvmBin = formatPath(path.join(nvmBin, '../lib'));\n  if (paths.indexOf(nvmBin) == -1) {\n    paths.push(nvmBin);\n  }\n}\n\nif (appDataDir && paths.indexOf(appDataDir) == -1) {\n  paths.push(appDataDir);\n}\n\nif (globalDir && paths.indexOf(globalDir) == -1) {\n  paths.push(globalDir);\n}\nif (env.PATH && typeof env.PATH === 'string') {\n  var list = env.PATH.trim().split(isWin ? ';' : ':');\n  ['', '../', '../lib'].forEach(function (prefix) {\n    list.forEach(function (dir) {\n      dir = formatPath(dir, prefix);\n      addPluginPath(dir);\n    });\n  });\n}\n\nfunction addPluginPath(dir) {\n  dir && paths.indexOf(dir) == -1 && paths.push(dir);\n}\n\nfunction formatPath(dir, prefix) {\n  if (typeof dir !== 'string' || !(dir = dir.trim())) {\n    return null;\n  }\n  if (/(?:^|\\/|\\\\)node_modules[\\\\\\/]?$/.test(dir)) {\n    return common.formatPathSep(dir);\n  }\n  return common.formatPathSep(path.resolve(dir, typeof prefix === 'string' ? prefix : '', 'node_modules'));\n}\n\nfunction getGlobalDir() {\n  var globalPrefix;\n  if (env.PREFIX) {\n    globalPrefix = env.PREFIX;\n  } else if (isWin) {\n    globalPrefix = execPath && path.dirname(execPath);\n  } else {\n    globalPrefix = execPath && path.dirname(path.dirname(execPath));\n    if (env.DESTDIR && typeof env.DESTDIR === 'string') {\n      globalPrefix = path.join(env.DESTDIR, globalPrefix);\n    }\n  }\n  if (typeof globalPrefix !== 'string') {\n    return;\n  }\n  return formatPath(globalPrefix, isWin ? '' : 'lib');\n}\npaths = uniqueArr(paths);\nexports.getPaths = function () {\n  return paths;\n};\n"
  },
  {
    "path": "lib/plugins/proxy.js",
    "content": "var net = require('net');\nvar http = require('http');\nvar extend = require('extend');\nvar LRU = require('lru-cache');\nvar parseUrl = require('../util/parse-url-safe');\nvar getServer = require('hagent').create(function () {\n  return net.createServer({ pauseOnConnect: true });\n}, 51500);\nvar formatHeaders = require('hparser').formatHeaders;\nvar common = require('../util/common');\n\nvar socketProto = net.Socket.prototype;\nvar originConnect = socketProto.connect;\nvar SPEC_HOST_RE =\n  /.whistle-policy(?:-(internal|capture|tunnel|connect)(?:-([A-Za-z\\d]+))?)?(?:\\.proto-([\\w%.-]+))?$/;\nvar lru = new LRU({ max: 5120, maxAge: 30000 });\nvar PROXY_ID_HEADER;\nvar proxyIp;\nvar proxyPort;\n\nfunction toNumber(x) {\n  x = Number(x);\n  return x >= 0 ? x : false;\n}\n\nfunction isPipeName(s) {\n  return typeof s === 'string' && toNumber(s) === false;\n}\n\nfunction normalizeArgs(args) {\n  if (args.length === 0) {\n    return [{}, null];\n  }\n\n  var arg0 = args[0];\n  if (Array.isArray(arg0)) {\n    return arg0;\n  }\n  var options = {};\n  if (typeof arg0 === 'object' && arg0 !== null) {\n    // (options[...][, cb])\n    options = arg0;\n  } else if (isPipeName(arg0)) {\n    // (path[...][, cb])\n    options.path = arg0;\n  } else {\n    // ([port][, host][...][, cb])\n    options.port = arg0;\n    if (args.length > 1 && typeof args[1] === 'string') {\n      options.host = args[1];\n    }\n  }\n\n  var cb = args[args.length - 1];\n  if (typeof cb !== 'function') {\n    return [options, null];\n  }\n  return [options, cb];\n}\n\nfunction createSocket(opts, cb) {\n  var done;\n  var client;\n  var headers = common.lowerCaseify(opts.headers);\n  var handleCb = function (err, socket, res) {\n    if (!done) {\n      done = true;\n      cb && cb(err, socket, res);\n    }\n    if (err && client) {\n      client.destroy();\n      client = null;\n    }\n  };\n  if (opts.isHttp) {\n    headers['x-whistle-policy'] = 'capture';\n  } else if (opts.isConnect) {\n    headers['x-whistle-policy'] = 'connect';\n  } else if (!headers['x-whistle-policy']) {\n    headers['x-whistle-policy'] = 'tunnel';\n  }\n  if (opts.reqId && typeof opts.reqId === 'string') {\n    headers['x-whistle-plugin-request-id'] = opts.reqId;\n  }\n  if (opts.isInternal) {\n    headers[PROXY_ID_HEADER] = '1';\n  }\n  if (opts.proto) {\n    headers['x-whistle-transport-protocol'] = opts.proto;\n  }\n  opts.path = opts.path || common.joinIpPort(opts.host, opts.port || 80);\n  headers.host = opts.path;\n  var clientIp = common.removeIPV6Prefix(opts.clientIp);\n  var clientPort = opts.clientPort;\n  if (clientIp && net.isIP(clientIp)) {\n    headers[common.CLIENT_IP_HEADER] = clientIp;\n  }\n  if (clientPort > 0 && clientPort < 65536) {\n    headers['x-whistle-client-port'] = clientPort;\n  }\n  headers[common.ACK_HEADER] = '1';\n  client = http.request({\n    host: proxyIp,\n    port: proxyPort,\n    method: 'CONNECT',\n    agent: false,\n    path: opts.path,\n    headers: headers\n  });\n  client.once('connect', function (res, socket) {\n    socket.on('error', handleCb);\n    if (res.statusCode !== 200) {\n      var err = new Error('Tunneling socket could not be established, statusCode=' + res.statusCode);\n      err.statusCode = res.statusCode;\n      socket.destroy();\n      return handleCb(err);\n    }\n    if (res.headers[common.ALLOW_ACK]) {\n      socket.write('1');\n    }\n    handleCb(null, socket, res);\n  });\n  client.on('error', handleCb);\n  client.end();\n}\n\nmodule.exports = function (options, cb) {\n  PROXY_ID_HEADER = options.PROXY_ID_HEADER;\n  proxyIp = options.proxyIp;\n  proxyPort = options.proxyPort;\n  var wrapWsReader = options.wrapWsReader;\n  var wrapWsWriter = options.wrapWsWriter;\n\n  getServer(function (server, port) {\n    var tempServerOpts = {\n      host: '127.0.0.1',\n      port: port\n    };\n    server.on('connection', function (socket) {\n      var destroyed;\n      var sock;\n      var destroy = function () {\n        if (!destroyed) {\n          destroyed = true;\n          socket.destroy();\n          sock && sock.destroy();\n        }\n      };\n      var handleProxy = function (connOpts) {\n        createSocket(connOpts, function (err, sock) {\n          if (err) {\n            return destroy();\n          }\n          sock.on('error', destroy);\n          socket.pipe(sock).pipe(socket);\n          socket.resume();\n        });\n      };\n      var key = `${socket.remotePort}:${port}`;\n      var opts = lru.peek(key);\n      if (opts) {\n        lru.del(key);\n        handleProxy(opts);\n      } else {\n        lru.set(key, handleProxy);\n      }\n    });\n    var handleTempCache = function (socket, opts) {\n      socket.once('connect', function () {\n        var key = `${socket.localPort}:${port}`;\n        var handler = lru.get(key);\n        if (typeof handler === 'function') {\n          lru.del(key);\n          handler(opts);\n        } else {\n          lru.set(key, opts);\n        }\n      });\n      return socket;\n    };\n    var connect = function (opts, cb) {\n      var socket = net.connect(tempServerOpts, cb);\n      handleTempCache(socket, opts);\n      socket.on('error', common.noop);\n      return socket;\n    };\n    var request = function (opts, cb) {\n      if (typeof opts === 'string') {\n        opts = { url: opts };\n      } else {\n        opts = extend({}, opts);\n      }\n      opts.url = common.setProtocol(opts.url);\n      var uri = parseUrl(opts.url);\n      var client;\n      if (common.isConnect(uri)) {\n        client = connect(\n          {\n            host: uri.hostname,\n            port: uri.port,\n            headers: opts.proxyHeaders\n          },\n          cb\n        );\n        client.isConnect = true;\n        client.isTunnel = true;\n        return client;\n      }\n      var protocol = uri.protocol;\n      var isHttp = protocol === 'https:' || protocol === 'http:';\n      var rawNames = {};\n      var headers = common.lowerCaseify(opts.headers, rawNames);\n      var isWs = common.isUpgrade(uri, headers);\n      if (!isHttp && !isWs) {\n        return cb && cb(new Error('Bad URL'));\n      }\n\n      var isHttps = protocol === 'https:' || protocol === 'wss:';\n      if (isWs) {\n        headers.connection = 'Upgrade';\n        headers.upgrade = headers.upgrade || 'websocket';\n        rawNames.connection = 'Connection';\n        rawNames.upgrade = 'Upgrade';\n      }\n      if (!opts.reserveHost && uri.host) {\n        headers.host = uri.host;\n      }\n      client = http.request(\n        {\n          path: opts.url || '/',\n          method: opts.method,\n          agent: null,\n          createConnection: function () {\n            return connect({\n              host: uri.hostname || 'localhost',\n              port: uri.port || (isHttps ? 443 : 80),\n              headers: opts.proxyHeaders,\n              isHttp: true,\n              isInternal: opts.isInternal,\n              clientIp: opts.clientIp,\n              clientPort: opts.clientPort\n            });\n          },\n          headers: formatHeaders(headers, rawNames)\n        },\n        cb\n      );\n      if (isWs) {\n        client.once('upgrade', function (res, socket) {\n          socket.headers = res.headers;\n          if (!opts.needRawData) {\n            wrapWsReader(socket, opts.maxPayload);\n            wrapWsWriter(socket);\n            opts.stopPing && socket.stopPing();\n          }\n        });\n      }\n      client.isHttps = isHttps;\n      client.isHttp = isHttp;\n      client.isUpgrade = isWs;\n      client.isWebSocket = isWs && common.isWebSocket(headers);\n      client.on('error', common.noop);\n      return client;\n    };\n    socketProto.connect = function () {\n      var args = normalizeArgs(arguments);\n      var opts = args[0];\n      var cb = args[1];\n      if (!opts || !SPEC_HOST_RE.test(opts.host)) {\n        return originConnect.apply(this, arguments);\n      }\n      var policy = RegExp.$1;\n      var reqId = RegExp.$2;\n      var proto = RegExp.$3;\n      var host = opts.host.slice(0, -RegExp['$&'].length);\n      originConnect.call(this, tempServerOpts, cb);\n      handleTempCache(this, {\n        host: host || 'localhost',\n        port: opts.port,\n        reqId: reqId,\n        proto: proto,\n        headers: opts.proxyHeaders,\n        isInternal: policy === 'internal',\n        isTunnel: !policy || policy === 'tunnel',\n        isConnect: policy === 'connect',\n        clientIp: opts.clientIp,\n        clientPort: opts.clientPort\n      });\n      return this;\n    };\n    cb({\n      connect: connect,\n      request: request\n    });\n  });\n};\n"
  },
  {
    "path": "lib/plugins/shared-storage.js",
    "content": "var fse = require('fs-extra2');\nvar fs = require('fs');\nvar common = require('../util/common');\n\nvar UTF8_OPTIONS = { encoding: 'utf8' };\n\nfunction ensureFile(filepath) {\n  return new Promise(function(resolve, reject) {\n    fse.ensureFile(filepath, function(err) {\n      if (err ) {\n        return reject(err);\n      }\n      resolve();\n    });\n  });\n}\n\nfunction writeJson(filepath, obj, bkFile) {\n  return new Promise(async function(resolve, reject) {\n    await ensureFile(filepath);\n    fse.writeJson(filepath, obj, function(err) {\n      if (err) {\n        return reject(err);\n      }\n      common.copyFile(filepath, bkFile, function() {\n        resolve(obj);\n      });\n    });\n  });\n}\n\nfunction readJson(filepath, bkFile) {\n  return new Promise(function(resolve, reject) {\n    fs.readFile(filepath, UTF8_OPTIONS, function(err, ctn) {\n      if (err) {\n        return err.code === 'ENOENT' ? resolve({}) : reject(err);\n      }\n      if (ctn) {\n        try {\n          ctn = JSON.parse(ctn) || {};\n          resolve(typeof ctn === 'object'? ctn : {});\n        } catch (e) {\n          reject(e);\n        }\n        return;\n      }\n      common.readJson(bkFile, function(_, obj) {\n        resolve(obj || {});\n      });\n    });\n  });\n}\n\nmodule.exports = function(filepath) {\n  var bkFile = filepath + '.bk';\n  var getAll = async function() {\n    try {\n      return await readJson(filepath, bkFile);\n    } catch (e) {}\n    return readJson(filepath, bkFile);\n  };\n\n  var writeAll = async function(obj) {\n    try {\n      return await writeJson(filepath, obj, bkFile);\n    } catch (e) {}\n    return writeJson(filepath, obj, bkFile);\n  };\n\n  var setItem = async function(key, value) {\n    var obj = await getAll();\n    if (key && typeof key === 'object') {\n      Object.keys(key).forEach(function(k) {\n        obj[k] = key[k];\n      });\n    } else {\n      obj[key] = value;\n    }\n    return writeAll(obj);\n  };\n\n  var getItem = async function(key) {\n    var obj = await getAll();\n    return obj[key];\n  };\n\n  var removeItem = async function(key) {\n    var obj = await getAll();\n    if (Array.isArray(key)) {\n      key.forEach(function(k) {\n        delete obj[k];\n      });\n    } else if (key && typeof key === 'object') {\n      Object.keys(key).forEach(function(k) {\n        delete obj[k];\n      });\n    } else {\n      delete obj[key];\n    }\n    return writeAll(obj);\n  };\n\n  return {\n    getAll: getAll,\n    setAll: writeAll,\n    setItem: setItem,\n    getItem: getItem,\n    removeItem: removeItem\n  };\n};\n"
  },
  {
    "path": "lib/plugins/util.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar util = require('../util');\nvar config = require('../config');\nvar protocols = require('../rules/protocols');\nvar common = require('../util/common');\n\nvar ORG_RE = /^@[\\w.~-]+$/;\nvar WHISLTE_PLUGIN_RE = /^whistle\\.[a-z\\d_\\-]+$/;\nvar PLUGIN_NAME_RE = /^(?:@[\\w.~-]+\\/)?(whistle\\.[a-z\\d_\\-]+)$/;\nvar DEV_PLUGINS_PATH = config.DEV_PLUGINS_PATH;\nvar MAX_EXPIRE = 36000;\nvar UTF8_OPTIONS = { encoding: 'utf8' };\nvar workerScript = util.readFileSync(path.join(__dirname, '../../assets/js/worker.js'), false);\nvar workers = {};\n\nfunction replaceScript(ctn, name) {\n  return workerScript.replace('$PLUGIN_NAME', name).replace('/*sourcecode*/', ctn);\n}\n\nexports.readWorkerSync = function(root, conf, name) {\n  var file = util.getWebWorker(conf);\n  file = file && util.readFileSync(path.join(root, file));\n  if (!file) {\n    return;\n  }\n  var id = util.createHash(file);\n  workers[id] = replaceScript(file, name);\n  return id;\n};\n\nexports.readWorker = function(root, conf, callback, name) {\n  var file = util.getWebWorker(conf);\n  if (!file) {\n    return callback();\n  }\n  readFile(path.join(root, file), function(err, ctn) {\n    if (err || !ctn) {\n      return callback();\n    }\n    var id = util.createHash(ctn);\n    workers[id] = replaceScript(ctn, name);\n    callback(id);\n  });\n};\n\nexports.getWorker = function(id) {\n  return workers[id];\n};\n\nexports.resetWorkers = function(plugins) {\n  var _workers = {};\n  Object.keys(plugins).forEach(function(key) {\n    var plugin = plugins[key];\n    _workers[plugin.webWorker] = workers[plugin.webWorker];\n  });\n  workers = _workers;\n};\n\nfunction isOrgModule(name) {\n  return ORG_RE.test(name);\n}\n\nexports.isOrgModule = isOrgModule;\n\nexports.isPluginName = function(name) {\n  return PLUGIN_NAME_RE.test(name);\n};\n\nfunction getPluginName(name) {\n  return name.substring(name.lastIndexOf('.') + 1);\n}\n\nexports.getPluginName = getPluginName;\n\nfunction isWhistleModule(name) {\n  return WHISLTE_PLUGIN_RE.test(name);\n}\n\nexports.isWhistleModule = isWhistleModule;\n\nfunction getHomePageFromPackage(pkg) {\n  if (util.isUrl(pkg.homepage)) {\n    return pkg.homepage;\n  }\n\n  return extractUrl(pkg.repository) || '';\n}\n\nfunction extractUrl(repository) {\n  if (\n    !repository ||\n    repository.type != 'git' ||\n    typeof repository.url != 'string'\n  ) {\n    return;\n  }\n\n  var url = repository.url.replace(/^git\\+/i, '');\n  if (!util.isUrl(url)) {\n    url = url.replace(/^git@([^:]+):/, 'http://$1/');\n  }\n\n  return url.replace(/\\.git\\s*$/i, '');\n}\n\nexports.getHomePageFromPackage = getHomePageFromPackage;\n\nexports.parseValues = function (val, name) {\n  if (val) {\n    val = util.parseJSON(val);\n  }\n  if (!val) {\n    return '';\n  }\n  var result = {};\n  name = util.getPluginFile(name);\n  Object.keys(val).forEach(function (key) {\n    result[util.getInlineKey(key, name)] = util.toString(val[key]);\n  });\n  return result;\n};\n\nexports.getPluginHomepage = function (pkg) {\n  var url = pkg.pluginHomepage || pkg.pluginHomePage;\n  return typeof url === 'string' ? url : undefined;\n};\n\nexports.excludePlugin = function (name) {\n  if (\n    protocols.contains(name) ||\n    (config.allowPluginList && config.allowPluginList.indexOf(name) === -1)\n  ) {\n    return true;\n  }\n  return config.blockPluginList && config.blockPluginList.indexOf(name) !== -1;\n};\n\nfunction setPlugin(plugins, pkg, root, mtime, sync) {\n  if (PLUGIN_NAME_RE.test(pkg.name)) {\n    var name = sync ? RegExp.$1 : getPluginName(RegExp.$1);\n    plugins[name] = {\n      root: root,\n      mtime: mtime,\n      notUn: true,\n      isProj: true,\n      isDev: true\n    };\n  }\n}\n\nexports.readDevPluginsSync = function() {\n  var plugins = {};\n  try {\n    var files = fs.readdirSync(DEV_PLUGINS_PATH);\n    files.forEach(function(file) {\n      file = path.join(DEV_PLUGINS_PATH, file);\n      try {\n        var ctn = fs.readFileSync(file, UTF8_OPTIONS).split('\\n');\n        if (Date.now() - ctn[0] < MAX_EXPIRE && ctn[1]) {\n          var root = ctn[1];\n          var pkgPath = path.join(root, 'package.json');\n          var stats = common.getStatSync(pkgPath);\n          var pkg = common.readJsonSync(pkgPath);\n          setPlugin(plugins, pkg, root, stats.mtime.getTime(), true);\n        } else {\n          fs.unlinkSync(file);\n        }\n      } catch (e) {}\n    });\n  } catch (e) {}\n  return plugins;\n};\n\nfunction readFile(filepath, callback) {\n  fs.readFile(filepath, UTF8_OPTIONS, function (err, text) {\n    if (!err) {\n      return callback(err, text);\n    }\n    fs.readFile(filepath, UTF8_OPTIONS, callback);\n  });\n}\n\nfunction readDir(dir, callback) {\n  fs.readdir(dir, function (err, list) {\n    if (!err) {\n      return callback(err, list);\n    }\n    if (err.code !== 'ENOENT') {\n      return callback(err);\n    }\n    fs.readdir(dir, callback);\n  });\n}\n\nexports.readFile = readFile;\nexports.readDir = readDir;\n\nexports.readDevPlugins = function(callback) {\n  var plugins = {};\n  readDir(DEV_PLUGINS_PATH, function(err, files) {\n    var len = files && files.length;\n    if (!len) {\n      return callback(plugins);\n    }\n    files.forEach(function(file) {\n      file = path.join(DEV_PLUGINS_PATH, file);\n      readFile(file, function(_, ctn) {\n        ctn = ctn && ctn.split('\\n');\n        if (ctn && Date.now() - ctn[0] < MAX_EXPIRE && ctn[1]) {\n          var root = ctn[1];\n          var pkgPath = path.join(root, 'package.json');\n          readFile(pkgPath, function(_, pkg) {\n            if (pkg) {\n              try {\n                pkg = JSON.parse(pkg);\n                return common.getStat(pkgPath, function(_, stats) {\n                  stats && stats.isFile() && setPlugin(plugins, pkg, root, stats.mtime.getTime());\n                  --len === 0 && callback(plugins);\n                });\n              } catch (e) {}\n            }\n            --len === 0 && callback(plugins);\n          });\n        } else {\n          fs.unlink(file, util.noop);\n          --len === 0 && callback(plugins);\n        }\n      });\n    });\n  });\n};\n\nfunction fsExistsSync(filepath, retry) {\n  try {\n    return fs.existsSync(filepath);\n  } catch (e) {}\n  return !retry && fsExistsSync(filepath, true);\n}\n\nexports.getSysPathSync = function(dir, name, org) {\n  var root = org ? path.join(dir, name, 'node_modules', org, name) : path.join(dir, name, 'node_modules', name);\n  if (fsExistsSync(root)) {\n    return root;\n  }\n  return path.join(dir, name);\n};\n\nexports.getSysPath = function(dir, name, isSys, cb) {\n  var root = path.join(dir, name);\n  if (!isSys) {\n    return cb(root);\n  }\n  var sysRoot = path.join(root, 'node_modules', name);\n  common.getStat(sysRoot, function(_, exists) {\n    cb(exists ? sysRoot : root);\n  });\n};\n"
  },
  {
    "path": "lib/rules/dns.js",
    "content": "var dns = require('dns');\nvar net = require('net');\nvar util = require('../util');\nvar config = require('../config');\n\nvar dnsCacheTime = parseInt(config.dnsCache, 10);\nvar dnsServer = config.dnsServer;\nvar dnsOverHttps = config.dnsOverHttps;\nvar resolve6 = config.resolve6;\nvar dnsResolve = config.dnsResolve;\nvar dnsResolve4= config.dnsResolve4;\nvar dnsResolve6 = config.dnsResolve6;\nvar dnsOptional = config.dnsOptional;\nvar dnsFallback = config.dnsFallback;\nvar dnsCache = {};\nvar callbacks = {};\nvar TIMEOUT = 10000;\nvar CACHE_TIME = dnsCacheTime >= 0 ? dnsCacheTime : 60000;\nvar MAX_CACHE_TIME = Math.max(CACHE_TIME * 3, 600000);\nvar IPV6_OPTIONS = {family: 6};\nvar IPV4_FIRST = {verbatim: false};\n\nif (dnsOverHttps) {\n  dnsOverHttps += (dnsOverHttps.indexOf('?') === -1 ? '?' : '&') + 'name=';\n}\n\nfunction getIpFromAnswer(data) {\n  var index = data.length - 1;\n  var ip = data[index] && data[index].data;\n  if (net.isIP(ip)) {\n    return ip;\n  }\n  while (index-- > 0) {\n    ip = data[index] && data[index].data;\n    if (net.isIP(ip)) {\n      return ip;\n    }\n  }\n}\n\nfunction lookDnsOverHttps(hostname, callback) {\n  util.request({ url: dnsOverHttps + hostname  }, function (err, data) {\n    if (err) {\n      return callback(err);\n    }\n    try {\n      data = JSON.parse(data);\n      data = data && data.Answer;\n      return callback(null, data && getIpFromAnswer(data));\n    } catch (e) {\n      err = data || e;\n    }\n    return callback(err || 'DNS Over HTTPS Look Failed');\n  });\n}\n\nfunction lookupDNS(hostname, callback) {\n  if (net.isIP(hostname)) {\n    return callback(null, hostname);\n  }\n  var list = callbacks[hostname];\n  if (list) {\n    if (callback) {\n      list.push(callback);\n    }\n    return;\n  }\n  callbacks[hostname] = list = [];\n  if (callback) {\n    list.push(callback);\n  }\n  var done;\n  var timer;\n  var optional = dnsOptional || dnsFallback;\n  var handleDns = function (fn, retry) {\n    timer = setTimeout(function () {\n      execCallback(util.TIMEOUT_ERR);\n    }, TIMEOUT);\n    if (!fn) {\n      if (dnsServer) {\n        fn = resolve6 ? 'resolve6' : 'resolve4';\n      } else if (dnsResolve) {\n        fn = 'resolve';\n      } else if (dnsResolve4) {\n        fn = 'resolve4';\n      } else if (dnsResolve6) {\n        fn = 'resolve6';\n      } else {\n        fn = 'lookup';\n      }\n    }\n    var useLookup;\n    var handleCallback = function (err, ip, type) {\n      clearTimeout(timer);\n      if (err) {\n        if (!retry && useLookup) {\n          return handleDns(config.ipv6Only ? 'resolve6' : 'resolve', true);\n        }\n        execCallback(err);\n      } else {\n        ip = Array.isArray(ip) ? ip[0] : ip;\n        if (!ip && optional) {\n          execCallback(true);\n        } else {\n          execCallback(null, ip || getDefaultIp(type));\n        }\n      }\n    };\n    try {\n      if (dnsOverHttps) {\n        return lookDnsOverHttps(hostname,  handleCallback);\n      }\n      if (fn === 'lookup') {\n        useLookup = true;\n        if (config.ipv6Only) {\n          return dns[fn](hostname, IPV6_OPTIONS, handleCallback);\n        }\n        if (!config.ipv6First && hostname === 'localhost') {\n          return dns[fn](hostname, IPV4_FIRST, handleCallback);\n        }\n      }\n      dns[fn](hostname, handleCallback);\n    } catch (err) {\n      //如果断网，可能直接抛异常，https代理没有用到error-handler\n      execCallback(err);\n    }\n  };\n  function execCallback(err, ip) {\n    clearTimeout(timer);\n    if (!err) {\n      dnsCache[hostname] = {\n        ip: ip,\n        hostname: hostname,\n        ipv6Only: config.ipv6Only,\n        order: config.dnsOrder,\n        time: Date.now()\n      };\n    } else if (optional) {\n      optional = false;\n      return handleDns('lookup', true);\n    }\n    if (done) {\n      return;\n    }\n    done = true;\n    var host = dnsCache[hostname];\n    delete callbacks[hostname];\n    list.forEach(function (callback) {\n      callback(err, host && host.ip);\n    });\n  }\n  handleDns();\n}\n\nfunction getDefaultIp(type) {\n  return resolve6 || type == 6 ? '0:0:0:0:0:0:0:1' : '127.0.0.1';\n}\n\nfunction checkCacheData(host) {\n  return host && (config.ipv6Only ? host.ipv6Only : !host.ipv6Only) && (config.dnsOrder === host.order) ? host: null;\n}\n\nmodule.exports = function lookup(hostname, callback, allowDnsCache) {\n  var host = allowDnsCache ? dnsCache[hostname] : null;\n  var cacheTime;\n  if (checkCacheData(host)) {\n    cacheTime = Date.now() - host.time;\n  }\n  if (host && cacheTime < MAX_CACHE_TIME) {\n    callback(null, host.ip);\n    if (cacheTime > CACHE_TIME) {\n      lookupDNS(host.hostname);\n    }\n    return host.ip;\n  }\n  lookupDNS(hostname, function (err, ip) {\n    err ? lookupDNS(hostname, callback) : callback(err, ip);\n  });\n};\n"
  },
  {
    "path": "lib/rules/index.js",
    "content": "var Pac = require('node-pac');\nvar net = require('net');\nvar LRU = require('lru-cache');\nvar path = require('path');\nvar iconv = require('iconv-lite');\nvar parseUrl = require('../util/parse-url-safe');\nvar extend = require('extend');\nvar parseQuery = require('querystring').parse;\nvar lookup = require('./dns');\nvar Rules = require('./rules');\nvar rulesUtil = require('./util');\nvar util = require('../util');\nvar logger = require('../util/logger');\nvar fileMgr = require('../util/file-mgr');\nvar config = require('../config');\n\n\nvar values = rulesUtil.values;\nvar globalRules = rulesUtil.rules;\nvar tplCache = new LRU({ max: 36 });\nvar clientCerts = new LRU({ max: 60, maxAge: 1000 * 60 * 60 });\nvar rules = new Rules();\nvar tempRules = new Rules();\nvar cachedPacs = {};\nvar RULES_HEADER = 'x-whistle-rule-value';\nvar KV_HEADER = 'x-whistle-key-value';\nvar NAME_HEADER = 'x-whistle-rule-name';\nvar KEY_HEADER = 'x-whistle-rule-key';\nvar HOST_HEADER = 'x-whistle-rule-host';\nvar LOCALHOST = '127.0.0.1';\nvar resolveReqRules = rules.resolveReqRules.bind(rules);\nvar AUTH_RE = /^([^\\\\/]+)@/;\nvar PLUGIN_VAR_RE = /^[a-z\\d_\\-]+\\./;\nvar COMMENT_RE = /^\\s*#/;\nvar VALUE_RE = /^\\s*``/m;\nvar BRACKET_RE = /[(\\[]/;\nvar SCRIPT_RE = /\\b(?:rules|values)\\b/;\nvar SEP_CIPHER_RE = /[^a-z\\d:!-]/i;\nvar CERT_RE = /^\\s*-----/;\n\nfunction isRulesContent(ctn) {\n  return !BRACKET_RE.test(ctn) || COMMENT_RE.test(ctn) || VALUE_RE.test(ctn) || !SCRIPT_RE.test(ctn);\n}\n\nrules._isGlobal = true;\n\nexports.Rules = Rules;\nexports.getEnabledRules = function() {\n  return rules._enabledList || [];\n};\nexports.getMFlag = function() {\n  return rules._mflag;\n};\nexports.parse = rules.parse.bind(rules);\nexports.append = rules.append.bind(rules);\nexports.resolveSNICallback = rules.resolveSNICallback.bind(rules);\nexports.resolveHost = rules.resolveHost.bind(rules);\nexports.getGRuleList = rules.getGRuleList.bind(rules);\nexports.resolveInternalHost = rules.resolveInternalHost.bind(rules);\nexports.resolveProxy = rules.resolveProxy.bind(rules);\nexports.resolveEnable = rules.resolveEnable.bind(rules);\nexports.hasReqScript = rules.hasReqScript.bind(rules);\nexports.resolveDisable = rules.resolveDisable.bind(rules);\nexports.resolvePipe = rules.resolvePipe.bind(rules);\nexports.resolveRule = rules.resolveRule.bind(rules);\nexports.resolveRules = resolveReqRules;\nexports.resolveResRules = rules.resolveResRules.bind(rules);\nexports.resolveBodyFilter = rules.resolveBodyFilter.bind(rules);\nexports.lookupHost = rules.lookupHost.bind(rules);\nexports.resolveLocalRule = rules.resolveLocalRule.bind(rules);\nexports.clearAppend = rules.clearAppend.bind(rules);\nexports.resolveTplVar = Rules.resolveTplVar;\n\nexports.disableDnsCache = function () {\n  Rules.disableDnsCache();\n};\n\nvar dnsResolve = function (host, callback) {\n  return lookup(host, callback || util.noop, true);\n};\nvar PROXY_HOSTS_RE = /[?&]proxyHosts?(?:&|$)/i;\nvar P_HOST_RE = /[?&]host=([\\w.:-]+)(?:&|$)/i;\n\nfunction isProxyHost(req, proxy, host) {\n  if (proxy) {\n    req._proxyTunnel =\n      isLineProp(proxy, 'proxyTunnel') ||\n      isLineProp(host, 'proxyTunnel') ||\n      util.isEnable(req, 'proxyTunnel') ||\n      isProxyEnable(req, 'proxyTunnel');\n    if (isLineProp(proxy, 'proxyHost')) {\n      return true;\n    }\n  }\n  return isLineProp(host, 'proxyHost');\n}\n\nfunction isProxyEnable(req, name) {\n  var props = req._proxyProps;\n  if (props === undefined) {\n    props = rules.resolveProxyProps(req);\n    req._proxyProps = props || null;\n  }\n  return props && util.isEnable(props, name);\n}\n\nfunction isLineProp(rule, name) {\n  return rule && rule.lineProps[name];\n}\n\nfunction resolveRulesFromList(list, fnName, req, options) {\n  var rule;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var mgr = list[i];\n    var _rule = mgr && mgr[fnName](req, options);\n    if (_rule) {\n      if (util.isImportant(_rule)) {\n        return _rule;\n      }\n      rule = rule || _rule;\n    }\n  }\n  return rule;\n}\n\nexports.getProxy = function (url, req, callback) {\n  if (!req) {\n    return callback();\n  }\n  var reqRules = req.rules;\n  req.curUrl = url;\n  if (!reqRules) {\n    return rules.lookupHost(req, callback);\n  }\n\n  delete reqRules.proxy;\n  delete reqRules.pac;\n  var pRules = req.pluginRules;\n  var fRules = req.rulesFileMgr;\n  var hRules = req.headerRulesMgr;\n  var filter = extend(\n    rules.resolveFilter(req),\n    pRules && pRules.resolveFilter(req),\n    fRules && fRules.resolveFilter(req),\n    hRules && hRules.resolveFilter(req)\n  );\n  var ignoreProxy;\n  var proxy;\n  var pacRule;\n  var host = rules.getHost(req, pRules, fRules, hRules);\n  var hostValue = util.getMatcherValue(host) || '';\n  if (config.multiEnv || req._headerRulesFirst) {\n    proxy = resolveRulesFromList([pRules, hRules, rules, fRules], 'resolveProxy', req, hostValue);\n  } else {\n    proxy = resolveRulesFromList([pRules, rules, fRules, hRules], 'resolveProxy', req, hostValue);\n  }\n  var proxyHostOnly;\n  var proxyHost = util.isEnable(req, 'proxyHost') || isProxyEnable(req, 'proxyHost');\n  if (proxy) {\n    proxyHostOnly = isLineProp(proxy, 'proxyHostOnly');\n    proxyHost = proxyHost || proxyHostOnly;\n    var protocol = proxy.matcher.substring(0, proxy.matcher.indexOf(':'));\n    ignoreProxy =\n      !filter['ignore:' + protocol] &&\n      (util.isIgnored(filter, 'proxy') || util.isIgnored(filter, protocol));\n    if (ignoreProxy) {\n      proxy = null;\n    } else {\n      proxyHost = proxyHost || PROXY_HOSTS_RE.test(proxy.matcher);\n    }\n  }\n\n  if (!ignoreProxy) {\n    proxyHost = isProxyHost(req, proxy, host) || proxyHost; // 不能调换顺序\n  }\n  var setHost = function () {\n    if (!host || req._isProxyReq) {\n      return false;\n    }\n    reqRules.host = host;\n    var hostname = util.removeProtocol(host.matcher, true);\n    if (!net.isIP(hostname)) {\n      req.curUrl = hostname || url;\n      rules.lookupHost(req, function (err, ip) {\n        callback(err, ip, host.port, host);\n      });\n    } else {\n      callback(null, hostname, host.port, host);\n    }\n    return true;\n  };\n  var resolvePacRule = function () {\n    if (pacRule != null) {\n      return;\n    }\n    if (util.isIgnored(filter, 'pac')) {\n      pacRule = false;\n      return;\n    }\n    if (config.multiEnv || req._headerRulesFirst) {\n      pacRule =\n        (pRules && pRules.resolvePac(req)) ||\n        (hRules && hRules.resolvePac(req)) ||\n        rules.resolvePac(req) ||\n        (fRules && fRules.resolvePac(req)) ||\n        false;\n    } else {\n      pacRule =\n        (pRules && pRules.resolvePac(req)) ||\n        rules.resolvePac(req) ||\n        (fRules && fRules.resolvePac(req)) ||\n        (hRules && hRules.resolvePac(req)) ||\n        false;\n    }\n    if (pacRule) {\n      proxyHostOnly = isLineProp(pacRule, 'proxyHostOnly');\n      proxyHost =\n        proxyHost || proxyHostOnly || isLineProp(pacRule, 'proxyHost');\n    }\n  };\n  if (host) {\n    if (!proxyHost && !ignoreProxy) {\n      resolvePacRule();\n    }\n    if (proxyHost) {\n      req._phost = parseUrl(util.setProtocol(util.joinIpPort(host.matcher, host.port)));\n    } else if (\n      !isLineProp(proxy, 'proxyFirst') &&\n      !isLineProp(host, 'proxyFirst') &&\n      !util.isEnable(req, 'proxyFirst') &&\n      !isProxyEnable(req, 'proxyFirst')\n    ) {\n      return setHost();\n    }\n    proxyHost = true;\n    reqRules.host = host;\n    req._enableProxyHost = true;\n  }\n  if (ignoreProxy || (proxyHostOnly && !req._phost)) {\n    req.curUrl = url;\n    return setHost() || rules.lookupHost(req, callback);\n  }\n  if (proxy) {\n    if (!req._phost && P_HOST_RE.test(proxy.matcher)) {\n      req._phost = parseUrl(util.setProtocol(RegExp.$1));\n    }\n    reqRules.proxy = proxy;\n    return callback();\n  }\n  resolvePacRule();\n  var pacUrl = util.getMatcherValue(pacRule);\n  if (!pacUrl) {\n    return setHost() || callback();\n  }\n  var auth;\n  if (AUTH_RE.test(pacUrl)) {\n    auth = RegExp.$1;\n    pacUrl = pacUrl.substring(auth.length + 1);\n  }\n  if (!util.isUrl(pacUrl) && !(pacUrl = util.joinPath(pacRule.root, pacUrl))) {\n    return setHost() || callback();\n  }\n  req._pacAuth = auth;\n  var pac = cachedPacs[pacUrl];\n  if (pac) {\n    delete cachedPacs[pacUrl];\n    cachedPacs[pacUrl] = pac;\n  } else {\n    var list = Object.keys(cachedPacs);\n    if (list.length >= 10) {\n      delete cachedPacs[list[0]];\n    }\n    cachedPacs[pacUrl] = pac = new Pac(pacUrl, dnsResolve);\n  }\n  reqRules.pac = pacRule;\n  return pac.findWhistleProxyForURL(\n    url.replace('tunnel:', 'https:'),\n    function (err, rule) {\n      if (rule) {\n        tempRules._file = pacRule.file;\n        tempRules.parse(pacRule.rawPattern + ' ' + rule);\n        req.curUrl = url;\n        if ((proxy = tempRules.resolveProxy(req, hostValue))) {\n          proxyHost = isProxyHost(req, proxy) || proxyHost; // 不能调换顺序\n          req._proxyTunnel =\n            req._proxyTunnel || isLineProp(pacRule, 'proxyTunnel');\n          var protocol = proxy.matcher.substring(0, proxy.matcher.indexOf(':'));\n          if (!util.isIgnored(filter, protocol)) {\n            reqRules.proxy = proxy;\n            reqRules.proxy.raw = pacRule.raw;\n          }\n        }\n      }\n      if (reqRules.proxy) {\n        (!proxyHost && setHost()) || callback();\n      } else {\n        req.curUrl = url;\n        setHost() || rules.lookupHost(req, callback);\n      }\n      logger.error(err);\n    }\n  );\n};\n\nfunction tpl(str, data) {\n  if (\n    typeof str !== 'string' ||\n    str.indexOf('<%') === -1 ||\n    str.indexOf('%>') === -1\n  ) {\n    return str + '';\n  }\n  var key = str;\n  var fn = tplCache.get(key);\n  if (!fn) {\n    str = str\n      .replace(/[\\u2028\\u2029]/g, '')\n      .replace(/\\t/g, ' ')\n      .replace(/\\r?\\n|\\r/g, '\\t')\n      .split('<%')\n      .join('\\u2028')\n      .replace(/((^|%>)[^\\u2028]*)'/g, '$1\\r')\n      .replace(/\\u2028=(.*?)%>/g, '\\',$1,\\'')\n      .split('\\u2028')\n      .join('\\');')\n      .split('%>')\n      .join('p.push(\\'')\n      .split('\\r')\n      .join('\\\\\\'');\n    try {\n      fn = new Function(\n        'obj',\n        'var p=[],print=function(){p.push.apply(p,arguments);};' +\n          'with(obj){p.push(\\'' +\n          str +\n          '\\');}return p.join(\\'\\');'\n      );\n    } catch (e) {\n      fn = e;\n      throw e;\n    } finally {\n      tplCache.set(key, fn);\n    }\n  } else if (typeof fn !== 'function') {\n    throw fn;\n  }\n  return fn(data || {}).replace(/\\t/g, '\\n');\n}\n\nfunction getScriptContext(req, res, body, pattern, frameOpts) {\n  var ip = req.clientIp || LOCALHOST;\n  var ctx = req.scriptContenxt;\n  if (!ctx) {\n    var headers = extend(true, {}, req.headers);\n    ctx = req.scriptContenxt = {\n      Buffer: Buffer,\n      pattern: pattern,\n      version: config.version,\n      port: config.port,\n      uiHost: 'local.wproxy.org',\n      uiPort: config.uiport,\n      url: req.fullUrl,\n      fullUrl: req.fullUrl,\n      method: util.toUpperCase(req.method) || 'GET',\n      httpVersion: req.httpVersion || '1.1',\n      decodeBuffer: function (buf, encoding) {\n        return iconv.decode(buf, encoding || 'utf8');\n      },\n      encodeString: function (str, encoding) {\n        return iconv.encode(str, encoding || 'utf8');\n      },\n      encodingExists: function (encoding) {\n        return iconv.encodingExists(encoding);\n      },\n      isLocalAddress: function (_ip) {\n        return util.isLocalAddress(_ip || ip);\n      },\n      ip: ip,\n      clientIp: ip,\n      clientPort: req.clientPort,\n      headers: headers,\n      reqHeaders: headers,\n      body: body || '',\n      reqScriptData: {},\n      res: null\n    };\n  }\n  if (frameOpts) {\n    ctx.ctx = {\n      sendToServer: frameOpts.sendToServer,\n      sendToClient: frameOpts.sendToClient,\n      handleSendToClientFrame: null,\n      handleSendToServerFrame: null\n    };\n  } else {\n    ctx.rules = [];\n    ctx.values = {};\n  }\n  ctx.value = req.globalValue;\n  ctx.getValue = function (key, onlyValues) {\n    var value = !onlyValues && req._inlineValues && req._inlineValues[key];\n    return typeof value === 'string' ? value : values.get(key);\n  };\n  ctx.parseUrl = parseUrl;\n  ctx.parseQuery = parseQuery;\n  ctx.tpl = ctx.render = tpl;\n  if (res) {\n    ctx.statusCode = res.statusCode;\n    ctx.serverIp = req.hostIp || LOCALHOST;\n    ctx.resHeaders = extend(true, {}, res.headers);\n  } else {\n    ctx.statusCode = '';\n    ctx.serverIp = '';\n    ctx.resHeaders = '';\n  }\n  return ctx;\n}\n\nfunction getReqPayload(req, res, cb) {\n  if (res) {\n    return cb();\n  }\n  if (req.getPayload && util.hasRequestBody(req)) {\n    if (typeof req._reqBody === 'string') {\n      cb(req._reqBody);\n    } else {\n      req.getPayload(function (_, payload) {\n        cb(fileMgr.decode(payload));\n      });\n    }\n  } else {\n    cb();\n  }\n}\n\nfunction execRulesScript(script, req, res, body, pattern, frameOpts) {\n  var context = getScriptContext(req, res, body, pattern, frameOpts);\n  if (!util.execScriptSync(script, context)) {\n    return '';\n  }\n  if (frameOpts) {\n    return context.ctx;\n  }\n  return Array.isArray(context.rules) ? {\n    rules: context.rules.join('\\n').trim(),\n    values: context.values\n  } : '';\n}\n\nvar CTX_RE = /\\bctx\\b/;\n\nexports.getFrameScriptCtx = function(script, req, res, options, cb) {\n  util.getRuleValue(script, function (text) {\n    if (!text || !CTX_RE.test(text)) {\n      return cb();\n    }\n    cb(execRulesScript(text, req, res, '', script.rawPattern, options));\n  }, null, null, null, req);\n};\n\nfunction handleDynamicRules(script, req, res, cb) {\n  util.getRuleValue(script, function (list) {\n    var scriptItem, index, text;\n    if (list) {\n      index = script.scriptIndex;\n      scriptItem = script.list[index];\n      text = scriptItem && list[index];\n    }\n    if (!scriptItem || isRulesContent(text)) {\n      return cb(list && list.join('\\n'));\n    }\n    getReqPayload(req, res, function (body) {\n      var result = execRulesScript(text, req, res, body, script.rawPattern);\n      list[index] = result.rules;\n      cb(list.join('\\n'), result.values);\n    });\n  }, null, null, null, req);\n}\n\nfunction resolvePluginVars(req, list) {\n  var varMap = {};\n  var globalValue;\n  var pList;\n  list.forEach(function (item) {\n    if (item.matcher[0] !== 'P') {\n      if (!globalValue) {\n        globalValue = item;\n      }\n      return;\n    }\n    var value = util.getMatcherValue(item);\n    var index = value.indexOf(PLUGIN_VAR_RE.test(value) ? '.' : '=');\n    if (index !== -1) {\n      var name = value.substring(0, index);\n      var plugin = getPluginName(name);\n      if (!plugin) {\n        return;\n      }\n      value = value.substring(index + 1);\n      if (value) {\n        pList = pList || [];\n        pList.push(item);\n        var list = varMap[name] || [];\n        varMap[name] = list;\n        if (list.indexOf(value) === -1) {\n          list.push(value);\n        }\n      }\n    }\n  });\n  req.rules = req.rules || {};\n  req._pluginVars = varMap;\n  req.rules.P = pList;\n  req.rules.G = globalValue;\n  req.globalValue = util.getMatcherValue(globalValue);\n}\n\nexports.resolvePluginVars = resolvePluginVars;\n\nexports.resolveRulesFile = function (req, callback) {\n  !req._resolvedG && req.rules.G && resolvePluginVars(req, req.rules.G.list);\n  req._resolvedG = true;\n  var rule = req.rules.rulesFile;\n  handleDynamicRules(rule, req, null, function (text, vals) {\n    if (text) {\n      vals = util.toPrivateValues(vals, rule.file);\n      var rulesFileMgr = new Rules(vals);\n      rulesFileMgr._file = rule.file;\n      rulesFileMgr.parse(text);\n      req.rulesFileMgr = rulesFileMgr;\n      req.curUrl = req.fullUrl;\n      text = req.rulesFileMgr.resolveReqRules(req);\n    }\n    // 不能放到if里面\n    util.mergeRules(req, text);\n    callback();\n  });\n};\n\nexports.resolveResRulesFile = function (req, res, callback) {\n  var rule = req.rules && req.rules.resScript;\n  handleDynamicRules(\n    rule,\n    req,\n    res,\n    function (text, vals) {\n      text = text && text.trim();\n      callback(\n        text && {\n          file: rule.file,\n          text: text,\n          values: util.toPrivateValues(vals, rule.file)\n        }\n      );\n    }\n  );\n};\n\nfunction getValue(req, key, keep) {\n  var value = req.headers[key];\n  if (value) {\n    if (!req.fromComposer && !keep && (config.strict || (!config.enableRequestHeaderRules && !config.multiEnv))) {\n      value = null;\n    } else {\n      req.rulesHeaders[key] = value;\n    }\n    delete req.headers[key];\n  }\n  try {\n    return value && decodeURIComponent(value);\n  } catch (e) {}\n  return value;\n}\n\nfunction getPluginName(key) {\n  return exports.getPlugin(key);\n}\n\nfunction initHeaderRules(req, needBodyFilters) {\n  if (req._bodyFilters !== undefined) {\n    return;\n  }\n  req._bodyFilters = null;\n  req.rulesHeaders = {};\n  var isPluginReq = req.isPluginReq && !req._isProxyReq;\n  req._headerRulesFirst = isPluginReq;\n  var rulesHeader = getValue(req, RULES_HEADER, isPluginReq);\n  var hostHeader = util.trimStr(getValue(req, HOST_HEADER));\n  var nameHeader = config.multiEnv && util.trimStr(getValue(req, NAME_HEADER));\n  var keyHeader = util.trimStr(getValue(req, KEY_HEADER));\n  var kvHeader = getValue(req, KV_HEADER, isPluginReq);\n\n  var ruleValue = util.trimStr(rulesHeader);\n  if (hostHeader) {\n    ruleValue = ruleValue + '\\n' + hostHeader;\n  }\n  if (keyHeader) {\n    keyHeader = util.trimStr(values.get(keyHeader));\n    if (keyHeader) {\n      ruleValue = keyHeader + '\\n' + ruleValue;\n    }\n  }\n  if (nameHeader) {\n    nameHeader = util.trimStr(globalRules.get(nameHeader));\n    if (nameHeader) {\n      ruleValue = ruleValue + '\\n' + nameHeader;\n    }\n  }\n  var curVars = rules._globalPluginVars;\n  var globalVars = {};\n  var value;\n  req._globalPluginVars = globalVars;\n  Object.keys(curVars).forEach(function (key) {\n    if (getPluginName(key)) {\n      value = curVars[key];\n      globalVars[key] = value;\n    }\n  });\n  if (ruleValue) {\n    var file = 'Header Rules';\n    var vals = util.toPrivateValues(util.parseJSON(kvHeader), file);\n    var rulesMgr = new Rules(vals);\n    rulesMgr._file = file;\n    rulesMgr.parse(ruleValue);\n    req.headerRulesMgr = rulesMgr;\n    var bodyFilters = needBodyFilters && rulesMgr._rules._bodyFilters;\n    if (bodyFilters && bodyFilters.length) {\n      req._bodyFilters = rules._rules._bodyFilters.concat(bodyFilters);\n    }\n    var headerVars = rulesMgr._globalPluginVars;\n    Object.keys(headerVars).forEach(function (key) {\n      var headerVal = getPluginName(key) && headerVars[key];\n      if (headerVal) {\n        var curVal = curVars[key];\n        globalVars[key] = curVal ? curVal.concat(headerVal) : headerVal;\n      }\n    });\n  }\n}\n\nexports.initHeaderRules = initHeaderRules;\n\nfunction initRules(req) {\n  var fullUrl = req.fullUrl || util.getFullUrl(req);\n  req.curUrl = fullUrl;\n  initHeaderRules(req);\n  if (req.headerRulesMgr) {\n    if (config.multiEnv || req._headerRulesFirst) {\n      req.rules = resolveReqRules(req);\n      util.mergeRules(req, req.headerRulesMgr.resolveReqRules(req));\n    } else {\n      req.rules = req.headerRulesMgr.resolveReqRules(req);\n      util.mergeRules(req, resolveReqRules(req));\n    }\n  } else {\n    req.rules = resolveReqRules(req);\n  }\n  return req.rules;\n}\n\nexports.initRules = initRules;\n\nvar HTTPS_RE = /^(?:ws|http)s:\\/\\//;\n\nfunction checkCache(cacheKey, callback) {\n  var result = clientCerts.peek(cacheKey);\n  if (result) {\n    if (result.pending) {\n      result.push(callback);\n    } else {\n      callback(result);\n    }\n    return true;\n  } else {\n    result = [callback];\n    result.pending = true;\n    clientCerts.set(cacheKey, result);\n  }\n}\n\nfunction getTlsOptions(req, cb) {\n  var cipher = req.rules.cipher;\n  if (!cipher) {\n    return cb();\n  }\n  cipher.list.forEach(function (rule) {\n    var value = rule && util.getMatcherValue(rule);\n    if (value && !SEP_CIPHER_RE.test(value)) {\n      rule.jsonObject = { ciphers: value };\n    }\n  });\n  util.parseRuleJson(cipher, function (data) {\n    if (!data) {\n      return cb();\n    }\n    var opts;\n    var addOption = function(name, value) {\n      if (!opts) {\n        opts = {};\n        cipher.__tlsOptions = function () {};\n        cipher.__tlsOptions._opts = opts;\n      }\n      opts[name] = value;\n    };\n    var addStrOption = function(name, opName) {\n      var value = (opName && data[opName]) || data[name];\n      if (util.isString(value)) {\n        addOption(name, value);\n      }\n    };\n    var addNumOption = function(name) {\n      var value = +data[name];\n      if (value >= 0) {\n        addOption(name, value);\n      }\n    };\n    addStrOption('ciphers', 'cipher');\n    addStrOption('secureProtocol');\n    addStrOption('maxVersion');\n    addStrOption('minVersion');\n    addStrOption('ca');\n    addStrOption('crl');\n    addStrOption('allowPartialTrustChain');\n    addStrOption('sessionIdContext');\n    addStrOption('sigalgs');\n    addStrOption('dhparam');\n    addStrOption('ecdhCurve');\n    addNumOption('secureOptions');\n    addNumOption('sessionTimeout');\n    if (data.honorCipherOrder) {\n      addOption('honorCipherOrder', true);\n    }\n    cb(data);\n  }, req);\n}\n\nfunction isCert(str) {\n  return CERT_RE.test(str);\n}\n\nexports.getClientCert = function (req, cb) {\n  if (!req) {\n    return cb();\n  }\n  req.curUrl = req.realUrl || req.fullUrl;\n  if (!HTTPS_RE.test(req.curUrl)) {\n    return cb();\n  }\n  getTlsOptions(req, function(options) {\n    var pRules = req.pluginRules;\n    var fRules = req.rulesFileMgr;\n    var hRules = req.headerRulesMgr;\n    var rule;\n    if (config.multiEnv || req._headerRulesFirst) {\n      rule = resolveRulesFromList([pRules, hRules, rules, fRules], 'resolveClientCert', req);\n    } else {\n      rule = resolveRulesFromList([pRules, rules, fRules, hRules], 'resolveClientCert', req);\n    }\n    if (rule) {\n      req.rules.clientCert = rule;\n      rule = util.parseJSON(rule.matcher.substring(17));\n      options = rule ? extend({}, rule, options) : options;\n    }\n    if (!options) {\n      return cb();\n    }\n    var base = util.getString(options.base);\n    var key = util.getString(options.key);\n    var cert = util.getString(options.cert);\n    var cacheKey;\n    try {\n      if (key && cert) {\n        if (base) {\n          key = path.join(base, key);\n          cert = path.join(base, cert);\n        }\n        cacheKey = 'cert\\n' + key + '\\n' + cert;\n        if (isCert(key) && isCert(cert)) {\n          return cb(key, cert, false, cacheKey);\n        }\n        if (\n        checkCache(cacheKey, function (data) {\n          if (data) {\n            cb(data[0], data[1], false, cacheKey);\n          } else {\n            cb();\n          }\n        })\n      ) {\n          return;\n        }\n        return fileMgr.readFileList([key, cert], function (data) {\n          var list = clientCerts.peek(cacheKey);\n          if (data[0] && data[1] && data[0].length && data[1].length) {\n            clientCerts.set(cacheKey, data);\n          } else {\n            clientCerts.del(cacheKey);\n            data = '';\n          }\n          list.forEach(function (fn) {\n            fn(data);\n          });\n        });\n      }\n      var pwd = util.getString(options.pwd || options.passphrase);\n      var pfx = util.getString(options.pfx);\n      if (pfx) {\n        if (base) {\n          pfx = path.join(base, key);\n        }\n        cacheKey = 'pfx\\n' + pwd + '\\n' + pfx;\n        if (isCert(pfx)) {\n          return cb(pwd, pfx, true, cacheKey);\n        }\n        if (\n        checkCache(cacheKey, function (buf) {\n          if (buf) {\n            cb(pwd, buf, true, cacheKey);\n          } else {\n            cb();\n          }\n        })\n      ) {\n          return;\n        }\n        return fileMgr.readFile(pfx, function (buf) {\n          var list = clientCerts.peek(cacheKey);\n          if (buf && buf.length) {\n            clientCerts.set(cacheKey, buf);\n          } else {\n            clientCerts.del(cacheKey);\n            buf = '';\n          }\n          list.forEach(function (fn) {\n            fn(buf);\n          });\n        });\n      }\n    } catch (e) {}\n    cb();\n  });\n};\n"
  },
  {
    "path": "lib/rules/protocols.js",
    "content": "var protocols = [\n  'G',\n  'style',\n  'host',\n  'rule',\n  'pipe',\n  'weinre',\n  'proxy',\n  'https2http-proxy',\n  'http2https-proxy',\n  'internal-proxy',\n  'pac',\n  'filter',\n  'ignore',\n  'enable',\n  'disable',\n  'delete',\n  'log',\n  'plugin',\n  'referer',\n  'auth',\n  'ua',\n  'urlParams',\n  'params',\n  'resMerge',\n  'replaceStatus',\n  'method',\n  'cache',\n  'attachment',\n  'forwardedFor',\n  'responseFor',\n  'rulesFile',\n  'resScript',\n  'frameScript',\n  'reqDelay',\n  'resDelay',\n  'headerReplace',\n  'reqSpeed',\n  'resSpeed',\n  'reqType',\n  'resType',\n  'reqCharset',\n  'resCharset',\n  'reqCookies',\n  'resCookies',\n  'reqCors',\n  'resCors',\n  'reqHeaders',\n  'resHeaders',\n  'trailers',\n  'reqPrepend',\n  'resPrepend',\n  'reqBody',\n  'resBody',\n  'reqAppend',\n  'resAppend',\n  'urlReplace',\n  'reqReplace',\n  'resReplace',\n  'reqWrite',\n  'resWrite',\n  'reqWriteRaw',\n  'resWriteRaw',\n  'cssAppend',\n  'htmlAppend',\n  'jsAppend',\n  'cssBody',\n  'htmlBody',\n  'jsBody',\n  'cssPrepend',\n  'htmlPrepend',\n  'jsPrepend',\n  'cipher',\n  'sniCallback'\n];\n\nvar RULE_RE = /^(?:|x|xs)(?:file|rawfile|dust|tpl|jsonp):/;\nvar LOC_RE = /^locationHref:/;\nvar PROXY_RE =\n  /^x?(?:socks|proxy|https?-proxy|internal-proxy|internal-https?-proxy|https2http-proxy|http2https-proxy)$/;\nvar pureResProtocols = [\n  'replaceStatus',\n  'cache',\n  'attachment',\n  'resMerge',\n  'resDelay',\n  'resSpeed',\n  'resType',\n  'resType',\n  'resCharset',\n  'resCookies',\n  'resCors',\n  'resHeaders',\n  'trailers',\n  'resPrepend',\n  'resBody',\n  'resAppend',\n  'resReplace',\n  'resWrite',\n  'resWriteRaw',\n  'cssAppend',\n  'htmlAppend',\n  'jsAppend',\n  'cssBody',\n  'htmlBody',\n  'jsBody',\n  'cssPrepend',\n  'htmlPrepend',\n  'jsPrepend',\n  'responseFor'\n];\nvar resProtocols = [\n  'filter',\n  'enable',\n  'disable',\n  'ignore',\n  'style',\n  'delete',\n  'headerReplace'\n].concat(pureResProtocols);\nvar binProtocols = [\n  'reqPrepend',\n  'resPrepend',\n  'reqBody',\n  'resBody',\n  'reqAppend',\n  'resAppend'\n];\nvar jsProtocols = ['jsAppend', 'jsBody', 'jsPrepend'];\nvar cssProtocols = ['cssAppend', 'cssBody', 'cssPrepend'];\nvar aliasProtocols = {\n  ruleFile: 'rulesFile',\n  ruleScript: 'rulesFile',\n  rulesScript: 'rulesFile',\n  reqScript: 'rulesFile',\n  reqRules: 'rulesFile',\n  resRules: 'resScript',\n  pathReplace: 'urlReplace',\n  download: 'attachment',\n  skip: 'ignore',\n  'http-proxy': 'proxy',\n  'xhttp-proxy': 'xproxy',\n  status: 'statusCode',\n  hosts: 'host',\n  xhost: 'host',\n  html: 'htmlAppend',\n  js: 'jsAppend',\n  reqMerge: 'params',\n  tlsOptions: 'cipher',\n  css: 'cssAppend',\n  excludeFilter: 'filter',\n  includeFilter: 'filter',\n  P: 'G'\n};\nvar protocolsWithoutG = protocols.slice(1);\nvar reqProtocols = protocols.filter(function (name) {\n  return pureResProtocols.indexOf(name) === -1;\n});\nvar reqProtosWithoutG = reqProtocols.filter(function (name) {\n  return name !== 'G';\n});\n\nvar RULE_PROTO_RE = /^([\\w.-]+):\\/\\//;\n\nexports.getRuleProto = function(rule) {\n  if (!RULE_PROTO_RE.test(rule.matcher)) {\n    return;\n  }\n  var proto = RegExp.$1;\n  var ruleProto = aliasProtocols[proto];\n  if (!ruleProto || ruleProto === 'filter') {\n    return proto;\n  }\n  if (ruleProto === 'rulesFile') {\n    return 'reqScript';\n  }\n  if (ruleProto === 'urlReplace') {\n    return 'pathReplace';\n  }\n  if (ruleProto === 'params') {\n    return 'reqMerge';\n  }\n  return ruleProto;\n};\n\nexports.multiMatchs = [\n  'G',\n  'ignore',\n  'enable',\n  'filter',\n  'disable',\n  'plugin',\n  'delete',\n  'style',\n  'cipher',\n  'trailers',\n  'urlParams',\n  'params',\n  'headerReplace',\n  'reqHeaders',\n  'resHeaders',\n  'reqCors',\n  'resCors',\n  'reqCookies',\n  'resCookies',\n  'reqReplace',\n  'urlReplace',\n  'resReplace',\n  'resMerge',\n  'reqBody',\n  'reqPrepend',\n  'resPrepend',\n  'reqAppend',\n  'resAppend',\n  'resBody',\n  'htmlAppend',\n  'jsAppend',\n  'cssAppend',\n  'htmlBody',\n  'jsBody',\n  'cssBody',\n  'htmlPrepend',\n  'jsPrepend',\n  'cssPrepend',\n  'rulesFile',\n  'resScript'\n];\nexports.protocols = protocols;\nexports.protocolsWithoutG = protocolsWithoutG;\nexports.pureResProtocols = pureResProtocols;\nexports.reqProtocols = reqProtocols;\nexports.reqProtosWithoutG = reqProtosWithoutG;\nexports.resProtocols = resProtocols;\nexports.aliasProtocols = aliasProtocols;\n\nfunction getRules() {\n  return resetRules({});\n}\n\nexports.getRules = getRules;\n\nfunction isBinProtocol(protocol) {\n  if (binProtocols.indexOf(protocol) != -1) {\n    return 1;\n  }\n  if (jsProtocols.indexOf(protocol) != -1) {\n    return 2;\n  }\n  if (cssProtocols.indexOf(protocol) != -1) {\n    return 3;\n  }\n}\n\nexports.isBinProtocol = isBinProtocol;\n\nfunction resetRules(rules) {\n  protocols.forEach(function (protocol) {\n    rules[protocol] = [];\n  });\n  rules._localRule = [];\n  rules._bodyFilters = [];\n  rules._clientCerts = [];\n  rules.rule.isRuleProto = true;\n  return rules;\n}\n\nexports.resetRules = resetRules;\n\nfunction isResRule(protocol) {\n  return resProtocols.indexOf(protocol) != -1;\n}\n\nexports.isResRule = isResRule;\n\nfunction isWebProtocol(protocol) {\n  return protocol == 'http:' || protocol == 'https:';\n}\n\nexports.isWebProtocol = isWebProtocol;\n\nfunction isWebsocketProtocol(protocol) {\n  return protocol == 'ws:' || protocol == 'wss:';\n}\n\nexports.isWebsocketProtocol = isWebsocketProtocol;\n\nfunction isFileProxy(protocol) {\n  return RULE_RE.test(protocol) || LOC_RE.test(protocol);\n}\n\nexports.isFileProxy = isFileProxy;\n\nexports.isLocHref = function(protocol) {\n  return LOC_RE.test(protocol);\n};\n\nfunction contains(name) {\n  if (\n    protocols.indexOf(name) != -1 ||\n    aliasProtocols[name] ||\n    PROXY_RE.test(name)\n  ) {\n    return true;\n  }\n  name += ':';\n  return (\n    isWebsocketProtocol(name) ||\n    isWebProtocol(name) ||\n    isFileProxy(name) ||\n    name == 'tunnel:'\n  );\n}\n\nexports.contains = contains;\n"
  },
  {
    "path": "lib/rules/recycle-bin.js",
    "content": "var fs = require('fs');\nvar fse = require('fs-extra2');\nvar path = require('path');\nvar readFileSafe = require('../util/common').readFileTextSync;\nvar logger = require('../util/logger');\n\nvar NAME_RE = /^\\d+\\.([\\s\\S]+)$/;\nvar MAX_COUNT = 200;\nvar noop = function () {};\n\nfunction getName(item) {\n  return item.name;\n}\n\nfunction RecycleBin(dir) {\n  fse.ensureDirSync(dir);\n  var list = [];\n  var map = {};\n  this._dir = dir;\n  fs.readdirSync(dir).forEach(function (name) {\n    if (NAME_RE.test(name)) {\n      var item = {\n        name: name,\n        data: readFileSafe(path.join(dir, name)) || ''\n      };\n      map[name] = item;\n      list.push(item);\n    }\n  });\n  list.sort(function (a, b) {\n    a = a.name;\n    b = b.name;\n    a = a.substring(0, a.indexOf('.'));\n    b = b.substring(0, a.indexOf('.'));\n    var aLen = a.length;\n    var bLen = b.length;\n    return aLen > bLen || (aLen === bLen && a > b) ? -1 : 1;\n  });\n  if (list.length > MAX_COUNT) {\n    list.slice(MAX_COUNT).forEach(function (item) {\n      try {\n        delete map[item.name];\n        fs.unlinkSync(path.join(dir, name));\n      } catch (e) {}\n    });\n    list = list.slice(0, MAX_COUNT);\n  }\n  this._list = list;\n  this._map = map;\n}\n\nvar proto = RecycleBin.prototype;\n\nproto.recycle = function (filename, data) {\n  if (!filename) {\n    return;\n  }\n  try {\n    var name = Date.now() + '.' + encodeURIComponent(filename);\n    data = data ? data + '' : '';\n    var item = {\n      name: name,\n      data: data\n    };\n    this._list.unshift(item);\n    this._map[name] = item;\n    fs.writeFile(path.join(this._dir, name), data, noop);\n  } catch (e) {\n    logger.error(e);\n  }\n};\n\nproto.recover = function (name) {\n  var item = this._map[name];\n  if (item) {\n    delete this._map[name];\n    this._list.splice(this._list.indexOf(item), 1);\n    fs.unlink(path.join(this._dir, name), noop);\n  }\n  return item;\n};\n\nproto.remove = proto.recover;\n\nproto.list = function () {\n  return this._list.map(getName);\n};\n\nproto.getFile = function (name) {\n  return this._map[name];\n};\n\nmodule.exports = RecycleBin;\n"
  },
  {
    "path": "lib/rules/rules.js",
    "content": "var parseUrl = require('../util/parse-url-safe');\nvar net = require('net');\nvar extend = require('extend');\nvar crypto = require('crypto');\nvar util = require('../util');\nvar rulesUtil = require('./util');\nvar lookup = require('./dns');\nvar protoMgr = require('./protocols');\nvar config = require('../config');\n\nvar rules = rulesUtil.rules;\nvar values = rulesUtil.values;\nvar env = process.env || {};\nvar allowDnsCache = true;\nvar SUB_MATCH_RE = /\\$[&\\d]/;\nvar BODY_MATCH_RE = /\\$b[&\\d]/;\nvar SPACE_RE = /\\s+/g;\nvar EXACT_RE = /^\\$/;\nvar NON_RE = /^!/;\nvar HAS_SPACE_RE = /\\s/;\nvar MULTI_TO_ONE_RE = /^\\s*line`\\s*[\\r\\n]([\\s\\S]*?)[\\r\\n]\\s*`\\s*?$/gm;\nvar WEB_PROTOCOL_RE = /^(?:https?|wss?|tunnel):\\/\\//;\nvar PORT_RE = /^(x?hosts?:\\/\\/)(?:([\\w.-]*)|\\[([:\\da-f.]+)\\])(?::(\\d+))?$/i;\nvar PLUGIN_RE = /^(?:plugin|whistle)\\.([a-z\\d_\\-]+:\\/\\/[\\s\\S]*)/;\nvar PLUGIN_TPL_RE = /(^|\\s)?(%[a-z\\d_-]+(?:\\.[^\\s=]+)?=|(?:whistle\\.)?[a-z\\d_-]+:\\/\\/)([^\\s]*)/g;\nvar ROOT_PLUGIN_RE = /[\\\\/]whistle\\.([a-z\\d_-]+)$/;\nvar TPL_KEY_RE = /\\{\\{\\s*%?ruleValue\\s*\\}\\}/g;\nvar END_RE = /\\$$/;\nvar protocols = protoMgr.protocols;\nvar protocolsWithoutG = protoMgr.protocolsWithoutG;\nvar reqProtocols = protoMgr.reqProtocols;\nvar reqProtosWithoutG = protoMgr.reqProtosWithoutG;\nvar pureResProtocols = protoMgr.pureResProtocols;\nvar multiMatchs = protoMgr.multiMatchs;\nvar aliasProtocols = protoMgr.aliasProtocols;\nvar FILE_RE = /^(?:[a-z]:(?:\\\\|\\/[^/])|\\/[^/])/i;\nvar PROXY_RE =\n  /^x?(socks|proxy|https?-proxy|internal-proxy|internal-https?-proxy|https2http-proxy|http2https-proxy):\\/\\//;\nvar VAR_RE = /\\${([^{}]+)}/g;\nvar NO_SCHEMA_RE = /^\\/\\/[^/]/;\nvar WILDCARD_RE = /^(\\$?((?:[a-z*]+):\\/\\/)?([^/?]*))/;\nvar RULE_KEY_RE = /^\\$\\{(\\S+)\\}$/;\nvar VALUE_KEY_RE = /^\\{(\\S+)\\}$/;\nvar LINE_END_RE = /\\n|\\r\\n|\\r/;\nvar LOCAL_RULE_RE =\n  /^https?:\\/\\/local\\.(?:whistlejs\\.com|wproxy\\.org)(:realPort)?(?:\\/|\\?|$)/;\nvar PATH_RE = /^<.*>$/;\nvar VALUE_RE = /^\\(.*\\)$/;\nvar REG_URL_RE = /^((?:[a-z*]+:)?\\/\\/)?([^/?]*)/;\nvar LIKE_REG_URL_RE = /^(?:(?:(?:https?|wss?|tunnel):)?\\/\\/)?\\*+\\/[^?*]*\\*/;\nvar LIKE_REG_URL_RE2 = /^(?:(?:(?:https?|wss?|tunnel):)?\\/\\/)?\\.[^./?]+\\.[^/?]+\\/[^?*]*\\*/;\nvar DOT_DOMAIN_RE = /^\\.[^./?]+\\.[^/?]/;\nvar REG_URL_SYMBOL_RE = /^(\\^+)/;\nvar PATTERN_FILTER_RE = /^(?:filter|ignore):\\/\\/(.+)\\/(i)?$/;\nvar LINE_PROPS_RE = /^lineProps:\\/\\/(.*)$/;\nvar FILTER_RE = /^(?:excludeFilter|includeFilter):\\/\\/(.*)$/;\nvar PROPS_FILTER_RE =\n  /^(?:filter|excludeFilter|includeFilter|ignore):\\/\\/(m(?:ethod)?|i(?:p)?|h(?:eader)?|env|s(?:tatusCode)?|from|b(?:ody)?|clientIp|clientIP|clientPort|remoteAddress|remotePort|serverIp|serverIP|serverPort|chance|probability|re[qs](?:H(?:eaders?)?)?):(.+)$/;\nvar PURE_FILTER_RE =\n  /^(?:excludeFilter|includeFilter):\\/\\/(statusCode|from|env|clientIp|clientIP|clientPort|remoteAddress|remotePort|serverIp|serverIP|chance|probability|serverPort|host|re[qs](?:H(?:eaders?)?)?)[.=](.+)$/;\nvar PATTERN_WILD_FILTER_RE = /^(?:filter|ignore):\\/\\/(!)?(\\*+\\/)/;\nvar INLINE_RE = /^((?:filter|excludeFilter|includeFilter|ignore):\\/\\/)(\\(.*\\)|\\<.*\\>)$/;\nvar CHROME_PATH_RE = /^file:\\/\\/\\/[A-Z]:\\//;\nvar AT_RE = /^@/;\nvar WILD_FILTER_RE = /^(\\*+\\/)/;\nvar regUrlCache = {};\nvar hostCache = {};\nvar NON_STAR_RE = /[^*]/;\nvar DOMAIN_STAR_RE = /([*~]+)(\\\\.)?/g;\nvar STAR_RE = /\\*+/g;\nvar PORT_PATTERN_RE = /^!?:\\d{1,5}$/;\nvar COMMENT_RE = /#[^\\r\\n]*/g;\nvar TPL_RE = /^((?:[\\w.-]+:)?\\/\\/)?(`.*`)$/;\n// url:  protocol, host, port, hostname, search, query, pathname, path, href, query.key\n// req|res: ip, method, statusCode, headers?.key, cookies?.key\nvar PLUGIN_NAME_RE =\n  /^[a-z\\d_\\-]+(?:\\.g?(?:all)?var(?:\\$|\\d*))?(?:\\.replace\\(.+\\))?$/i;\nvar VAR_INDEX_RE = /^([a-z\\d_\\-]+)\\.(g)?(all)?var(\\$|\\d*)/i;\nvar TPL_VAR_RE =\n  /(\\$)?\\$\\{(\\{)?(id|reqId|whistle|env|now|random(?:Int\\(\\d{1,15}(?:-\\d{1,15})?\\))?|randomUUID|host|port|realPort|realHost|realUrl|version|url|hostname|query|search|queryString|searchString|path|pathname|clientId|localClientId|ip|clientIp|clientPort|remoteAddress|remotePort|serverIp|serverPort|method|status(?:Code)|reqCookies?|resCookies?|re[qs]H(?:eaders?)?)(?:\\.([^{}]+))?\\}(\\})?/gi;\nvar RANDOM_RE = /randomInt\\((\\d{1,15})(?:-(\\d{1,15}))?\\)/i;\nvar REPLACE_PATTERN_RE = /(^|\\.)replace\\((.+)\\)$/i;\nvar SEP_RE = /^[?/]/;\nvar COMMA1_RE = /\\\\,/g;\nvar COMMA2_RE = /\\\\\\\\,/g;\nvar G_CR_RE = /\\r/g;\nvar G_LF_RE = /\\n/g;\nvar SUFFIX_RE = /^\\\\\\.[\\w-]+$/;\nvar DOT_PATTERN_RE = /^\\.[\\w-]+(?:[?$]|$)/;\nvar inlineValues = {};\nvar pluginMgr;\nvar ENABLE_PROXY_RE =\n  /\\bproxy(?:Host|First|Tunnel)|clientId|multiClient|singleClient\\b/i;\nvar PLUGIN_VAR_RE = /^%([a-z\\d_\\-]+)([=.])([^\\s]*)/;\nvar EXACT_IGNORE_RE = /^ignore:\\/\\/(pattern|matcher|operator|operation)[=:](.+)$/;\nvar EXACT_SKIP_RE = /^(pattern|matcher|operator|operation)[=:](.+)$/;\nvar FILE_PROTO_RE = /^x?((raw)?file|tpl|jsonp|dust):\\/\\//;\nvar NO_PROTO_RE = /[^\\w!*|.-]/;\nvar SKIP_RE = /^skip:\\/\\//;\nvar SUB_VAR_RE = /\\$\\{RegExp\\.\\$([&\\d])\\}/g;\nvar mIndex = 0;\nvar mNow = Date.now();\nvar IS_JSON = Symbol('isJson');\n\nfunction getMFlag() {\n  if (mIndex === Number.MAX_SAFE_INTEGER) {\n    mIndex = 0;\n    mNow = Date.now();\n  }\n  return mNow + '-' + (mIndex++);\n}\n\nfunction removeComment(text) {\n  return text.replace(COMMENT_RE, '').trim();\n}\n\nfunction domainToRegExp(all, star, dot) {\n  var len = star.length;\n  var result = len > 1 ? '([^/?]*)' : '([^/?.]*)';\n  if (dot) {\n    result += '\\\\.';\n    if (len > 2) {\n      result = '(?:' + result + ')?';\n    }\n  }\n  return result;\n}\n\nfunction pathToRegExp(all) {\n  var len = all.length;\n  if (len > 2) {\n    return '(.*)';\n  }\n  return len > 1 ? '([^?]*)' : '([^?/]*)';\n}\n\nfunction queryToRegExp(all) {\n  return all.length > 1 ? '(.*)' : '([^&]*)';\n}\n\nfunction isRegUrl(url, isCheck) {\n  var result = regUrlCache[url];\n  if (result) {\n    return isCheck ? result : extend({}, result);\n  }\n  var oriUrl = url;\n  var not = isNegativePattern(url);\n  if (not) {\n    url = url.substring(1);\n  }\n  if (DOT_PATTERN_RE.test(url)) {\n    url = '^' + url;\n  }\n  var hasStartSymbol = REG_URL_SYMBOL_RE.test(url);\n  var hasEndSymbol, ignoreCase, startWithDot;\n  if (hasStartSymbol) {\n    ignoreCase = RegExp.$1.length;\n    url = url.substring(ignoreCase);\n    hasEndSymbol = END_RE.test(url);\n    if (hasEndSymbol) {\n      url = url.slice(0, -1);\n    }\n    ignoreCase = ignoreCase === 1;\n  } else {\n    startWithDot = LIKE_REG_URL_RE2.test(url);\n    if (startWithDot || LIKE_REG_URL_RE.test(url)) {\n      ignoreCase = hasStartSymbol = true;\n    }\n  }\n  if (!hasStartSymbol || !REG_URL_RE.test(url)) {\n    return false;\n  }\n  var protocol = RegExp.$1 || '';\n  var domain = RegExp.$2;\n  var pathname = url.substring(protocol.length + domain.length);\n  var query = '';\n  var index = pathname.indexOf('?');\n  if (index !== -1) {\n    query = pathname.substring(index);\n    pathname = pathname.substring(0, index);\n  }\n  if (!protocol || protocol === '//') {\n    protocol = '[a-z]+://';\n  } else {\n    protocol = util.escapeRegExp(protocol).replace(/\\*+/, '([a-z:]*)');\n  }\n  if (startWithDot) {\n    domain = domain.substring(1);\n  }\n  domain = util.escapeRegExp(domain);\n  if (domain.length > 2 && !NON_STAR_RE.test(domain)) {\n    domain = '([^?]*)';\n  } else if (domain) {\n    domain = domain.replace(DOMAIN_STAR_RE, domainToRegExp);\n  } else {\n    domain = '[^/?]*';\n  }\n  if (startWithDot) {\n    domain = '(?:[^/?.]*.)?' + domain;\n  }\n  if (pathname) {\n    pathname = util.escapeRegExp(pathname).replace(STAR_RE, pathToRegExp);\n  } else if (hasStartSymbol && SUFFIX_RE.test(domain)) {\n    pathname = '/[^?]+' + domain + (hasEndSymbol || query ? '' : '(?:\\\\??.*)$');\n    domain = '[^/?]+';\n  } else if (query || hasEndSymbol) {\n    pathname = '/';\n  }\n  query =\n    pathname +\n    (query ? util.escapeRegExp(query).replace(STAR_RE, queryToRegExp) : '');\n  var pattern = '^' + protocol + domain + query + (hasEndSymbol ? '$' : '');\n  try {\n    result = regUrlCache[oriUrl] = {\n      not: not,\n      pattern: new RegExp(pattern, ignoreCase ? 'i' : '')\n    };\n  } catch (e) {}\n  return result;\n}\n\nfunction formatShorthand(url) {\n  if (NO_SCHEMA_RE.test(url)) {\n    return url;\n  }\n\n  if (url === 'includeFilter://safeHtml') {\n    return 'lineProps://safeHtml';\n  }\n  if (url === 'includeFilter://strictHtml') {\n    return 'lineProps://strictHtml';\n  }\n\n  if (\n    url === '{}' ||\n    VALUE_KEY_RE.test(url) ||\n    PATH_RE.test(url) ||\n    VALUE_RE.test(url)\n  ) {\n    return 'file://' + url;\n  }\n\n  if (url === '/' || (FILE_RE.test(url) && !util.isRegExp(url))) {\n    return 'file://' + url;\n  }\n  // compact Chrome\n  if (CHROME_PATH_RE.test(url)) {\n    return 'file://' + url.substring(8);\n  }\n\n  if (AT_RE.test(url)) {\n    if (url.indexOf('@://') === -1) {\n      url = '@://' + url.substring(1);\n    }\n    return url.replace('@', 'G');\n  }\n\n  if (PLUGIN_VAR_RE.test(url)) {\n    return url.replace('%', 'P://');\n  }\n\n  return url;\n}\n\n\nfunction getKey(url) {\n  if (url.indexOf('{') == 0) {\n    var index = url.lastIndexOf('}');\n    return index > 1 && url.substring(1, index);\n  }\n\n  return false;\n}\n\nfunction getValue(url, start, end, lineProps) {\n  if (url.indexOf(start || '(') == 0) {\n    var len = url.length - 1;\n    if (url[len] === (end || ')')) {\n      return url.substring(1, len);\n    }\n  }\n  if (lineProps) {\n    if (lineProps[IS_JSON] == null) {\n      lineProps[IS_JSON] = util.isJson(url);\n    }\n    if (lineProps[IS_JSON]) {\n      return url;\n    }\n  }\n  return false;\n}\n\nfunction getFiles(path) {\n  return FILE_PROTO_RE.test(path) ? util.removeProtocol(path, true).split('|') : null;\n}\n\nfunction setProtocol(target, source) {\n  if (util.hasProtocol(target)) {\n    return target;\n  }\n\n  var protocol = util.getProtocol(source);\n  if (protocol == null) {\n    return target;\n  }\n\n  return protocol + (NO_SCHEMA_RE.test(target) ? '' : '//') + target;\n}\n\nfunction isPathSeparator(ch) {\n  return ch == '/' || ch == '\\\\' || ch == '?';\n}\n\n/**\n * query1: xxxx, xxxx?, xxx?xxxx\n * query2: ?xxx, xxx?xxxx\n * @param query1\n * @param query2\n * @returns\n */\nfunction joinQuery(query1, query2) {\n  if (!query1 || !query2) {\n    return query1 || query2;\n  }\n  query2 = query2.substring(1);\n  var firstLen = query1.length;\n  var secondLen = query2.length;\n  var sep =\n    firstLen < 2 ||\n    !secondLen ||\n    query1[firstLen - 1] === '&' ||\n    query2[0] === '&'\n      ? ''\n      : '&';\n  return query1 + sep + query2;\n}\nfunction joinUrl(a, b) {\n  if (!a || !b) {\n    return a + b;\n  }\n\n  var firstIndex = a.indexOf('?');\n  var secondIndex = b.indexOf('?');\n  var firstQuery = '';\n  var secondQuery = '';\n\n  if (firstIndex != -1) {\n    firstQuery = a.substring(firstIndex);\n    a = a.substring(0, firstIndex);\n  }\n\n  if (secondIndex != -1) {\n    secondQuery = b.substring(secondIndex);\n    b = b.substring(0, secondIndex);\n  }\n\n  if (b) {\n    var lastIndex = a.length - 1;\n    var startWithSep = isPathSeparator(b[0]);\n    if (isPathSeparator(a[lastIndex])) {\n      a = startWithSep\n        ? a.substring(0, lastIndex) + b\n        : a + b;\n    } else {\n      a = a + (startWithSep ? '' : '/') + b;\n    }\n  }\n  var query = joinQuery(firstQuery, secondQuery);\n  return WEB_PROTOCOL_RE.test(a) ? util.formatUrl(a + query) : a + query;\n}\n\nfunction toLine(_, line) {\n  return line.replace(SPACE_RE, ' ');\n}\n\nfunction mergeLines(text) {\n  return removeComment(text).replace(MULTI_TO_ONE_RE, toLine);\n}\n\nfunction parsePluginTpl(lines) {\n  var ruleTpls = pluginMgr && pluginMgr.ruleTpls;\n  if (!ruleTpls) {\n    return lines;\n  }\n  var result = [];\n  lines.forEach(function(line) {\n    line = removeComment(line);\n    var isPrivate = HAS_SPACE_RE.test(line);\n    line = line.replace(PLUGIN_TPL_RE, function(all, sep, key, value) {\n      var isVar = key.indexOf('=') !== -1;\n      if (isVar && isPrivate) {\n        key = '_' + key;\n      }\n      var tpl = ruleTpls[key.slice(0, isVar ? -1 : -3)];\n      if (tpl == null) {\n        return all;\n      }\n      return (sep || '') + mergeLines(tpl).replace(TPL_KEY_RE, function(key) {\n        return key.indexOf('%') === -1 ? value : util.encodeURIComponent(value);\n      });\n    });\n    result.push.apply(result, mergeLines(line).split(LINE_END_RE));\n  });\n  return result;\n}\n\nfunction getLines(text, root) {\n  if (!text || !(text = text.trim())) {\n    return [];\n  }\n  text = mergeLines(text);\n  var ruleKeys = {};\n  var valueKeys = {};\n  var lines = text.split(LINE_END_RE);\n  var result = [];\n  lines.forEach(function (line) {\n    line = line.trim();\n    if (!line) {\n      return;\n    }\n    var ruleList;\n    var isRuleKey = RULE_KEY_RE.test(line);\n    if (isRuleKey || VALUE_KEY_RE.test(line)) {\n      if (root) {\n        var key = RegExp.$1;\n        line = '';\n        if (isRuleKey) {\n          if (!ruleKeys[key]) {\n            ruleKeys[key] = 1;\n            line = rules.get(key);\n          }\n        } else if (!valueKeys[key]) {\n          valueKeys[key] = 1;\n          line = values.get(key);\n        }\n        ruleList = line && mergeLines(line).split(LINE_END_RE);\n      }\n    } else {\n      ruleList = [line];\n    }\n    if (ruleList) {\n      ruleList = parsePluginTpl(ruleList);\n      result.push.apply(result, ruleList);\n    }\n  });\n  return result;\n}\n\nfunction resolvePropValue(obj, key) {\n  return (key && obj && obj[key.toLowerCase()]) || '';\n}\n\nfunction resolveUrlVar(req, key, escape) {\n  var url = req.fullUrl || req.curUrl;\n  if (!key) {\n    return url;\n  }\n  var options = req.__options || req.options;\n  if (!options || options.href !== url) {\n    options = req.__options = util.parseUrl(url);\n    req.__query = req.__query$ = '';\n  }\n  if (key.indexOf('query.') !== 0 || !options.query) {\n    if (key === 'actualPort' || key === 'realPort') {\n      return options['port'] || (options.protocol === 'https:' || options.protocol === 'wss:' ? '443' : '80');\n    }\n    return options[key] || '';\n  }\n  var queryKey = '__query' + (escape ? '$' : '');\n  var query = req[queryKey];\n  if (!query) {\n    query = req[queryKey] = util.parseQuery(options.query, null, null, escape);\n  }\n  return util.getQueryValue(query[key.substring(6)]);\n}\nfunction resolveReqCookiesVar(req, key, escape) {\n  var cookie = req.headers.cookie || '';\n  if (!cookie || !key) {\n    return cookie;\n  }\n  var cookies = req.__cookies;\n  if (!cookies || req.__rawCookies !== cookie) {\n    req.__rawCookies = cookie;\n    cookies = req.__cookies = util.parseQuery(cookie, '; ', null, escape);\n  }\n  return util.getQueryValue(cookies[key]);\n}\nfunction resolveResCookiesVar(req, key) {\n  var resHeaders = req.resHeaders;\n  var cookie = resHeaders && resHeaders['set-cookie'];\n  var isArray = Array.isArray(cookie);\n  if (!isArray && cookie) {\n    isArray = true;\n    cookie = [String(cookie)];\n  }\n  if (!isArray) {\n    return cookie || '';\n  }\n  var rawCookie = cookie.join(', ');\n  if (!key || !rawCookie) {\n    return rawCookie;\n  }\n  var cookies = req.__resCookies;\n  if (!cookies || req.__rawResCookies !== rawCookie) {\n    req.__rawResCookies = cookie.join();\n    cookies = req.__resCookies = {};\n    cookie.forEach(function (c) {\n      c = util.parseQuery(c, '; ', null, escape);\n      Object.keys(c).forEach(function (key) {\n        var item = {};\n        switch (key.toLowerCase()) {\n        case 'domain':\n          item.domain = c[key];\n          break;\n        case 'path':\n          item.path = c[key];\n          break;\n        case 'expires':\n          item.expires = c[key];\n          break;\n        case 'max-age':\n          item.maxAge = item['max-age'] = item['Max-Age'] = c[key];\n          break;\n        case 'httponly':\n          item.httpOnly = true;\n          break;\n        case 'secure':\n          item.secure = true;\n          break;\n        case 'samesite':\n          item.samesite = item.sameSite = item.SameSite = c[key];\n          break;\n        case 'partitioned':\n          item.partitioned = true;\n          break;\n        default:\n          if (!cookies[key]) {\n            item.value = c[key];\n            cookies[key] = item;\n          }\n        }\n      });\n    });\n  }\n  var index = key.indexOf('.');\n  var name;\n  if (index !== -1) {\n    name = key.substring(index + 1);\n    key = key.substring(0, index);\n  }\n  cookie = cookies[key];\n  if (!cookie) {\n    return '';\n  }\n  return (name ? cookie[name] : cookie.value) || '';\n}\n\nfunction resolveServerIpVar(req, key) {\n  if (!req.resHeaders) {\n    return '';\n  }\n  return req.hostIp || '127.0.0.1';\n}\n\nfunction resolveResHeadersVar(req, key) {\n  return resolvePropValue(req.resHeaders, key);\n}\n\nfunction getPluginVar(vars, index) {\n  if (!vars) {\n    return '';\n  }\n  if (vars && index === '$') {\n    index = vars.length - 1;\n  }\n  return (vars && vars[index || 0]) || '';\n}\n\nfunction resolveRuleValue(req, key) {\n  var curRules = key && req.rules;\n  if (curRules) {\n    if (VAR_INDEX_RE.test(key)) {\n      var shortName = RegExp.$1;\n      var isGlobal = RegExp.$2;\n      var isAll = RegExp.$3;\n      var index = RegExp.$4 || 0;\n      var gVars = req._globalPluginVars && req._globalPluginVars[shortName];\n      var vars = req._pluginVars && req._pluginVars[shortName];\n      if (isAll) {\n        if (vars && gVars) {\n          vars = isGlobal ? gVars.concat(vars) : vars.concat(gVars);\n        }\n        return getPluginVar(vars || gVars, index);\n      }\n      return getPluginVar(isGlobal ? gVars : vars, index);\n    }\n    var plugin = curRules.plugin;\n    var matcher;\n    key = key + '://';\n    if (plugin) {\n      var list = Array.isArray(plugin.list) ? plugin.list : [plugin];\n      var name = 'whistle.' + key;\n      for (var i = 0, len = list.length; i < len; i++) {\n        matcher = list[i].matcher;\n        if (!matcher.indexOf(name)) {\n          return matcher.substring(name.length);\n        }\n      }\n    }\n    matcher = curRules.rule && curRules.rule.matcher;\n    if (matcher && !matcher.indexOf(key)) {\n      return matcher.substring(key.length);\n    }\n  }\n  return '';\n}\n\nfunction resolveVarValue(req, escape, name, key) {\n  var lname = name.toLowerCase();\n  if (RANDOM_RE.test(lname)) {\n    var min = 0;\n    var max = 0;\n    if (RegExp.$2) {\n      min = parseInt(RegExp.$1, 10);\n      max = parseInt(RegExp.$2, 10);\n      if (max < min) {\n        var temp = max;\n        max = min;\n        min = temp;\n      }\n      max = max - min;\n    } else if (RegExp.$1) {\n      max = parseInt(RegExp.$1, 10);\n    }\n    return max && (Math.floor(Math.random() * (max + 1)) + min);\n  }\n  switch (lname) {\n  case 'now':\n    return Date.now();\n  case 'random':\n    return Math.random();\n  case 'randomuuid':\n    return crypto.randomUUID ? crypto.randomUUID() : '';\n  case 'id':\n  case 'reqid':\n    return req.reqId || '';\n  case 'whistle':\n    return resolveRuleValue(req, key);\n  case 'path':\n  case 'pathname':\n  case 'search':\n    return key ? '' : resolveUrlVar(req, lname, escape);\n  case 'querystring':\n  case 'searchstring':\n    return key ? '' : resolveUrlVar(req, 'search', escape) || '?';\n  case 'query':\n    key = key ? 'query.' + key : 'query';\n    return resolveUrlVar(req, key, escape);\n  case 'url':\n    return resolveUrlVar(req, key, escape);\n  case 'port':\n    return config.port;\n  case 'host':\n    return config.host || '';\n  case 'realport':\n    return config.realPort || config.port;\n  case 'realhost':\n    return config.realHost || config.host || '';\n  case 'realurl':\n    return req.realUrl && (req.realUrl !== req.fullUrl) ? req.realUrl : '';\n  case 'version':\n    return config.version;\n  case 'reqcookie':\n  case 'reqcookies':\n    return resolveReqCookiesVar(req, key, escape);\n  case 'rescookie':\n  case 'rescookies':\n    return resolveResCookiesVar(req, key, escape);\n  case 'method':\n    return req.method;\n  case 'ip':\n  case 'clientip':\n    return req.clientIp;\n  case 'clientid':\n    return req._origClientId || util.getClientId(req.headers);\n  case 'clientport':\n    return req.clientPort || '';\n  case 'localclientid':\n    return config.clientId;\n  case 'statuscode':\n  case 'status':\n    return req.statusCode || '';\n  case 'serverip':\n    return resolveServerIpVar(req, key);\n  case 'serverport':\n    return req.serverPort || '';\n  case 'reqh':\n  case 'reqheader':\n  case 'reqheaders':\n    return resolvePropValue(req.headers, key);\n  case 'hostname':\n    return util.hostname();\n  case 'remoteaddress':\n    return req._remoteAddr || '';\n  case 'remoteport':\n    return req._remotePort || '0';\n  case 'env':\n    return (key && env[key]) || '';\n  default:\n    return resolveResHeadersVar(req, key);\n  }\n}\n\nfunction resetComma(str) {\n  return str && str.replace(G_CR_RE, ',').replace(G_LF_RE, '\\\\,');\n}\n\nfunction resolveTplVar(value, req) {\n  return value.replace(TPL_VAR_RE, function (all, escape, lb, name, key, rb) {\n    if (\n      (lb && !rb) ||\n      (name === 'whistle' && (!key || !PLUGIN_NAME_RE.test(key)))\n    ) {\n      return all;\n    }\n    var pattern, regPattern;\n    var replacement = '';\n    if (REPLACE_PATTERN_RE.test(key)) {\n      pattern = RegExp.$2;\n      var dot = RegExp.$1 || '';\n      key = key.substring(0, key.length - 9 - dot.length - pattern.length);\n      if (pattern.indexOf(',') !== -1) {\n        pattern = pattern.replace(COMMA2_RE, '\\n').replace(COMMA1_RE, '\\r');\n        var index = pattern.indexOf(',');\n        if (index !== -1) {\n          replacement = resetComma(pattern.substring(index + 1));\n          pattern = pattern.substring(0, index);\n        }\n        pattern = resetComma(pattern);\n      }\n      regPattern = util.toOriginalRegExp(pattern);\n    }\n    var val = resolveVarValue(req, escape, name, key);\n    if (typeof val !== 'string') {\n      val = val == null ? '' : val + '';\n    }\n    if (!val || !pattern) {\n      val = pattern ? val : val || replacement;\n    } else if (!regPattern || !SUB_MATCH_RE.test(replacement)) {\n      val = val.replace(regPattern || pattern, replacement);\n    } else {\n      val = val.replace(regPattern, function () {\n        return util.replacePattern(replacement, arguments);\n      });\n    }\n    if (val && lb) {\n      val = util.encodeURIComponent(val);\n    }\n    return val + (!lb && rb ? '}' : '');\n  });\n}\n\nRules.resolveTplVar = resolveTplVar;\n\nfunction renderTpl(rule, req) {\n  var matcher = rule.matcher;\n  if (rule.isTpl === false) {\n    return matcher;\n  }\n  rule.isTpl = false;\n  return matcher.replace(TPL_RE, function (_, proto, value) {\n    rule.isTpl = true;\n    return (proto || '') + resolveTplVar(value.slice(1, -1), req);\n  });\n}\n\nfunction resolveVar(rule, vals, req) {\n  var matcher = renderTpl(rule, req);\n  return matcher.replace(VAR_RE, function (all, key) {\n    key = getValueFor(key, vals, rule.file);\n    if (typeof key === 'string') {\n      return rule.isTpl && key ? resolveTplVar(key, req) : key;\n    }\n    return all;\n  });\n}\n\nfunction getValueFor(key, vals, file) {\n  if (!key) {\n    return;\n  }\n  var key1 = util.getInlineKey(key, file);\n  var val = vals ? vals[key1] : undefined;\n  if (val !== undefined) {\n    val = vals[key1] = val && typeof val == 'object' ? JSON.stringify(val) : val;\n  } else {\n    val = values.get(key);\n  }\n  return val;\n}\n\nfunction getRule(req, list, vals, index, isFilter, host) {\n  var rule = resolveRuleList(req, list, vals, index || 0, isFilter, null, host);\n  resolveValue(rule, vals, req);\n  return rule;\n}\n\nfunction getRuleList(req, list, vals, isEnableProxy) {\n  return resolveRuleList(req, list, vals, isEnableProxy).map(function (rule) {\n    return resolveValue(rule, vals, req);\n  });\n}\n\nfunction resolveValue(rule, vals, req) {\n  if (!rule) {\n    return;\n  }\n\n  var matcher = rule.matcher;\n  var index = matcher.indexOf('://') + 3;\n  var protocol = matcher.substring(0, index);\n  var regExp = rule.regExp;\n  delete rule.regExp;\n  matcher = matcher.substring(index);\n  var key = getKey(matcher);\n  if (key) {\n    rule.key = key;\n  }\n  var value = getValueFor(key, vals, rule.file);\n  if (value == null) {\n    value = getValue(matcher, null, null, rule.lineProps);\n    regExp = null;\n  }\n  if (value !== false) {\n    var val = setProtocol(protocol + value, req.curUrl);\n    if (rule.isTpl && regExp) {\n      val = val.replace(SUB_VAR_RE, function(_, index) {\n        index = index === '&' ? 0 : index;\n        return regExp[index] || '';\n      });\n    }\n    if (rule.isTpl) {\n      val = resolveTplVar(val, req);\n    }\n    if (protocol === 'style://') {\n      rule.value = val.substring(0, 128);\n    } else {\n      Object.defineProperty(rule, 'value', { value: val });\n    }\n  } else if (!key && (value = getValue(matcher, '<', '>')) !== false) {\n    rule.path = setProtocol(protocol + value, req.curUrl);\n    rule.files = getFiles(rule.path);\n  }\n  return rule;\n}\n\nfunction getRelativePath(pattern, url, matcher) {\n  var index = url.indexOf('?');\n  if (index === -1 || pattern.indexOf('?') !== -1) {\n    return '';\n  }\n  if (matcher.indexOf('?') === -1) {\n    return url.substring(index);\n  }\n  url = url.substring(index + 1);\n  return (url && '&') + url;\n}\n\nfunction removeFilters(rule) {\n  var filters = rule.filters;\n  if (filters) {\n    if (filters.curFilter) {\n      rule.filter = filters.curFilter;\n    }\n    delete rule.filters;\n  }\n}\n\nfunction replaceSubMatcher(url, regExp, req) {\n  var vals = req._bodySubVals;\n  req._bodySubVals = undefined;\n  if ((!regExp || !SUB_MATCH_RE.test(url)) && (!vals || !BODY_MATCH_RE.test(url))) {\n    return url;\n  }\n  return util.replacePattern(url, regExp, vals);\n}\n\nvar PROTOS = {\n  http: 1,\n  https: 1,\n  tunnel: 1,\n  ws: 1,\n  wss: 1\n};\n\nfunction removePort(url) {\n  var index = url.indexOf('://');\n  if (index === -1) {\n    return url;\n  }\n  var protocol = url.substring(0, index);\n  if (!PROTOS[protocol]) {\n    return url;\n  }\n  index += 3;\n  var urlPath = '';\n  var end = url.indexOf('/', index);\n  if (end !== -1) {\n    urlPath = url.substring(end);\n    url = url.substring(0, end);\n  }\n  end = url.indexOf(':', index);\n  if (end !== -1) {\n    url = url.substring(0, end);\n  }\n  return url + urlPath;\n}\n\nfunction checkInternal(req, rule) {\n  var props = rule.lineProps;\n  if (req._isInternalReq) {\n    return !props.internal && !props.internalOnly;\n  }\n  return props.internalOnly;\n}\n\nfunction resolveRuleList(req, list, vals, index, isFilter, isEnableProxy, host) {\n  var curUrl = util.formatUrl(req.curUrl);\n  var notHttp = list.isRuleProto && curUrl[0] !== 'h';\n  //支持域名匹配\n  var domainUrl = removePort(curUrl);\n  var hasIndex = typeof index === 'number';\n  index = hasIndex ? index : -1;\n  var results = [];\n  var url = util.getPureUrl(curUrl);\n  var _domainUrl = util.getPureUrl(domainUrl);\n  var rule, matchedUrl, files, matcher, result, origMatcher, filePath;\n  var setMatcher = function(matcher) {\n    result.matcher = matcher;\n    var _matcher = rule ? rule.matcher : undefined;\n    if (_matcher !== matcher) {\n      result._matcher = _matcher;\n    }\n  };\n  var getPathRule = function () {\n    result = extend(\n      {\n        files: files,\n        url: joinUrl(matcher, filePath)\n      },\n      rule\n    );\n    if (files && filePath) {\n      result.files = files.map(function (file) {\n        return joinUrl(file, filePath);\n      });\n      result.rawFiles = files;\n    }\n    setMatcher(origMatcher);\n    removeFilters(result);\n    if (hasIndex) {\n      return result;\n    }\n    results.push(result);\n  };\n  var getExactRule = function (relPath, regObj) {\n    origMatcher = resolveVar(rule, vals, req);\n    origMatcher = replaceSubMatcher(origMatcher, regObj, req);\n    matcher = setProtocol(origMatcher, curUrl);\n    result = extend(\n      {\n        files: getFiles(matcher),\n        url: matcher + relPath\n      },\n      rule\n    );\n    setMatcher(origMatcher);\n    removeFilters(result);\n    if (hasIndex) {\n      return result;\n    }\n    results.push(result);\n  };\n  var checkFilter = function () {\n    req._bodySubVals = undefined;\n    if (notHttp && protoMgr.isFileProxy(rule.matcher)) {\n      return false;\n    }\n    return (isFilter || !matchExcludeFilters(curUrl, rule, req)) && (host == null || util.checkProxyHost(rule, host));\n  };\n\n  for (var i = 0; (rule = list[i]); i++) {\n    if ((isEnableProxy && !ENABLE_PROXY_RE.test(rule.matcher)) || checkInternal(req, rule) ||\n      (req._skipProps && (util.exactIgnore(req._skipProps, rule) || util.checkSkip(req._skipProps, rule, curUrl)))) {\n      continue;\n    }\n    var pattern = rule.isRegExp\n      ? rule.pattern\n      : setProtocol(rule.pattern, curUrl);\n    var not = rule.not;\n    var matchedRes;\n    if (rule.isRegExp) {\n      matchedRes = pattern.test(curUrl);\n      matchedRes = not ? !matchedRes : matchedRes;\n      var regExp;\n      if (matchedRes) {\n        regExp = {};\n        if (!not) {\n          for (var j = 1; j < 10; j++) {\n            regExp[j] = RegExp['$' + j];\n          }\n        }\n      }\n      if (matchedRes && checkFilter() && --index < 0) {\n        regExp['0'] = curUrl;\n        matcher = resolveVar(rule, vals, req);\n        // 支持 $x 包含 `|` 的情形\n        matcher = setProtocol(replaceSubMatcher(matcher, regExp, req), curUrl);\n        files = getFiles(matcher);\n        result = extend({ url: matcher, files: files }, rule);\n        setMatcher(matcher);\n        result.regExp = regExp;\n        removeFilters(result);\n        if (hasIndex) {\n          return result;\n        }\n        results.push(result);\n      }\n    } else if (rule.wildcard) {\n      var wildcard = rule.wildcard;\n      var matched = wildcard.preMatch.exec(curUrl);\n      if (matched && checkFilter()) {\n        var regObj = {};\n        for (var k = 0; k < 9; k++) {\n          regObj[k] = matched[k + 1] || '';\n        }\n        filePath = curUrl.substring(regObj[0].length);\n        var wPath = wildcard.path;\n        if (wildcard.isExact) {\n          if (\n            (filePath === wPath || util.getPureUrl(filePath) === wPath) &&\n            --index < 0\n          ) {\n            if (\n              (result = getExactRule(\n                getRelativePath(wPath, filePath, rule.matcher),\n                regObj\n              ))\n            ) {\n              return result;\n            }\n          }\n        } else if (filePath.indexOf(wPath) === 0) {\n          var wpLen = wPath.length;\n          filePath = filePath.substring(wpLen);\n          if (\n            (wildcard.hasQuery ||\n              !filePath ||\n              wPath[wpLen - 1] === '/' ||\n              SEP_RE.test(filePath)) &&\n            --index < 0\n          ) {\n            origMatcher = resolveVar(rule, vals, req);\n            origMatcher = replaceSubMatcher(origMatcher, regObj, req);\n            matcher = setProtocol(origMatcher, curUrl);\n            files = getFiles(matcher);\n            if (wildcard.hasQuery && filePath) {\n              filePath = '?' + filePath;\n            }\n            if ((result = getPathRule())) {\n              return result;\n            }\n          }\n        }\n      }\n    } else if (rule.isExact) {\n      matchedRes = pattern === url || pattern === curUrl;\n      if ((not ? !matchedRes : matchedRes) && checkFilter() && --index < 0) {\n        if (\n          (result = getExactRule(\n            getRelativePath(pattern, curUrl, rule.matcher)\n          ))\n        ) {\n          return result;\n        }\n      }\n    } else if (\n      ((matchedUrl = curUrl.indexOf(pattern) === 0) ||\n        (rule.isDomain && domainUrl.indexOf(pattern) === 0)) &&\n      checkFilter()\n    ) {\n      var len = pattern.length;\n      origMatcher = resolveVar(rule, vals, req);\n      origMatcher = replaceSubMatcher(origMatcher, null, req);\n      matcher = setProtocol(origMatcher, curUrl);\n      files = getFiles(matcher);\n      var hasQuery = pattern.indexOf('?') !== -1;\n      if (\n        (hasQuery ||\n          (matchedUrl\n            ? pattern == url || isPathSeparator(url[len])\n            : pattern == _domainUrl || isPathSeparator(_domainUrl[len])) ||\n          isPathSeparator(pattern[len - 1])) &&\n        --index < 0\n      ) {\n        filePath = (matchedUrl ? curUrl : domainUrl).substring(len);\n        if (hasQuery) {\n          if (filePath) {\n            filePath = '?' + filePath;\n          }\n        } else if (rule.isDomain && rule.lineProps.originUrl) {\n          filePath = '/';\n        }\n        if ((result = getPathRule())) {\n          return result;\n        }\n      }\n    }\n  }\n\n  return hasIndex ? null : results;\n}\n\nfunction resolveProps(req, rules, vals, isIgnore) {\n  var list = this.getRuleList(req, rules, vals);\n  var result = {};\n  if (isIgnore) {\n    list = list.filter(function(rule) {\n      var matcher = rule.matcher;\n      if (SKIP_RE.test(matcher)) {\n        matcher = matcher.slice(7);\n        if (!matcher) {\n          return false;\n        }\n        if (EXACT_SKIP_RE.test(matcher) || NO_PROTO_RE.test(matcher)) {\n          var prop ='ignore|' + (RegExp.$1 === 'pattern' ? 'pattern' : 'matcher') + '=' + (RegExp.$2 || matcher);\n          req._skipProps = req._skipProps || {};\n          result[prop] = true;\n          req._skipProps[prop] = true;\n          return false;\n        }\n        matcher.split('|').forEach(function(name) {\n          if (name) {\n            req._skipProps = req._skipProps || {};\n            req._skipProps[name] = true;\n          }\n        });\n      } else if (EXACT_IGNORE_RE.test(matcher)) {\n        result['ignore|' + (RegExp.$1 === 'pattern' ? 'pattern' : 'matcher') + '=' + RegExp.$2] = true;\n        return false;\n      }\n      return true;\n    });\n    if (!list.length) {\n      return result;\n    }\n  }\n  return util.resolveProperties(list, result);\n}\n\nfunction parseWildcard(pattern, not) {\n  if (!WILDCARD_RE.test(pattern)) {\n    return;\n  }\n  var preMatch = RegExp.$1;\n  var protocol = RegExp.$2;\n  var domain = RegExp.$3;\n  var startWithDot = DOT_DOMAIN_RE.test(domain);\n  if (\n    !startWithDot &&\n    protocol.indexOf('*') === -1 &&\n    domain.indexOf('*') === -1 &&\n    domain.indexOf('~') === -1\n  ) {\n    return;\n  }\n  if (not) {\n    return false;\n  }\n  var restPath = pattern.substring(preMatch.length);\n  var path = restPath || '/';\n  var isExact = preMatch.indexOf('$') === 0;\n  if (isExact) {\n    preMatch = preMatch.substring(1);\n  }\n  var index = path.indexOf('?');\n  var hasQuery = index !== -1;\n  if (hasQuery && index === 0) {\n    path = '/' + path;\n  }\n  var dLen = domain.length;\n  var allowMatchPath = dLen > 2 && !NON_STAR_RE.test(domain);\n  if (allowMatchPath) {\n    preMatch = '[^?]*';\n  } else {\n    if (\n      !startWithDot &&\n      (domain === '*' || domain === '~') &&\n      path.charAt(0) === '/'\n    ) {\n      preMatch += '*';\n    }\n    preMatch = util\n      .escapeRegExp(preMatch)\n      .replace(DOMAIN_STAR_RE, domainToRegExp);\n    if (dLen && domain[dLen - 1] !== '*' && domain.indexOf(':') === -1) {\n      preMatch += '(?::\\\\d+)?';\n    }\n    if (startWithDot) {\n      preMatch = preMatch.replace('\\\\.', '(?:[^/?.]*\\\\.)?');\n    }\n  }\n  if (!protocol) {\n    preMatch = '[a-z]+://' + preMatch;\n  } else if (protocol === '//') {\n    preMatch = '[a-z]+:' + preMatch;\n  }\n  preMatch =\n    '^(' + preMatch + (restPath ? '' : '[^/?]*') + ')' + (allowMatchPath ? util.escapeRegExp(path, true) : '');\n\n  return {\n    preMatch: new RegExp(preMatch),\n    path: path,\n    hasQuery: hasQuery,\n    isExact: isExact\n  };\n}\nfunction parseRule(rulesMgr, pattern, matcher, raw, root, options, file) {\n  if (isNegativePattern(matcher)) {\n    return;\n  }\n  var regUrl = regUrlCache[pattern];\n  var rawPattern = pattern;\n  var rawMatcher = matcher;\n  var noSchema;\n  var isRegExp, not, port, protocol, isExact;\n  if (regUrl) {\n    not = regUrl.not;\n    isRegExp = true;\n    pattern = regUrl.pattern;\n  } else {\n    not = isNegativePattern(pattern);\n    // 位置不能变\n    var isPortPattern = PORT_PATTERN_RE.test(pattern);\n    if (not) {\n      pattern = pattern.substring(1);\n    }\n    if (NO_SCHEMA_RE.test(pattern)) {\n      noSchema = true;\n      pattern = pattern.substring(2);\n    }\n    if (!pattern) {\n      return;\n    }\n    if (isPortPattern) {\n      isRegExp = true;\n      pattern = new RegExp('^[\\\\w]+://[^/?]+' + pattern + '/');\n    }\n    if (\n      !isRegExp &&\n      (isRegExp = util.isRegExp(pattern)) &&\n      !(pattern = util.toRegExp(pattern))\n    ) {\n      return;\n    }\n    if (!isRegExp) {\n      var wildcard = parseWildcard(pattern, not);\n      if (wildcard === false) {\n        return;\n      }\n      if (!wildcard && isExactPattern(pattern)) {\n        isExact = true;\n        pattern = pattern.slice(1);\n      } else if (not) {\n        return;\n      }\n    }\n  }\n  var proxyName, isRules, isSpec;\n  if (isHost(matcher)) {\n    matcher = 'host://' + matcher;\n    protocol = 'host';\n  } else if (matcher[0] === '/') {\n    if (matcher[1] === '/') {\n      protocol = 'rule';\n    } else {\n      matcher = 'file://' + matcher;\n      protocol = 'file';\n    }\n  } else if (PLUGIN_RE.test(matcher)) {\n    protocol = 'plugin';\n  } else if (PROXY_RE.test(matcher)) {\n    proxyName = RegExp.$1;\n    protocol = 'proxy';\n  } else {\n    var index = matcher.indexOf('://');\n    var origProto;\n    if (index !== -1) {\n      origProto = matcher.substring(0, index);\n      protocol = aliasProtocols[origProto];\n    }\n    if (!protocol) {\n      protocol = origProto;\n      if (matcher === 'host://') {\n        matcher = 'host://127.0.0.1';\n      }\n    } else if (protocol && (origProto === 'reqRules' || origProto === 'resRules')) {\n      isRules = true;\n    }\n    var isStatus = protocol === 'statusCode';\n    if (isStatus || protocol === 'redirect') {\n      isSpec = isStatus ? 1 : 2;\n    }\n  }\n  var rules = rulesMgr._rules;\n  var list =\n    protocol === 'sniCallback' ? rulesMgr._sniCallback : rules[protocol];\n  var useRealPort;\n  if (!list) {\n    protocol = 'rule';\n    list = LOCAL_RULE_RE.test(matcher) ? rules._localRule : rules.rule;\n    useRealPort = RegExp.$1;\n  } else if (!matcher.indexOf('G://clientCert://')) {\n    list = rules._clientCerts;\n  } else if (protocol == 'host') {\n    var protoIndex = matcher.indexOf(':') + 3;\n    var realProto = matcher.substring(0, protoIndex);\n    var opts = isHost(matcher.substring(protoIndex));\n    if (opts) {\n      matcher = realProto + opts.host;\n      port = opts.port;\n    }\n  }\n  var rule = {\n    not: not,\n    file: file,\n    isRules: isRules,\n    isSpec: isSpec,\n    name: protocol,\n    root: root,\n    wildcard: wildcard,\n    isRegExp: isRegExp,\n    isExact: isExact,\n    protocol: isRegExp ? null : util.getProtocol(pattern),\n    pattern: isRegExp ? pattern : util.formatUrl(pattern),\n    matcher: matcher,\n    port: port,\n    raw: raw,\n    isDomain:\n      !isRegExp &&\n      !not &&\n      (noSchema ? pattern : util.removeProtocol(rawPattern, true)).replace(/\\?.*$/, '').indexOf(\n        '/'\n      ) == -1,\n    rawPattern: rawPattern,\n    rawMatcher: rawMatcher,\n    filters: options.filters,\n    lineProps: options.lineProps,\n    hostFilter: options.hostFilter\n  };\n  if (options.rawProps.length) {\n    rule.rawProps = options.rawProps;\n  }\n  if (protocol === 'log' || protocol === 'weinre') {\n    rule.isTpl = false;\n  }\n  if (useRealPort) {\n    rule.realPort = config.realPort;\n    rule.matcher = rule.matcher.replace(\n      'realPort',\n      config.realPort || config.port\n    );\n  }\n  if (proxyName) {\n    switch (proxyName) {\n    case 'socks':\n      rule.isSocks = true;\n      break;\n    case 'https-proxy':\n      rule.isHttps = true;\n      break;\n    case 'internal-http-proxy':\n    case 'https2http-proxy':\n    case 'internal-proxy':\n      rule.isInternal = true;\n      break;\n    case 'internal-https-proxy':\n      rule.isInternal = true;\n      rule.isHttps = true;\n      break;\n    case 'http2https-proxy':\n      rule.isHttp2https = true;\n      break;\n    }\n  }\n  if (options.hasBodyFilter) {\n    rules._bodyFilters.push(rule);\n  }\n  if (util.isImportant(options)) {\n    for (var i = 0, len = list.length; i < len; i++) {\n      if (!util.isImportant(list[i])) {\n        return list.splice(i, 0, rule);\n      }\n    }\n  }\n  list.push(rule);\n}\n\nfunction isPattern(item) {\n  return (\n    PORT_PATTERN_RE.test(item) ||\n    isExactPattern(item) ||\n    isRegUrl(item, true) || // 缓存 regUrl\n    NO_SCHEMA_RE.test(item) ||\n    isNegativePattern(item) ||\n    WEB_PROTOCOL_RE.test(item) ||\n    util.isRegExp(item)\n  );\n}\n\nvar IP_WITH_PORT_RE = /^\\[([:\\da-f.]+)\\](?::(\\d+))?$/i;\nvar IPV4_RE = /^(?:::(?:ffff:)?)?(\\d+\\.[\\d.]+)(?:\\:(\\d+))?$/;\n\nfunction parseHost(item) {\n  var port;\n  if (IP_WITH_PORT_RE.test(item)) {\n    item = RegExp.$1;\n    port = RegExp.$2;\n  }\n\n  if (IPV4_RE.test(item)) {\n    port = port || RegExp.$2;\n    item = RegExp.$1;\n    if (!net.isIP(item)) {\n      return false;\n    }\n  } else if (!net.isIP(item)) {\n    return false;\n  }\n  return {\n    host: item,\n    port: port\n  };\n}\n\nfunction isHost(item) {\n  var result = hostCache[item];\n  if (result == null) {\n    result = parseHost(item);\n  }\n  hostCache[item] = result;\n  return result;\n}\n\nfunction indexOfPattern(list) {\n  var ipIndex = -1;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var item = list[i];\n    if (isPattern(item)) {\n      return i;\n    }\n\n    if (!util.hasProtocol(item)) {\n      if (!isHost(item)) {\n        return i;\n      } else if (ipIndex === -1) {\n        ipIndex = i;\n      }\n    }\n  }\n  return ipIndex;\n}\n\nfunction resolveFilterPattern(matcher) {\n  var not, isInclude, filter, caseIns, wildcard;\n  if (PATTERN_FILTER_RE.test(matcher)) {\n    filter = RegExp.$1;\n    caseIns = RegExp.$2;\n    not = filter[0] === '!';\n    if (not) {\n      filter = filter.substring(1);\n    }\n    if (filter[0] === '/') {\n      filter = filter.substring(1);\n    }\n    return filter\n      ? {\n        not: not,\n        filter: filter,\n        caseIns: caseIns\n      }\n      : false;\n  } else if (FILTER_RE.test(matcher)) {\n    filter = RegExp.$1;\n    if (!filter || filter === '!') {\n      return false;\n    }\n    isInclude = matcher[0] === 'i';\n    if (filter[0] === '!') {\n      not = !not;\n      filter = filter.substring(1);\n    }\n    if (util.isRegExp(filter)) {\n      filter = RegExp.$1;\n      caseIns = RegExp.$2;\n      return {\n        not: not,\n        isInclude: isInclude,\n        filter: filter,\n        caseIns: caseIns\n      };\n    }\n    if (filter[0] === '/' && filter[1] !== '/') {\n      wildcard = '/';\n    } else if (WILD_FILTER_RE.test(filter)) {\n      wildcard = RegExp.$1;\n    }\n  } else if (PATTERN_WILD_FILTER_RE.test(matcher)) {\n    not = RegExp.$1 || '';\n    wildcard = RegExp.$2;\n  } else {\n    return;\n  }\n  if (wildcard) {\n    matcher =\n      filter || matcher.substring(matcher.indexOf('://') + 3 + not.length);\n    var path = util.escapeRegExp(matcher.substring(wildcard.length));\n    if (path.indexOf('*') !== -1) {\n      path = path.replace(STAR_RE, pathToRegExp);\n    } else if (path && path[path.length - 1] !== '/') {\n      path += '(?:[/?]|$)';\n    }\n    return {\n      not: not,\n      isInclude: isInclude,\n      filter:\n        '^[a-z]+://' + (wildcard.length > 3 ? '[^?]' : '[^/?]') + '+/' + path\n    };\n  }\n  var result = isRegUrl('^' + filter);\n  if (result) {\n    result.not = not;\n    result.isInclude = isInclude;\n    return result;\n  }\n}\n\nfunction resolveMatchFilter(list) {\n  var matchers = [];\n  var lineProps = {};\n  var rawProps = [];\n  var filters, hasBodyFilter, hostFilter;\n  list.forEach(function (matcher) {\n    var rawMatcher = matcher;\n    if (INLINE_RE.test(matcher)) {\n      matcher = RegExp.$1 + RegExp.$2.slice(1, -1);\n    }\n    if (LINE_PROPS_RE.test(matcher)) {\n      rawProps.push(rawMatcher);\n      extend(lineProps, util.parseLineProps(matcher));\n      return;\n    }\n    var filter, not, isInclude, orgVal;\n    if (PROPS_FILTER_RE.test(matcher) || PURE_FILTER_RE.test(matcher)) {\n      var raw = RegExp['$&'];\n      var propName = RegExp.$1;\n      var value = RegExp.$2;\n      var isHostFilter = propName === 'host';\n      isInclude = matcher[1] === 'n';\n      if (value[0] === '!') {\n        not = !not;\n        value = value.substring(1);\n      }\n      var pattern;\n      var isIp = propName === 'i' || propName === 'ip';\n      var isClientPort, isServerPort, isClientIp, isServerIp;\n      if (!isIp) {\n        isClientPort = propName === 'clientPort';\n        if (!isClientPort) {\n          isServerPort = propName === 'serverPort';\n          if (!isServerPort) {\n            isClientIp = (propName === 'clientIp' || propName === 'clientIP');\n            isServerIp =\n              !isClientIp &&\n              (propName === 'serverIp' || propName === 'serverIP');\n          }\n        }\n      }\n      if (isClientPort || isServerPort) {\n        pattern = util.toRegExp(value);\n        if (isClientPort) {\n          propName = pattern ? 'cpPattern' : 'clientPort';\n        } else {\n          propName = pattern ? 'spPattern' : 'serverPort';\n        }\n        value = pattern || value.toLowerCase();\n      } else if (isIp || isClientIp || isServerIp) {\n        pattern = util.toRegExp(value);\n        if (!pattern && !net.isIP(value)) {\n          return;\n        }\n        if (isIp) {\n          propName = pattern ? 'iPattern' : 'ip';\n        } else if (isClientIp) {\n          propName = pattern ? 'clientPattern' : 'clientIp';\n        } else if (isServerIp) {\n          propName = pattern ? 'serverPattern' : 'serverIp';\n        }\n        value = pattern || value;\n      } else if (propName[0] === 'm') {\n        pattern = util.toRegExp(value, true);\n        propName = pattern ? 'mPattern' : 'method';\n        value = pattern || value.toUpperCase();\n      } else if (propName === 'from') {\n        pattern = null;\n        propName = 'from';\n        value = value.toLowerCase();\n      } else if (propName === 's' || propName === 'statusCode') {\n        pattern = util.toRegExp(value);\n        propName = pattern ? 'sPattern' : 'statusCode';\n        value = pattern || value.toLowerCase();\n      } else if (propName === 'b' || propName === 'body') {\n        hasBodyFilter = true;\n        pattern = util.toRegExp(value);\n        if (pattern) {\n          propName = 'bodyPattern';\n          value = pattern;\n        } else {\n          propName = 'body';\n          value = {\n            orgVal: util.encodeURIComponent(value).toLowerCase(),\n            value: value.toLowerCase()\n          };\n        }\n      } else if (propName === 'remoteAddress') {\n        pattern = util.toRegExp(value);\n        if (pattern) {\n          propName = 'addrPattern';\n        }\n        value = pattern || value.toLowerCase();\n      } else if (propName === 'remotePort') {\n        pattern = util.toRegExp(value);\n        if (pattern) {\n          propName = 'portPattern';\n        }\n        value = pattern || value.toLowerCase();\n      } else if (isHostFilter) {\n        pattern = util.toRegExp(value);\n        if (pattern) {\n          propName = 'hostPattern';\n        }\n        value = pattern || value.toLowerCase();\n      } else {\n        var isHeader = propName !== 'env' && propName !== 'chance' && propName !== 'probability';\n        var index = value.indexOf('=');\n        if (isHeader && index === -1) {\n          index = value.indexOf(':');\n        }\n        var key = index === -1 ? value : value.substring(0, index);\n        if (isHeader) {\n          key = key.toLowerCase();\n        }\n        var lastIndex = key.length - 1;\n        if (key[lastIndex] === '!') {\n          key = key.substring(0, lastIndex);\n          if (!key) {\n            return;\n          }\n          not = !not;\n        }\n        orgVal = index === -1 ? '' : value.substring(index + 1);\n        value = { key: key };\n        if ((pattern = util.toRegExp(orgVal))) {\n          value.hPattern = pattern;\n        } else {\n          value.orgVal = orgVal = orgVal.toLowerCase();\n          value.value = util.encodeURIComponent(orgVal);\n        }\n        if (isHeader) {\n          switch (propName[2]) {\n          case 'q':\n            propName = 'reqHeader';\n            break;\n          case 's':\n            propName = 'resHeader';\n            break;\n          case 'v':\n            propName = 'env';\n            break;\n          default:\n            propName = 'header';\n          }\n        }\n      }\n      filter = { not: not, isInclude: isInclude };\n      filter[propName] = value;\n      filter.raw = raw;\n      if (isHostFilter) {\n        hostFilter = hostFilter || [];\n        hostFilter.push(filter);\n      } else {\n        filters = filters || [];\n        filters.push(filter);\n      }\n      return;\n    }\n    var result = resolveFilterPattern(matcher);\n    if (result === false) {\n      return;\n    } else if (!result) {\n      matchers.push(rawMatcher);\n      return;\n    }\n    if (result.pattern) {\n      filters = filters || [];\n      result.raw = rawMatcher;\n      return filters.push(result);\n    }\n    filter = '/' + result.filter + '/' + (result.caseIns ? 'i' : '');\n    if ((filter = util.toRegExp(filter))) {\n      filters = filters || [];\n      filters.push({\n        raw: rawMatcher,\n        pattern: filter,\n        not: result.not,\n        isInclude: result.isInclude\n      });\n    }\n  });\n  return {\n    rawProps: rawProps,\n    hasBodyFilter: hasBodyFilter,\n    matchers: matchers,\n    hostFilter: hostFilter,\n    filters: filters,\n    lineProps: lineProps\n  };\n}\n\nfunction getPluginName(root) {\n  return root && ROOT_PLUGIN_RE.test(root) ? util.getPluginFile(RegExp.$1) : undefined;\n}\n\nfunction parseText(rulesMgr, text, root, file) {\n  var pluginVars = rulesMgr._globalPluginVars;\n  var enabledList = rulesMgr._enabledList || [];\n  rulesMgr._enabledList = enabledList;\n  getLines(text, root).forEach(function (line) {\n    var raw = line;\n    var rawLine = line;\n    line = removeComment(line);\n    line = line && line.split(/\\s+/);\n    var len = line && line.length;\n    if (len === 1 && PLUGIN_VAR_RE.test(line[0])) {\n      var name = RegExp.$1;\n      var value = RegExp.$3;\n      if (value) {\n        var vars = pluginVars[name];\n        if (!vars) {\n          vars = [value];\n          pluginVars[name] = vars;\n        } else if (vars.indexOf(value) === -1) {\n          vars.push(value);\n        }\n      }\n      enabledList.push([rawLine, file]);\n      return;\n    }\n    if (!len || len < 2) {\n      return;\n    }\n    line = line.map(formatShorthand);\n    var patternIndex = indexOfPattern(line);\n    if (patternIndex === -1) {\n      return;\n    }\n\n    var pattern = line[0];\n    var result = resolveMatchFilter(line.slice(1));\n    var matchers = result.matchers;\n    if (patternIndex > 0) {\n      //supports: operator-uri1 operator-uriX pattern1 pattern2 ... patternN\n      var opList = [pattern];\n      var patternList = matchers.filter(function (p) {\n        if (isPattern(p) || isHost(p) || !util.hasProtocol(p)) {\n          return true;\n        }\n        opList.push(p);\n      });\n      opList.forEach(function (matcher) {\n        patternList.forEach(function (pattern) {\n          parseRule(rulesMgr, pattern, matcher, raw, root, result, file);\n        });\n      });\n    } else {\n      //supports: pattern operator-uri1 operator-uri2 ... operator-uriN\n      matchers.forEach(function (matcher) {\n        parseRule(rulesMgr, pattern, matcher, raw, root, result, file);\n      });\n    }\n    enabledList.push([rawLine, file]);\n  });\n  regUrlCache = {};\n  hostCache = {};\n}\n\nfunction isExactPattern(pattern) {\n  return EXACT_RE.test(pattern);\n}\n\nfunction isNegativePattern(pattern) {\n  return NON_RE.test(pattern);\n}\n\nfunction getFilterResult(result, filter) {\n  return result == null ? false : filter.not ? !result : result;\n}\nfunction matchFilter(url, filter, req) {\n  var result;\n  if (filter.pattern) {\n    result = filter.pattern.test(url);\n    return getFilterResult(result, filter);\n  }\n  if (!req) {\n    return false;\n  }\n  var filterProp = function (value, expectVal, pattern) {\n    if (value == null) {\n      return expectVal || pattern;\n    }\n    if (expectVal) {\n      result = value == expectVal;\n      return true;\n    }\n    if (pattern) {\n      result = pattern.test(value);\n      return true;\n    }\n  };\n  if (filter.from) {\n    if (filter.from === 'tunnel') {\n      return filter.not ? !req.fromTunnel : req.fromTunnel;\n    }\n    if (filter.from === 'composer') {\n      return filter.not ? !req.fromComposer : req.fromComposer;\n    }\n    if (filter.from === 'internalPath') {\n      return filter.not ? !req.fromInternalPath : req.fromInternalPath;\n    }\n    if (filter.from === 'sni') {\n      return filter.not ? !req.useSNI : req.useSNI;\n    }\n    if (filter.from === 'httpserver') {\n      return filter.not ? !req.fromHttpServer : req.fromHttpServer;\n    }\n    if (filter.from === 'httpsserver') {\n      return filter.not ? !req.fromHttpsServer : req.fromHttpsServer;\n    }\n    if (filter.from === 'httpsport') {\n      return filter.not ? !req.isHttpsServer : req.isHttpsServer;\n    }\n    return false;\n  }\n  var chance = filter.chance || filter.probability;\n  if (chance) {\n    var key = chance.key;\n    var keyLen = key && key.length - 1;\n    if (keyLen > 0 && key[keyLen] === '%') {\n      key = key.substring(0, keyLen) / 100;\n    }\n    return getFilterResult(Math.random() < chance.key, filter);\n  }\n  if (filterProp(req.method, filter.method, filter.mPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.statusCode, filter.statusCode, filter.sPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.clientIp, filter.ip, filter.iPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.hostIp, filter.ip, filter.iPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.clientIp, filter.clientIp, filter.clientPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.hostIp, filter.serverIp, filter.serverPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.clientPort, filter.clientPort, filter.cpPattern)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterProp(req.serverPort, filter.serverPort, filter.spPattern)) {\n    return getFilterResult(result, filter);\n  }\n\n  if (filterProp(req._remoteAddr, filter.remoteAddress, filter.addrPattern)) {\n    return getFilterResult(result, filter);\n  }\n\n  if (filterProp(req._remotePort, filter.remotePort, filter.portPattern)) {\n    return getFilterResult(result, filter);\n  }\n\n  var reqBody = req._reqBody;\n  if (filter.bodyPattern || filter.body) {\n    if (typeof reqBody !== 'string') {\n      return false;\n    }\n    if (filter.bodyPattern) {\n      if (result = filter.bodyPattern.exec(reqBody)) {\n        req._bodySubVals = [reqBody];\n        for (var i = 1, len = result.length; i < len; i++) {\n          req._bodySubVals[i] = (result[i] || '');\n        }\n      }\n    } else {\n      reqBody = reqBody.toLowerCase();\n      result =\n        reqBody.indexOf(filter.body.value) !== -1 ||\n        reqBody.indexOf(filter.body.orgVal) !== -1;\n    }\n\n    return filter.not ? !result : result;\n  }\n\n  var filterHeader = function (headers, filterVal, resHeaders) {\n    if (!filterVal || !headers) {\n      return;\n    }\n    var value = headers[filterVal.key];\n    if (value == null) {\n      value = resHeaders && resHeaders[filterVal.key];\n      if (value == null) {\n        result = false;\n        return true;\n      }\n    }\n    if (!value) {\n      result = !filterVal.hPattern && !filterVal.value;\n      return true;\n    }\n    value = String(value);\n    if (filterVal.hPattern) {\n      result = filterVal.hPattern.test(value);\n    } else {\n      value = value.toLowerCase();\n      result =\n        value.indexOf(filterVal.value) !== -1 ||\n        value.indexOf(filterVal.orgVal) !== -1;\n    }\n    return true;\n  };\n  if (filterHeader(req.headers, filter.header, req.resHeaders)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterHeader(req.headers, filter.reqHeader)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterHeader(req.resHeaders, filter.resHeader)) {\n    return getFilterResult(result, filter);\n  }\n  if (filterHeader(env, filter.env)) {\n    return getFilterResult(result, filter);\n  }\n  return false;\n}\n\nfunction matchExcludeFilters(url, rule, options) {\n  var filters = rule.filters;\n  if (!filters) {\n    return;\n  }\n  filters.curFilter = null;\n  var hasIncludeFilter;\n  var include, exclude;\n  for (var i = 0, len = filters.length; i < len; i++) {\n    var filter = filters[i];\n    hasIncludeFilter = hasIncludeFilter || filter.isInclude;\n    if (\n      (filter.isInclude ? !include : !exclude) &&\n      matchFilter(url, filter, options)\n    ) {\n      if (filter.isInclude) {\n        include = true;\n        filters.curFilter = filter.raw;\n      } else {\n        exclude = true;\n      }\n    }\n  }\n  return hasIncludeFilter ? !include || exclude : exclude;\n}\n\nfunction Rules(values) {\n  this._rules = protoMgr.getRules();\n  this._globalPluginVars = {};\n  this._sniCallback = [];\n  this._orgValues = this._values = values || {};\n}\n\nvar proto = Rules.prototype;\n\nfunction resolveInlineValuesFn(item, rulesMgr) {\n  var file = item.file || getPluginName(item.root) || rulesMgr._file;\n  item.file = file;\n  item.text = util.resolveInlineValues(item.text, inlineValues, file);\n  return item;\n}\n\nfunction trimInlineValues(item, rulesMgr) {\n  var text = item.text;\n  var isArr = Array.isArray(text);\n  var first = isArr && item.text[0];\n  if (first && first.resolved) {\n    return isArr ? text : [item];\n  }\n  return isArr ? text.map(function(i) {\n    return resolveInlineValuesFn(i, rulesMgr);\n  }) : [resolveInlineValuesFn(item, rulesMgr)];\n}\n\nproto._parse = function (list, append) {\n  var self = this;\n  if (!append) {\n    protoMgr.resetRules(self._rules);\n    self._globalPluginVars = {};\n    self._sniCallback = [];\n  }\n  trimInlineValues(list, self).forEach(function (item) {\n    item && parseText(self, item.text, item.root, item.file);\n  });\n};\n\nproto.parse = function (text, root, _inlineValues) {\n  var item = {\n    first: true,\n    text: text,\n    root: root\n  };\n  this._enabledList = [];\n  this._inlineValues = _inlineValues;\n  this.disabled = !arguments.length;\n  if (this._rawText) {\n    if (this._rawText[0].first) {\n      this._rawText.shift();\n    }\n    this._rawText.unshift(item);\n  } else {\n    this._rawText = [item];\n  }\n  inlineValues = extend({}, _inlineValues);\n  this._parse(item);\n  if (!this.disabled) {\n    for (var i = 1, len = this._rawText.length; i < len; i++) {\n      item = this._rawText[i];\n      this._parse(item, true);\n    }\n  }\n  this._values = extend({}, this._orgValues, inlineValues);\n  inlineValues = {};\n  this._mflag = getMFlag();\n};\n\nproto.clearAppend = function () {\n  this._enabledList = [];\n  if (this._rawText && this._rawText[0].first) {\n    var item = this._rawText[0];\n    inlineValues = extend({}, this._inlineValues);\n    !this.disabled && this._parse(item);\n    this._rawText = [item];\n    this._values = extend({}, this._orgValues, inlineValues);\n    inlineValues = {};\n  } else {\n    this._rawText = null;\n  }\n  this._mflag = getMFlag();\n};\n\nproto.append = function (text, root, batchUpdate) {\n  var item = {\n    text: text,\n    root: root\n  };\n  if (this._rawText) {\n    this._rawText.push(item);\n    !this.disabled && !batchUpdate && this._parse(item, true);\n    extend(this._values, inlineValues);\n    inlineValues = {};\n  } else {\n    this._rawText = [item];\n  }\n  this._mflag = getMFlag();\n};\n\nproto.resolveHost = function (\n  req,\n  callback,\n  pluginRulesMgr,\n  rulesFileMgr,\n  headerRulesMgr\n) {\n  if (!req.curUrl) {\n    return callback();\n  }\n  var host = this.getHost(req, pluginRulesMgr, rulesFileMgr, headerRulesMgr);\n  if (host) {\n    return callback(\n      null,\n      util.removeProtocol(host.matcher, true),\n      host.port,\n      host\n    );\n  }\n  if (!req._enableProxyHost && req.rules) {\n    delete req.rules.host;\n  }\n  this.lookupHost(req, callback);\n};\n\nproto.lookupHost = function (req, callback) {\n  req.curUrl = util.formatUrl(util.setProtocol(req.curUrl));\n  var options = parseUrl(req.curUrl);\n  lookup(\n    options.hostname,\n    callback,\n    allowDnsCache && !this.resolveDisable(req).dnsCache\n  );\n};\n\nvar ignoreHost = function (req, rulesMgr, filter) {\n  if (!rulesMgr) {\n    return false;\n  }\n  var ignore = rulesMgr.resolveFilter(req);\n  extend(filter, ignore);\n  return (\n    ignore.host ||\n    ignore.hosts ||\n    ignore['ignore|host'] ||\n    ignore['ignore|hosts']\n  );\n};\n\nfunction getHostFromList(list, req) {\n  var host;\n  for (var i = 0, len = list.length; i < len; i++) {\n    var mgr = list[i];\n    var _host = mgr && mgr.getRule(req, mgr._rules.host, mgr._values);\n    if (_host) {\n      if (util.isImportant(_host)) {\n        return _host;\n      }\n      host = host || _host;\n    }\n  }\n  return host;\n}\n\nproto.getHost = function (req, pluginRulesMgr, rulesFileMgr, headerRulesMgr) {\n  var curUrl = util.formatUrl(util.setProtocol(req.curUrl));\n  req.curUrl = curUrl;\n  var filter = {};\n  var filterHost =\n    ignoreHost(req, this, filter) ||\n    ignoreHost(req, pluginRulesMgr, filter) ||\n    ignoreHost(req, rulesFileMgr, filter) ||\n    ignoreHost(req, headerRulesMgr, filter);\n  if (filterHost) {\n    return;\n  }\n  var list;\n  if (config.multiEnv) {\n    list = [pluginRulesMgr, headerRulesMgr, this, rulesFileMgr];\n  } else {\n    list = [pluginRulesMgr, this, rulesFileMgr, headerRulesMgr];\n  }\n  var host = getHostFromList(list, req);\n  if (!host || util.exactIgnore(filter, host)) {\n    return;\n  }\n  var matcher = util.rule.getMatcher(host);\n  if (matcher && PORT_RE.test(matcher)) {\n    host.matcher = RegExp.$1 + (RegExp.$2 || RegExp.$3);\n    host.port = host.port || RegExp.$4;\n  }\n  return host;\n};\n\nproto.resolveFilter = function (req) {\n  var filter = resolveProps.call(this, req, this._rules.filter, this._values);\n  var ignore = resolveProps.call(this, req, this._rules.ignore, this._values, true);\n  util.resolveFilter(ignore, filter);\n  delete filter.filter;\n  delete filter.ignore;\n  delete filter['ignore|filter'];\n  delete filter['ignore|ignore'];\n  return filter;\n};\nproto.resolveDisable = function (req) {\n  return resolveProps.call(this, req, this._rules.disable, this._values);\n};\nvar pluginProtocols = [\n  'enable',\n  'disable',\n  'plugin',\n  'rule',\n  'reqHeaders',\n  'auth',\n  'referer',\n  'forwardedFor'\n];\nvar proxyProtocols = [\n  'enable',\n  'disable',\n  'plugin',\n  'reqHeaders',\n  'auth',\n  'referer',\n  'forwardedFor'\n];\n\nfunction resolveRules(req, isReq, isRes) {\n  if (req.isInternalUrl) {\n    return {};\n  }\n  var self = this;\n  var rule;\n  var rules = self._rules;\n  var _rules = {};\n  var vals = self._values;\n  var filter = self.resolveFilter(req);\n  var protos;\n  if (req.isPluginReq) {\n    protos = req._isProxyReq ? proxyProtocols : pluginProtocols;\n  } else if (isRes) {\n    protos = pureResProtocols;\n  } else if (isReq) {\n    protos = req._resolvedG ? reqProtosWithoutG : reqProtocols;\n  } else {\n    protos = req._resolvedG ? protocolsWithoutG : protocols;\n  }\n  req._inlineValues = vals;\n  protos.forEach(function (name) {\n    if (\n      name !== 'pipe' &&\n      (name === 'proxy' ||\n        name === 'rule' ||\n        name === 'plugin' ||\n        !filter[name]) &&\n      (rule = self.getRule(req, rules[name], vals))\n    ) {\n      _rules[name] = rule;\n    }\n  });\n\n  multiMatchs.forEach(function (name) {\n    rule = _rules[name];\n    if (rule) {\n      rule.list = self.getRuleList(req, rules[name], vals);\n      util.filterRepeatPlugin(rule);\n      if (name === 'rulesFile' || name === 'resScript') {\n        var hasScript,\n          scriptIndex = -1;\n        rule.list = rule.list.filter(function (item, i) {\n          if (item.isRules) {\n            return true;\n          }\n          if (hasScript) {\n            return false;\n          }\n          scriptIndex = i;\n          hasScript = true;\n          return true;\n        });\n        rule.scriptIndex = scriptIndex;\n        rule.isRawList = true;\n      }\n    }\n  });\n  util.ignoreRules(_rules, filter);\n  return _rules;\n}\n\nproto.getRule = function(req, list, vals, index, isFilter, host) {\n  if (!this._isGlobal || !req._disabledProxyRules) {\n    return getRule(req, list, vals, index, isFilter, host);\n  }\n};\n\nproto.getRuleList = function(req, list, vals, isEnableProxy) {\n  if (!this._isGlobal || !req._disabledProxyRules) {\n    return getRuleList(req, list, vals, isEnableProxy);\n  }\n  return [];\n};\n\nproto.resolveRules = function (req) {\n  return resolveRules.call(this, req);\n};\n\nproto.resolveReqRules = function (req) {\n  return resolveRules.call(this, req, true);\n};\n\nproto.resolveResRules = function (req) {\n  return resolveRules.call(this, req, false, true);\n};\n\nproto.resolveEnable = function (req) {\n  return resolveProps.call(this, req, this._rules.enable, this._values);\n};\n\nfunction mergeRule(rules, list, name) {\n  if (list.length) {\n    if (rules[name]) {\n      var curList = rules[name].list;\n      var flags = curList.map(function (item) {\n        return item.raw;\n      });\n      list.forEach(function (item) {\n        if (flags.indexOf(item.raw) === -1) {\n          curList.push(item);\n        }\n      });\n    } else {\n      rules[name] = extend({ list: list }, list[0]);\n    }\n  }\n}\n\nproto.resolveProxyProps = function (req) {\n  if (req.curUrl === req.fullUrl) {\n    return;\n  }\n  var enable = this.getRuleList(req, this._rules.enable, this._values, true);\n  var disable = this.getRuleList(req, this._rules.disable, this._values, true);\n  if (!enable.length && !disable.length) {\n    return;\n  }\n  mergeRule(req.rules, enable, 'enable');\n  mergeRule(req.rules, disable, 'disable');\n  enable = util.resolveProperties(enable);\n  disable = util.resolveProperties(disable);\n  if (disable.clientId || disable.clientID || disable.clientid) {\n    req.disable.clientId = true;\n  }\n  if (enable.clientId || enable.clientID || enable.clientid) {\n    req.enable.clientId = true;\n  }\n  if (enable.multiClient) {\n    req.enable.multiClient = true;\n  }\n  if (enable.singleClient) {\n    req.enable.singleClient = true;\n  }\n  if (disable.multiClient) {\n    req.disable.multiClient = true;\n  }\n  return {\n    enable: enable,\n    disable: disable\n  };\n};\n\nfunction resolveSingleRule(req, protocol, multi) {\n  req.curUrl = req.curUrl || req.fullUrl;\n  var filter = this.resolveFilter(req);\n  if (util.isIgnored(filter, protocol)) {\n    return;\n  }\n  var list = protocol === 'sniCallback' ? this._sniCallback : this._rules[protocol];\n  if (multi) {\n    list = this.getRuleList(req, list, this._values).filter(function(rule) {\n      return !util.exactIgnore(filter, rule);\n    });\n    return list.length ? list : null;\n  }\n  var rule = this.getRule(req, list, this._values);\n  return rule && !util.exactIgnore(filter, rule) ? rule : null;\n}\n\nproto.resolvePipe = function (req) {\n  return req.isPluginReq ? null : resolveSingleRule.call(this, req, 'pipe');\n};\n\nproto.getGRuleList = function (req) {\n  return resolveSingleRule.call(this, req, 'G', true);\n};\n\nproto.resolvePac = function (req) {\n  return resolveSingleRule.call(this, req, 'pac');\n};\n\nproto.resolveRule = function (req) {\n  return resolveSingleRule.call(this, req, 'rule');\n};\n\nvar WHISTLE_INTERNAL_HOST =\n  /^reqHeaders:\\/\\/whistleInternalHost=([a-z\\d.-]+(?::\\d{1,5})?)$/;\nproto.resolveInternalHost = function (req) {\n  var list = resolveSingleRule.call(this, req, 'reqHeaders', true);\n  if (list) {\n    for (var i = 0, len = list.length; i < len; i++) {\n      var item = list[i];\n      if (WHISTLE_INTERNAL_HOST.test(item.matcher)) {\n        return RegExp.$1;\n      }\n    }\n  }\n};\n\nproto.hasReqScript = function (req) {\n  return req.isPluginReq\n    ? null\n    : this.getRule(req, this._rules.rulesFile, this._values);\n};\n\nproto.resolveProxy = function resolveProxy(req, host) {\n  var proxy = this.getRule(req, this._rules.proxy, this._values, null, null, host);\n  var matcher = proxy && proxy.matcher;\n  var name = matcher && matcher.substring(0, matcher.indexOf(':'));\n  var filter = this.resolveFilter(req);\n  if (!proxy) {\n    return proxy;\n  }\n  if (util.exactIgnore(filter, proxy)) {\n    return;\n  }\n  if (filter['ignore:' + name]) {\n    return proxy;\n  }\n  if (util.isIgnored(filter, 'proxy')) {\n    return;\n  }\n  if (matcher[0] === 'x' && util.isIgnored(filter, 'xproxy')) {\n    return;\n  }\n  if (matcher.indexOf(name + '://') === 0 && util.isIgnored(filter, name)) {\n    return;\n  }\n  return proxy;\n};\n\nproto.resolveSNICallback = function (req) {\n  return resolveSingleRule.call(this, req, 'sniCallback');\n};\n\nproto.resolveLocalRule = function (req) {\n  return this.getRule(req, this._rules._localRule);\n};\nproto.resolveClientCert = function (req) {\n  return this.getRule(req, this._rules._clientCerts);\n};\nproto.resolveBodyFilter = function (req) {\n  return req.isPluginReq\n    ? null\n    : this.getRule(\n        req,\n        req._bodyFilters || this._rules._bodyFilters,\n        null,\n        null,\n        true\n      );\n};\n\nRules.disableDnsCache = function () {\n  allowDnsCache = false;\n};\n\nmodule.exports = Rules;\n\nRules.setPluginMgr = function(p) {\n  pluginMgr = p;\n};\n"
  },
  {
    "path": "lib/rules/storage.js",
    "content": "var fs = require('fs');\nvar fse = require('fs-extra2');\nvar path = require('path');\nvar logger = require('../util/logger');\nvar common = require('../util/common');\nvar RecycleBin = require('./recycle-bin');\n\nvar isGroup = common.isGroup;\nvar copyFile = common.copyFile;\nvar readFileSafe = common.readFileTextSync;\nvar RETRY_INTERVAL = 16000;\nvar ASCII_RE = /[\\x00-\\x7f]/g;\nvar MAX_FILENAME_LEN = 254;\nvar EMPTY_ARR = [];\n\nfunction encodeName(index, name) {\n  var filename;\n  try {\n    filename = index + '.' + encodeURIComponent(name);\n    if (filename.length <= MAX_FILENAME_LEN) {\n      return filename;\n    }\n  } catch (e) {}\n  try {\n    filename = index + '.' + name.replace(ASCII_RE, encodeURIComponent);\n    if (filename.length <= MAX_FILENAME_LEN) {\n      return filename;\n    }\n  } catch (e) {\n    logger.error(e);\n  }\n  return index + '.' + name;\n}\n\nfunction readJsonSafe(file) {\n  try {\n    file = readFileSafe(file);\n    return file && JSON.parse(file);\n  } catch (e) {}\n}\n\nfunction copyFileObj(file) {\n  if (!file) {\n    return file;\n  }\n\n  return {\n    index: file.index,\n    name: file.name,\n    data: file.data,\n    selected: file.selected\n  };\n}\n\nfunction noop() {}\n\nfunction compare(v1, v2) {\n  return v1 == v2 ? 0 : v1 > v2 ? 1 : -1;\n}\n\nfunction Storage(dir, filters, disabled, allows) {\n  var self = this;\n  self.recycleBin = new RecycleBin(path.join(dir, '.recycle_bin'));\n  var backupDir = path.join(dir, '.backup');\n  fse.ensureDirSync(dir);\n  fse.ensureDirSync(backupDir);\n\n  self._disabled = disabled === true;\n  self._backupDir = backupDir;\n  self._files = path.join(dir, 'files');\n  self._properties = path.join(dir, 'properties');\n  self._backupProps = path.join(backupDir, 'properties');\n  fse.ensureDirSync(self._files);\n  fse.ensureFileSync(self._properties);\n\n  var maxIndex = -1;\n  var files = {};\n  var fileNames = { properties: true };\n\n  filters = filters || {};\n  fs.readdirSync(self._files).forEach(function (file) {\n    if (!/^(\\d+)\\.(.+)$/.test(file)) {\n      return;\n    }\n    var index = parseInt(RegExp.$1, 10);\n    var filename = RegExp.$2;\n    try {\n      filename = decodeURIComponent(filename);\n    } catch (e) {\n      logger.error(e);\n    }\n    var filePath = path.join(self._files, file);\n    if (files[filename]) {\n      return fs.unlinkSync(filePath);\n    }\n    if (index > maxIndex) {\n      maxIndex = index;\n    }\n    if (filters[filename] || (allows && !allows[filename])) {\n      return;\n    }\n    var data = readFileSafe(filePath);\n    var backFile = path.join(backupDir, file);\n    if (data) {\n      fs.writeFileSync(backFile, data);\n    } else {\n      data = readFileSafe(backFile) || '';\n      if (data) {\n        fs.writeFileSync(filePath, data);\n      }\n    }\n    files[filename] = {\n      index: index,\n      name: filename,\n      data: isGroup(filename) ? '' : data\n    };\n    var newFile = encodeName(index, filename);\n    fileNames[newFile] = true;\n    if (file !== newFile) {\n      fs.writeFileSync(path.join(self._files, newFile), data);\n      fs.writeFileSync(path.join(backupDir, newFile), data);\n      fs.unlinkSync(filePath);\n    }\n  });\n\n  var properties = readJsonSafe(self._properties);\n  if (properties) {\n    fse.outputJsonSync(self._backupProps, properties);\n  } else {\n    properties = readJsonSafe(self._backupProps);\n    if (properties) {\n      fse.outputJsonSync(self._properties, properties);\n    } else {\n      properties = {};\n    }\n  }\n\n  self._cache = {\n    maxIndex: maxIndex,\n    files: files,\n    properties: properties\n  };\n\n  var filesOrder = properties.filesOrder;\n  if (!Array.isArray(filesOrder)) {\n    filesOrder = null;\n  }\n  filesOrder = Object.keys(files).sort(function (cur, next) {\n    if (filesOrder) {\n      var curIndex = filesOrder.indexOf(cur);\n      if (curIndex !== -1) {\n        var nextIndex = filesOrder.indexOf(next);\n        if (nextIndex !== -1) {\n          return compare(curIndex, nextIndex);\n        }\n      }\n    }\n    cur = files[cur];\n    next = files[next];\n    return compare(cur.index, next.index);\n  });\n  this._cache.properties['filesOrder'] = filesOrder;\n  fs.readdirSync(backupDir).forEach(function (file) {\n    if (allows || fileNames[file] || filters[file]) {\n      return;\n    }\n    try {\n      fs.unlinkSync(path.join(backupDir, file));\n    } catch (e) {}\n  });\n}\n\nvar proto = Storage.prototype;\n\nproto._writeProperties = function () {\n  var self = this;\n  if (self._disabled || self._writePropertiesPending) {\n    self._writePropertiesWaiting = true;\n    return;\n  }\n  clearTimeout(self._writePropertiesTimeout);\n  self._writePropertiesPending = true;\n  fse.outputJson(self._properties, self._cache.properties, function (err) {\n    self._writePropertiesPending = false;\n    if (err) {\n      self._writePropertiesTimeout = setTimeout(\n        self._writeProperties.bind(self),\n        RETRY_INTERVAL\n      );\n      logger.error(err);\n    } else {\n      copyFile(self._properties, self._backupProps, function () {\n        if (self._writePropertiesWaiting) {\n          self._writePropertiesWaiting = false;\n          self._writeProperties();\n        }\n      });\n    }\n  });\n};\n\nproto._writeFile = function (file) {\n  var self = this;\n  if (self._disabled || !(file = self._cache.files[file])) {\n    return;\n  }\n  if (file._pending) {\n    file._waiting = true;\n    return;\n  }\n  clearTimeout(file._timeout);\n  file._pending = true;\n  fs.writeFile(self._getFilePath(file), file.data, function (err) {\n    file._pending = false;\n    if (err) {\n      file._timeout = setTimeout(function () {\n        self._writeFile(file.name);\n      }, RETRY_INTERVAL);\n      logger.error(err);\n    } else {\n      copyFile(\n        self._getFilePath(file),\n        self._getFilePath(file, true),\n        function () {\n          if (file._waiting) {\n            file._waiting = false;\n            self._writeFile(file.name);\n          }\n        }\n      );\n    }\n  });\n};\n\nproto._getFilePath = function (file, backup) {\n  file = typeof file == 'string' ? this._cache.files[file] : file;\n  var name = encodeName(file.index, file.name);\n  return path.join(backup ? this._backupDir : this._files, name);\n};\n\nproto.count = function () {\n  return this._disabled ? 0 : Object.keys(this._cache.files).length;\n};\n\nproto.existsFile = function (file) {\n  return !this._disabled && this._cache.files[file];\n};\n\nproto.getFileList = function (origObj) {\n  var cache = this._cache;\n  var files = cache.files;\n  var filesOrder = cache.properties.filesOrder;\n  return this._disabled\n    ? EMPTY_ARR\n    : filesOrder.map(function (file) {\n      return origObj ? files[file] : copyFileObj(files[file]);\n    });\n};\n\nproto.writeFile = function (file, data) {\n  if (this._disabled || !file || typeof file !== 'string') {\n    return;\n  }\n\n  var self = this;\n  var cache = self._cache;\n  var fileData = cache.files[file];\n  var hasChanged = true;\n  data = typeof data == 'string' ? data : '';\n  if (!fileData) {\n    fileData = cache.files[file] = {\n      index: ++cache.maxIndex,\n      name: file\n    };\n    cache.properties.filesOrder.push(file);\n    self._writeProperties();\n  } else if (fileData.data === data) {\n    hasChanged = false;\n  }\n  fileData.data = data;\n  self._writeFile(file);\n  return hasChanged;\n};\n\nproto.updateFile = function (file, data) {\n  return !this._disabled && this.existsFile(file) && this.writeFile(file, data);\n};\n\nproto.readFile = function (file) {\n  file = !this._disabled && file && this._cache.files[file];\n  return file && file.data;\n};\n\nfunction removeFile(self, file) {\n  var files = self._cache.files;\n  file = !self._disabled && file && files[file];\n  if (!file) {\n    return;\n  }\n  var filesOrder = self._cache.properties.filesOrder;\n  filesOrder.splice(filesOrder.indexOf(file.name), 1);\n  self.recycleBin.recycle(file.name, file.data);\n  delete files[file.name];\n  fs.unlink(self._getFilePath(file), function (err) {\n    if (!err) {\n      fs.unlink(self._getFilePath(file, true), noop);\n    }\n  });\n  return true;\n}\n\nproto.removeFile = function (file) {\n  if (removeFile(this, file)) {\n    this._writeProperties();\n    return true;\n  }\n};\n\nproto.removeGroup = function(groupName) {\n  if (!isGroup(groupName)) {\n    return;\n  }\n  var self = this;\n  var filesOrder = self._cache.properties.filesOrder;\n  var index = filesOrder.indexOf(groupName) + 1;\n  if (!index) {\n    return;\n  }\n  var result = [groupName];\n  for (var len = filesOrder.length; index < len; index++) {\n    var file = filesOrder[index];\n    if (isGroup(file)) {\n      break;\n    }\n    result.push(file);\n  }\n  result.forEach(function(file) {\n    removeFile(self, file);\n  });\n  this._writeProperties();\n  return result;\n};\n\nproto.renameFile = function (file, newFile) {\n  var self = this;\n  var cache = self._cache;\n  if (\n    this._disabled ||\n    !newFile ||\n    !(file = cache.files[file]) ||\n    cache.files[newFile]\n  ) {\n    return;\n  }\n  var filesOrder = self._cache.properties.filesOrder;\n  filesOrder[filesOrder.indexOf(file.name)] = newFile;\n  var path = self._getFilePath(file);\n  var backupPath = self._getFilePath(file, true);\n  delete cache.files[file.name];\n  file.name = newFile;\n  cache.files[newFile] = file;\n  fs.rename(path, self._getFilePath(file), function (err) {\n    if (!err) {\n      fs.rename(backupPath, self._getFilePath(file, true), noop);\n    }\n  }); //不考虑并发\n  self._writeProperties();\n  return true;\n};\n\nproto.moveGroupToTop = function(name) {\n  var filesOrder = this._cache.properties.filesOrder;\n  var index = filesOrder.indexOf(name);\n  if (index === -1) {\n    return;\n  }\n  var groupName;\n  for (var i = 0; i < index; i++) {\n    groupName = filesOrder[i];\n    if (isGroup(groupName)) {\n      return this.moveTo(name, groupName, true, true);\n    }\n  }\n};\n\nproto.moveToGroup = function(name, groupName, isTop) {\n  if (!groupName) {\n    return;\n  }\n  var filesOrder = this._cache.properties.filesOrder;\n  var index = filesOrder.indexOf(name);\n  if (index === -1 || filesOrder.indexOf(groupName) === -1) {\n    return;\n  }\n  filesOrder.splice(index, 1);\n  index = filesOrder.indexOf(groupName) + 1;\n  if (isTop) {\n    filesOrder.splice(index, 0, name);\n  } else {\n    for (var len = filesOrder.length; index < len; index++) {\n      if (isGroup(filesOrder[index])) {\n        break;\n      }\n    }\n    filesOrder.splice(index, 0, name);\n  }\n  this._writeProperties();\n  return true;\n};\n\nproto.getGroupName = function(name) {\n  var filesOrder = this._cache.properties.filesOrder;\n  var i = filesOrder.indexOf(name);\n  if (i !== -1) {\n    for (; i >=0; i--) {\n      name = filesOrder[i];\n      if (isGroup(name)) {\n        return name;\n      }\n    }\n  }\n};\n\nproto.moveTo = function (fromName, toName, group, toTop) {\n  var filesOrder = this._cache.properties.filesOrder;\n  var fromIndex = filesOrder.indexOf(fromName);\n  if (this._disabled || fromIndex === -1) {\n    return false;\n  }\n  var toIndex = filesOrder.indexOf(toName);\n  if (toIndex === -1 || fromIndex === toIndex) {\n    return false;\n  }\n  if (group && isGroup(fromName)) {\n    var files = this._cache.files;\n    var children = [fromName];\n    var len = filesOrder.length;\n    for (var i = fromIndex + 1; i < len; i++) {\n      var name = files[filesOrder[i]].name;\n      if (isGroup(name)) {\n        break;\n      }\n      children.push(name);\n    }\n    if (fromIndex < toIndex && isGroup(toName)) {\n      for (; toIndex < len; toIndex++) {\n        if (isGroup(filesOrder[toIndex + 1])) {\n          break;\n        }\n      }\n    }\n    len = children.length;\n    if (len > 1 && fromIndex < toIndex) {\n      toIndex = Math.max(0, toIndex - len + 1);\n    }\n    filesOrder.splice(fromIndex, len);\n    children.unshift(toIndex, 0);\n    filesOrder.splice.apply(filesOrder, children);\n  } else if (toTop || isGroup(fromName) || !isGroup(toName) || this.getGroupName(fromName) === toName) {\n    filesOrder.splice(fromIndex, 1);\n    filesOrder.splice(toIndex, 0, fromName);\n  } else {\n    filesOrder.splice(fromIndex, 1);\n    filesOrder.splice(fromIndex > toIndex ? toIndex + 1 : toIndex, 0, fromName);\n  }\n  this._writeProperties();\n  return true;\n};\n\nproto.setProperty = function (name, value) {\n  if (!this._disabled) {\n    this._cache.properties[name] = value;\n    this._writeProperties();\n  }\n};\n\nproto.hasProperty = function (name) {\n  return !this._disabled && name in this._cache.properties;\n};\n\nproto.setProperties = function (obj) {\n  if (this._disabled || !obj) {\n    return;\n  }\n\n  var props = this._cache.properties;\n  Object.keys(obj).forEach(function (key) {\n    props[key] = obj[key];\n  });\n  this._writeProperties();\n  return true;\n};\n\nproto.getProperty = function (name) {\n  return this._disabled ? null : this._cache.properties[name];\n};\n\nproto.removeProperty = function (name) {\n  if (!this._disabled && this.hasProperty(name) && name !== 'filesOrder') {\n    delete this._cache.properties[name];\n    this._writeProperties();\n  }\n};\n\nmodule.exports = Storage;\n"
  },
  {
    "path": "lib/rules/util.js",
    "content": "var rules = require('./index');\nvar util = require('../util');\nvar config = require('../config');\nvar Storage = require('./storage');\nvar httpMgr = require('../util/http-mgr');\n\nvar INTERVAL = 1000 * 60 * 60 * 2;\nvar rulesStorage = new Storage(\n  config.rulesDir,\n  { Default: true },\n  config.disableWebUI\n);\nvar valuesStorage = new Storage(config.valuesDir, null, config.disableWebUI);\nvar propertiesStorage = new Storage(config.propertiesDir, { composerHistory: true }, config.disableWebUI);\nvar LINE_END_RE = /\\n|\\r\\n|\\r/g;\nvar SPACE_RE = /\\s/;\nvar MAX_COUNT_BY_IMPORT = 100;\nvar inlineValues = {};\nvar proxy, pluginMgr;\nvar serviceRules;\nvar mockRules;\nvar dnsOrder = propertiesStorage.getProperty('dnsOrder');\n\nconfig.ipv6Only = config.ipv6Only || !!propertiesStorage.getProperty('ipv6Only');\nif (!config.dnsOrder && !config.setDefaultResultOrder(dnsOrder)) {\n  config.dnsOrder = config.defaultDnsOrder;\n}\n\nfunction limitValueLen(name, len) {\n  var value = propertiesStorage.getProperty(name);\n  if (typeof value !== 'string') {\n    propertiesStorage.setProperty(name, name);\n  } else if (value.length > len) {\n    propertiesStorage.setProperty(name, value.substring(0, len));\n  }\n}\n\nlimitValueLen('Custom1', 16);\nlimitValueLen('Custom2', 16);\n\n/**\n * rules\n */\n\nfunction handleInlineValues(text, file) {\n  return util.resolveInlineValues(text, inlineValues, file);\n}\n\nfunction reverseRules(text, file) {\n  if (!text) {\n    return '';\n  }\n  text = handleInlineValues(text, file);\n  text = text.split(LINE_END_RE).reverse();\n  return text.join('\\n');\n}\n\nif (config.networkMode && !config.shadowRulesMode) {\n  exports.parse = util.noop;\n} else {\n  httpMgr.addChangeListener(function() {\n    var disableRules =\n    !config.notAllowedDisableRules &&\n    propertiesStorage.getProperty('disabledAllRules');\n    var shadowRules =\n    disableRules && config.allowDisableShadowRules ? null : config.shadowRules;\n    var backRulesFirst =\n    !config.disabledBackOption &&\n    propertiesStorage.getProperty('backRulesFirst') === true;\n    disableRules = disableRules || config.pluginsMode || config.shadowRulesMode;\n    var defaultRules = disableRules || defaultRulesIsDisabled() ? null : getDefaultRules();\n    var rulesList = [];\n    var resolveRemoteRules = util.getRemoteRulesResolver(inlineValues);\n    var addRules = function(text, name, type) {\n      if (text) {\n        var file = (type || 'File: ') + name;\n        if (backRulesFirst && !type) {\n          text = resolveRemoteRules(reverseRules(text, file), file);\n          rulesList.unshift({\n            resolved: true,\n            file: file,\n            text: text\n          });\n        } else {\n          text = resolveRemoteRules(handleInlineValues(text, file), file);\n          rulesList.push({\n            resolved: true,\n            file: file,\n            text: text\n          });\n        }\n      }\n    };\n    if (!disableRules && !config.multiEnv) {\n      getAllRulesFile().forEach(function (file) {\n        if (file.selected && !util.isGroup(file.name)) {\n          addRules(file.data, file.name);\n        }\n      });\n    }\n    addRules(defaultRules, 'Default');\n    addRules(mockRules, '', 'Mock Rules');\n    addRules(serviceRules, '', 'Service Rules');\n    addRules(shadowRules, '', 'Shadow Rules');\n    rules._rawRulesText = rulesList.map(function(item) {\n      return item.text;\n    }).join('\\r\\n');\n    pluginMgr.emit('updateRules', true);\n    rules.parse(rulesList, null, inlineValues);\n    inlineValues = {};\n  });\n}\n\nfunction parseRules() {\n  return httpMgr.triggerChange();\n}\n\nexports.parseRules = parseRules;\n\nfunction setDefaultRules(data) {\n  data = typeof data != 'string' ? '' : data;\n  var oldData = rulesStorage.getProperty('defalutRules') || '';\n  rulesStorage.setProperty('defalutRules', data);\n  parseRules();\n  return data !== oldData;\n}\n\nfunction getDefaultRules() {\n  return rulesStorage.getProperty('defalutRules');\n}\n\nfunction disableDefaultRules() {\n  rulesStorage.setProperty('disabledDefalutRules', true);\n  parseRules();\n  proxy.emit('rulesDataChange', 'disabledDefalutRules', true);\n}\n\nfunction enableDefaultRules() {\n  rulesStorage.setProperty('disabledDefalutRules', false);\n  parseRules();\n  proxy.emit('rulesDataChange', 'disabledDefalutRules', false);\n}\n\nfunction defaultRulesIsDisabled() {\n  return rulesStorage.getProperty('disabledDefalutRules');\n}\n\nfunction selectRulesFile(file) {\n  if (!rulesStorage.existsFile(file) || config.multiEnv) {\n    return;\n  }\n  var isGroup = util.isGroup(file);\n  var selectedList = isGroup || allowMultipleChoice() ? getSelectedRulesList() : [];\n  if (!isGroup && selectedList.indexOf(file) == -1) {\n    selectedList.push(file);\n    rulesStorage.setProperty('selectedList', selectedList);\n  }\n  parseRules();\n  proxy.emit('rulesDataChange', 'selectedList', selectedList);\n  return selectedList;\n}\n\nfunction unselectRulesFile(file, force, all) {\n  if (!force && config.multiEnv) {\n    return;\n  }\n  var selectedList = getSelectedRulesList();\n  var hasChanged;\n  all = all || [file];\n  all.forEach(function(name) {\n    var index = selectedList.indexOf(name);\n    if (index != -1) {\n      selectedList.splice(index, 1);\n      hasChanged = true;\n    }\n  });\n  if (hasChanged) {\n    rulesStorage.setProperty('selectedList', selectedList);\n    parseRules();\n    proxy.emit('rulesDataChange', 'selectedList', selectedList);\n  }\n  return selectedList;\n}\n\nfunction allowMultipleChoice() {\n  return (\n    !config.disabledMultipleOption &&\n    propertiesStorage.getProperty('allowMultipleChoice')\n  );\n}\n\nfunction clearSelection() {\n  rulesStorage.setProperty('selectedList', []);\n  proxy.emit('rulesDataChange', 'selectedList', []);\n  parseRules();\n}\n\nfunction getSelectedRulesList() {\n  if (config.multiEnv) {\n    return [];\n  }\n  var selectedList = rulesStorage.getProperty('selectedList');\n  if (!Array.isArray(selectedList)) {\n    selectedList = [];\n    rulesStorage.setProperty('selectedList', selectedList);\n  }\n  return selectedList;\n}\n\nfunction removeRulesFile(file, all) {\n  if (all) {\n    if (rulesStorage.removeGroup(file)) {\n      unselectRulesFile(file, true, all);\n      return true;\n    }\n    return false;\n  }\n  unselectRulesFile(file, true);\n  return rulesStorage.removeFile(file);\n}\n\nfunction renameRulesFile(file, newFile) {\n  if (!rulesStorage.renameFile(file, newFile)) {\n    return;\n  }\n\n  var selectedList = getSelectedRulesList();\n  var index = selectedList.indexOf(file);\n  if (index != -1) {\n    selectedList[index] = newFile;\n    rulesStorage.setProperty('selectedList', selectedList);\n  }\n  return true;\n}\n\nfunction addRulesFile(file, data) {\n  return rulesStorage.writeFile(file, data);\n}\n\nfunction getAllRulesFile() {\n  var list = rulesStorage.getFileList();\n  var selectedList = getSelectedRulesList();\n  list.forEach(function (file) {\n    file.selected = !util.isGroup(file.name) && selectedList.indexOf(file.name) != -1;\n  });\n  return list;\n}\n\nfunction getFirstGroup(storage) {\n  var list = storage.getFileList();\n  for (var i = 0, len = list.length; i < len; i++) {\n    if (util.isGroup(list[i].name)) {\n      return list[i];\n    }\n  }\n}\n\nfunction resetRulesIfResort(fromName, toName) {\n  var selectedList = getSelectedRulesList();\n  if (\n    selectedList.indexOf(fromName) == -1 &&\n    selectedList.indexOf(toName) == -1\n  ) {\n    return;\n  }\n  parseRules();\n}\n\nfunction moveRulesTo(fromName, toName, clientId, group, toTop) {\n  if (rulesStorage.moveTo(fromName, toName, group, toTop)) {\n    resetRulesIfResort(fromName, toName);\n    config.setModified(clientId, true);\n    proxy.emit('rulesDataChange', 'move', fromName, toName);\n    return true;\n  }\n}\n\nexports.rules = {\n  getConfig: function() {\n    var list = [\n      {\n        name: 'Default',\n        selected: !defaultRulesIsDisabled()\n      }\n    ];\n    var selectedList = getSelectedRulesList();\n    rulesStorage.getFileList().forEach(function (file) {\n      if (!util.isGroup(file.name)) {\n        list.push({ name: file.name, selected: selectedList.indexOf(file.name) !== -1 });\n      }\n    });\n    return {\n      pluginsDisabled: propertiesStorage.getProperty('disabledAllPlugins'),\n      disabled: !config.notAllowedDisableRules && propertiesStorage.getProperty('disabledAllRules'),\n      list: list\n    };\n  },\n  disableAllRules: function(disabled) {\n    propertiesStorage.setProperty('disabledAllRules', disabled);\n    parseRules();\n    proxy.emit('rulesDataChange', 'disabledAllRules', disabled);\n  },\n  getRawRulesText: function() {\n    return rules._rawRulesText || '';\n  },\n  getEnabledRules: function() {\n    return rules.getEnabledRules();\n  },\n  getMFlag: function() {\n    return rules.getMFlag();\n  },\n  moveGroupToTop: function(groupName, clientId) {\n    var result = rulesStorage.moveGroupToTop(groupName);\n    if (result) {\n      config.setModified(clientId, true);\n      proxy.emit('rulesDataChange', 'moveGroupToTop');\n    }\n    return result;\n  },\n  exists: function(name) {\n    return rulesStorage.existsFile(name);\n  },\n  recycleBin: rulesStorage.recycleBin,\n  enableBackRulesFirst: function (backRulesFirst) {\n    var curFlag = propertiesStorage.getProperty('backRulesFirst') === true;\n    if (curFlag !== backRulesFirst) {\n      propertiesStorage.setProperty('backRulesFirst', backRulesFirst);\n      parseRules();\n      proxy.emit('rulesDataChange', 'backRulesFirst', backRulesFirst);\n    }\n  },\n  getAllData: function() {\n    var list = getAllRulesFile();\n    list.unshift({\n      name: 'Default',\n      data: getDefaultRules(),\n      selected: !defaultRulesIsDisabled()\n    });\n    return {\n      backRulesFirst: propertiesStorage.getProperty('backRulesFirst') === true,\n      allowMultipleChoice: !!propertiesStorage.getProperty('allowMultipleChoice'),\n      list: list\n    };\n  },\n  moveTo: moveRulesTo,\n  moveToTop: function (name, clientId) {\n    var first = name && getAllRulesFile()[0];\n    first && moveRulesTo(name, first.name, clientId, null, true);\n  },\n  moveToGroup: function(name, groupName, isTop) {\n    if (rulesStorage.moveToGroup(name, groupName, isTop)) {\n      proxy.emit('rulesDataChange', 'moveToGroup', name, groupName, isTop);\n    }\n  },\n  get: function (file) {\n    return rulesStorage.readFile(file);\n  },\n  remove: function (file, clientId, all) {\n    if (removeRulesFile(file, all)) {\n      config.setModified(clientId, true);\n      proxy.emit('rulesDataChange', 'remove', file);\n    }\n  },\n  getFirstGroup: function() {\n    return getFirstGroup(rulesStorage);\n  },\n  add: function (file, data, clientId) {\n    if (file === 'Default') {\n      return;\n    }\n    var result = addRulesFile(file, data);\n    if (result) {\n      config.setModified(clientId, true);\n      proxy.emit('rulesDataChange', 'add', file);\n    }\n    return result;\n  },\n  rename: function (file, newFile, clientId) {\n    if (renameRulesFile(file, newFile)) {\n      config.setModified(clientId, true);\n      proxy.emit('rulesDataChange', 'rename', file, newFile);\n    }\n  },\n  select: selectRulesFile,\n  unselect: unselectRulesFile,\n  list: getAllRulesFile,\n  getDefault: getDefaultRules,\n  setDefault: function (value, clientId) {\n    if (setDefaultRules(value)) {\n      config.setModified(clientId, true);\n      proxy.emit('rulesDataChange', 'add', 'Default');\n    }\n  },\n  enableDefault: enableDefaultRules,\n  disableDefault: disableDefaultRules,\n  defaultRulesIsDisabled: defaultRulesIsDisabled,\n  parseRules: parseRules,\n  clearSelection: clearSelection,\n  getSelectedList: getSelectedRulesList\n};\n\n/**\n * values\n */\n\nfunction addValuesFile(file, data) {\n  return valuesStorage.writeFile(file, data);\n}\n\nexports.removeBatch = function(obj, body) {\n  var list = body.list;\n  var name = body.name;\n  if (!Array.isArray(list) || !list.length) {\n    list = [name];\n  }\n  list.forEach(function(item) {\n    obj.remove(item, body.clientId);\n  });\n};\n\nexports.values = {\n  recycleBin: valuesStorage.recycleBin,\n  exists: function(name) {\n    return valuesStorage.existsFile(name);\n  },\n  moveTo: function (fromName, toName, clientId, group, toTop) {\n    if (valuesStorage.moveTo(fromName, toName, group, toTop)) {\n      config.setModified(clientId);\n      proxy.emit('valuesDataChange', 'move', fromName, toName);\n      return true;\n    }\n  },\n  moveToGroup: function(name, groupName, isTop) {\n    if (valuesStorage.moveToGroup(name, groupName, isTop)) {\n      proxy.emit('valuesDataChange', 'moveToGroup', name, groupName, isTop);\n    }\n  },\n  getFirstGroup: function() {\n    return getFirstGroup(valuesStorage);\n  },\n  add: function (file, data, clientId) {\n    var result = addValuesFile(file, data);\n    if (result) {\n      config.setModified(clientId);\n      proxy.emit('valuesDataChange', 'add', file);\n    }\n    return result;\n  },\n  get: function (file) {\n    return valuesStorage.readFile(file);\n  },\n  remove: function remove(file, clientId, all) {\n    if (all ? valuesStorage.removeGroup(file) : valuesStorage.removeFile(file)) {\n      config.setModified(clientId);\n      proxy.emit('valuesDataChange', 'remove', file);\n    }\n  },\n  rename: function (file, newFile, clientId) {\n    if (valuesStorage.renameFile(file, newFile)) {\n      config.setModified(clientId);\n      proxy.emit('valuesDataChange', 'rename', file, newFile);\n    }\n  },\n  list: function list() {\n    return valuesStorage.getFileList();\n  }\n};\n\nsetTimeout(function getWhistleVersion() {\n  util.getLatestVersion('https://registry.npmjs.org/whistle', function (ver) {\n    ver && propertiesStorage.writeFile('latestVersion', ver);\n    setTimeout(getWhistleVersion, INTERVAL);\n  });\n  if (config.checkUpdateClient) {\n    util.getLatestVersion('https://raw.githubusercontent.com/avwo/whistle-client/main/package.json', function (ver) {\n      ver && propertiesStorage.writeFile('latestClientVersion', ver);\n    }, 'version' );\n  }\n}, 1000); //等待package的信息配置更新完成\n\nfunction setEnableCapture(enable) {\n  config.isEnableCapture = enable;\n  propertiesStorage.setProperty('interceptHttpsConnects', enable);\n}\n\nif (config.persistentCapture && config.isEnableCapture) {\n  setEnableCapture(true);\n}\n\nexports.setDisabledCertFile = function(filename, disabled) {\n  var len = util.isString(filename) ? filename.length : 0;\n  if (!len || len > 100) {\n    return;\n  }\n  var list = propertiesStorage.getProperty('disabledCertFiles');\n  if (!Array.isArray(list)) {\n    list = [];\n  }\n  var index = list.indexOf(filename);\n  if (disabled) {\n    if (index !== -1) {\n      list.splice(index, 1);\n    }\n    list.push(filename);\n    if (list.length > 256) {\n      list = list.slice(-256);\n    }\n    propertiesStorage.setProperty('disabledCertFiles', list);\n  } else if (index !== -1) {\n    list.splice(index, 1);\n    propertiesStorage.setProperty('disabledCertFiles', list);\n  }\n};\n\nexports.isDisabledCertFile = function(filename) {\n  var list = propertiesStorage.getProperty('disabledCertFiles');\n  return Array.isArray(list) && list.indexOf(filename) !== -1;\n};\n\nexports.properties = {\n  setIPv6Only: function(checked) {\n    checked = !!checked;\n    propertiesStorage.setProperty('ipv6Only', checked);\n    config.ipv6Only = checked;\n  },\n  setDnsOrder: function(order) {\n    if (config.setDefaultResultOrder(order)) {\n      order = +order;\n      config.dnsOrder = order;\n      propertiesStorage.setProperty('dnsOrder', order);\n    }\n  },\n  getLatestVersion: function (name) {\n    var version = propertiesStorage.readFile(name || 'latestVersion');\n    return typeof version === 'string' && version.length < 60 ? version : '';\n  },\n  isEnableCapture: function () {\n    if (config.multiEnv || config.notAllowedEnableHTTPS) {\n      return false;\n    }\n    if (config.isEnableCapture != null) {\n      return config.isEnableCapture;\n    }\n    return !!propertiesStorage.getProperty('interceptHttpsConnects');\n  },\n  setEnableCapture: setEnableCapture,\n  isEnableHttp2: function () {\n    if (config.isEnableHttp2 != null) {\n      return config.isEnableHttp2;\n    }\n    return propertiesStorage.getProperty('enableHttp2') !== false;\n  },\n  setEnableHttp2: function (enable) {\n    config.isEnableHttp2 = enable;\n    propertiesStorage.setProperty('enableHttp2', enable);\n  },\n  set: function (name, value) {\n    typeof name == 'string'\n      ? propertiesStorage.setProperty(name, value)\n      : propertiesStorage.setProperties(name);\n  },\n  remove: function (name) {\n    propertiesStorage.removeProperty(name);\n  },\n  get: function (name) {\n    return propertiesStorage.getProperty(name);\n  }\n};\n\nfunction getRules(rules) {\n  if (Array.isArray(rules)) {\n    return rules.join('\\n');\n  }\n  if (typeof rules === 'string') {\n    return rules;\n  }\n}\n\nfunction getKeys(obj) {\n  var list = obj[''];\n  var keys = Object.keys(obj);\n  list = list && (Array.isArray(list) ? list : Array.isArray(list.list) ? list.list : null);\n  if (!list) {\n    return keys;\n  }\n  delete obj[''];\n  var result = [];\n  var count = 0;\n  list = list.concat(keys);\n  for (var i = 0, len = list.length; i < len; i++) {\n    var name = list[i];\n    if (util.isString(name) && (result.indexOf(name) === -1)) {\n      if (++count > MAX_COUNT_BY_IMPORT) {\n        return result;\n      }\n      result.push(name);\n    }\n  }\n  return result;\n}\n\nexports.addRules = function (rules, replace, clientId) {\n  if (rules == null) {\n    return;\n  }\n  replace = replace !== false;\n  var hasChanged;\n  if (Array.isArray(rules) || typeof rules == 'string') {\n    if (replace !== false || !getDefaultRules()) {\n      hasChanged = setDefaultRules(getRules(rules));\n    }\n  } else {\n    getKeys(rules).forEach(function (name) {\n      var item = name ? rules[name] : null;\n      if (Array.isArray(item) || typeof item === 'string') {\n        item = { rules: item };\n      }\n      if (item) {\n        item.rules = getRules(item.rules);\n        if (typeof item.replace !== 'boolean') {\n          item.replace = replace;\n        }\n        if (name === 'Default') {\n          if (\n            typeof item.rules === 'string' &&\n            (item.replace !== false || !getDefaultRules())\n          ) {\n            if (setDefaultRules(item.rules)) {\n              hasChanged = true;\n            }\n          }\n          if (item.enable) {\n            enableDefaultRules();\n          } else if (item.enable === false) {\n            disableDefaultRules();\n          }\n        } else {\n          if (\n            typeof item.rules === 'string' &&\n            (item.replace !== false || !rulesStorage.existsFile(name))\n          ) {\n            if (addRulesFile(name, item.rules)) {\n              hasChanged = true;\n            }\n          }\n          if (item.enable) {\n            selectRulesFile(name);\n          } else if (item.enable === false) {\n            unselectRulesFile(name);\n          }\n        }\n      }\n    });\n  }\n  if (hasChanged) {\n    config.setModified(clientId, true);\n    proxy.emit('rulesDataChange', 'addRules');\n  }\n};\n\nexports.addValues = function (values, replace, clientId) {\n  if (values == null || Array.isArray(values)) {\n    return;\n  }\n  replace = replace !== false;\n  var hasChanged;\n  getKeys(values).forEach(function (name) {\n    var isGroup = name[0] === '\\r';\n    name = name.trim();\n    if (!name || SPACE_RE.test(name)) {\n      return;\n    }\n    if (isGroup) {\n      name = '\\r' + name;\n    }\n    if (!replace && valuesStorage.existsFile(name)) {\n      return;\n    }\n    var value = name ? values[name] : null;\n    if (value == null) {\n      return;\n    }\n    if (typeof value !== 'string') {\n      value = JSON.stringify(value, null, '  ') || '';\n    }\n    if (addValuesFile(name, value)) {\n      hasChanged = true;\n    }\n  });\n  if (hasChanged) {\n    config.setModified(clientId);\n    proxy.emit('valuesDataChange', 'addValues');\n  }\n};\n\nexports.setup = function (p) {\n  proxy = p;\n};\n\nexports.setServiceRules = function(rulesText) {\n  if (typeof rulesText === 'string') {\n    serviceRules = rulesText;\n    parseRules();\n  }\n};\n\nexports.setMockRules = function(rulesText) {\n  if (typeof rulesText === 'string') {\n    mockRules = rulesText;\n    parseRules();\n  }\n};\n\nexports.setPluginMgr = function(p) {\n  pluginMgr = p;\n};\n"
  },
  {
    "path": "lib/service/compose-data.js",
    "content": "var LRU = require('lru-cache');\n\nvar cache = new LRU({ max: 120, maxAge: 1000 * 60 });\nvar MAX_SIZE = 1024 * 1280;\n\nfunction getData(reqId) {\n  return cache.get(reqId);\n}\n\nexports = module.exports = function(req, res) {\n  var ids = req.query.ids;\n  var result = {};\n  ids = ids && typeof ids === 'string' ? ids.split(',') : ids;\n  if (!ids) {\n    return res.json(result);\n  }\n  ids.forEach(function(reqId) {\n    var curBuf = getData(reqId);\n    if (curBuf) {\n      if (curBuf._hasW2End) {\n        cache.del(reqId);\n      } else {\n        cache.set(reqId, '');\n      }\n    }\n    result[reqId] = curBuf ? curBuf.toString('base64') : curBuf;\n  });\n  res.json(result);\n};\n\nexports.setData = function(reqId, buffer, init) {\n  var curBuf = cache.get(reqId);\n  if (!init && curBuf == null) {\n    return false;\n  }\n  if (curBuf) {\n    if (buffer && curBuf.length < MAX_SIZE) {\n      cache.set(reqId, Buffer.concat([curBuf, buffer]));\n    }\n  } else {\n    cache.set(reqId, buffer || '');\n  }\n  return true;\n};\n\nexports.getData = getData;\n\nexports.removeData = function(reqId) {\n  cache.del(reqId);\n};\n"
  },
  {
    "path": "lib/service/composer.js",
    "content": "var http = require('http');\nvar gzip = require('zlib').gzip;\nvar tls = require('tls');\nvar crypto = require('crypto');\nvar extend = require('extend');\nvar common = require('../util/common');\nvar getSender = require('ws-parser').getSender;\nvar hparser = require('hparser');\nvar composeData = require('./compose-data');\nvar Storage = require('../rules/storage');\nvar dataCenter = require('./data-center');\n\nvar formatHeaders = hparser.formatHeaders;\nvar getRawHeaders = hparser.getRawHeaders;\nvar getRawHeaderNames = hparser.getRawHeaderNames;\nvar config = {};\nvar noop = common.noop;\nvar parseReq = hparser.parse;\nvar MAX_LENGTH = 1024 * 2048;\nvar MAX_REQ_COUNT = 100;\nvar propertiesStorage;\nvar TLS_PROTOS = 'https:,wss:,tls:'.split(',');\nvar PROXY_OPTS = {};\nvar composerHistory = [];\nvar MAX_HISTORY_LEN = 100;\nvar MAX_URL_LEN = 10 * 1024;\nvar MAX_HEADERS_LEN = 128 * 1024;\nvar MAX_BODY_LEN = 260 * 1024;\nvar MAX_BASE64_LEN = 360 * 1024;\nvar MAX_METHOD_LEN = 64;\nvar LEN_HEADER = 'content-length';\n\nvar composerTimer;\nfunction saveComposerHistory() {\n  composerTimer = null;\n  try {\n    propertiesStorage.writeFile('composerHistory', JSON.stringify(composerHistory));\n  } catch (e) {}\n}\n\nfunction handleComposerHistory(data) {\n  var url = data.url;\n  var method = data.method;\n  var headers = data.headers;\n  var body = data.body;\n  var base64 = data.base64;\n  if (body || !base64 || typeof base64 !== 'string' || base64.length > MAX_BASE64_LEN) {\n    base64 = undefined;\n  }\n  var result = {\n    date: Date.now(),\n    useH2: data.useH2,\n    url: url.length > MAX_URL_LEN ? url.substring(0, MAX_URL_LEN) : url,\n    method:\n        method.length > MAX_METHOD_LEN\n          ? method.substring(0, MAX_METHOD_LEN)\n          : method,\n    headers:\n        headers.length > MAX_HEADERS_LEN\n          ? headers.substring(0, MAX_HEADERS_LEN)\n          : headers,\n    body: body.length > MAX_BODY_LEN ? body.substring(0, MAX_BODY_LEN) : body,\n    isHexText: !!data.isHexText,\n    base64: base64,\n    enableProxyRules: !!data.enableProxyRules\n  };\n  var dataHash;\n  var base64Hash;\n  for (var i = 0, len = composerHistory.length; i < len; i++) {\n    var item = composerHistory[i];\n    if (\n        item.url === result.url &&\n        item.method === result.method &&\n        item.headers === result.headers &&\n        item.body === result.body &&\n        item.base64 === result.base64 &&\n        !item.useH2 !== result.useH2 &&\n        !item.enableProxyRules !== result.enableProxyRules\n      ) {\n      dataHash = item.dataHash;\n      base64Hash = item.base64Hash;\n      composerHistory.splice(i, 1);\n      break;\n    }\n  }\n  result.dataHash = dataHash;\n  result.base64Hash = base64Hash;\n  composerHistory.unshift(result);\n  var overflow = composerHistory.length - MAX_HISTORY_LEN;\n  if (overflow > 0) {\n    composerHistory.splice(MAX_HISTORY_LEN, overflow);\n  }\n  composerTimer = composerTimer || setTimeout(saveComposerHistory, 2000);\n}\n\nfunction parseHeaders(headers, rawHeaderNames, clientId) {\n  var type = headers && typeof headers;\n  if (type != 'string' && type !== 'object') {\n    return {};\n  }\n\n  var reqHeaders = type === 'object' ? headers : common.parseRawJson(headers);\n  if (reqHeaders) {\n    reqHeaders = common.lowerCaseify(reqHeaders, rawHeaderNames);\n  } else {\n    reqHeaders = common.parseHeaders(headers, rawHeaderNames);\n  }\n  if (clientId && reqHeaders[config.CLIENT_ID_HEADER] !== clientId) {\n    reqHeaders[config.COMPOSER_CLIENT_ID_HEADER] = clientId;\n  }\n  return reqHeaders;\n}\n\nfunction drain(socket) {\n  socket.on('error', noop);\n  socket.on('data', noop);\n}\n\nfunction getReqCount(count) {\n  return count > 0 ? Math.min(count, MAX_REQ_COUNT) : 1;\n}\n\nfunction handleConnect(options, cb, count) {\n  count = getReqCount(count);\n  options.headers['x-whistle-policy'] = 'tunnel';\n  var origOpts = options;\n  var lastIndex = count - 1;\n  for (var i = 0; i < count; i++) {\n    var execCb;\n    if (i === lastIndex) {\n      execCb = cb;\n    } else {\n      options = extend({}, origOpts);\n    }\n    common.connectInner({\n      host: options.hostname,\n      port: options.port || 443,\n      proxyHost: PROXY_OPTS.host,\n      proxyPort: PROXY_OPTS.port,\n      headers: options.headers\n    }, function(socket, svrRes, err) {\n      if (err) {\n        return execCb && execCb(err);\n      }\n      if (TLS_PROTOS.indexOf(options.protocol) !== -1) {\n        socket = tls.connect({\n          rejectUnauthorized: config.rejectUnauthorized,\n          socket: socket,\n          servername: options.hostname\n        });\n      }\n      drain(socket);\n      var data = options.body;\n      if (data && data.length) {\n        socket.write(data);\n        options.body = data = null;\n      }\n      execCb && execCb(null, {\n        statusCode: svrRes.statusCode,\n        headers: svrRes.headers\n      });\n    }, config).on('error', execCb || noop);\n  }\n}\n\nfunction getReqRaw(options) {\n  var headers = options.headers;\n  var statusLine = options.method +' ' + (options.path || '/') +' ' + 'HTTP/1.1';\n  var raw = statusLine;\n  if (headers = getRawHeaders(headers)) {\n    raw += '\\r\\n' + headers;\n  }\n  return raw + '\\r\\n\\r\\n';\n}\n\nfunction handleWebSocket(options, cb, count) {\n  count = getReqCount(count);\n  if (options.protocol === 'https:' || options.protocol === 'wss:') {\n    options.headers[common.HTTPS_FIELD] = 1;\n  }\n  var binary = !!options.headers['x-whistle-frame-binary'];\n  delete options.headers['x-whistle-frame-binary'];\n  var origOpts = options;\n  var lastIndex = count - 1;\n  for (var i = 0; i < count; i++) {\n    var execCb;\n    if (i === lastIndex) {\n      execCb = cb;\n    } else {\n      options = extend({}, origOpts);\n    }\n    common.connect(PROXY_OPTS, function(err, socket) {\n      if (err) {\n        execCb && execCb(err);\n      } else {\n        socket.write(getReqRaw(options));\n        var data = options.body;\n        if ((!data || !data.length) && !cb) {\n          return drain(socket);\n        }\n        parseReq(socket, function(e) {\n          if (e) {\n            socket.destroy();\n            return execCb && execCb(e);\n          }\n          var statusCode = socket.statusCode;\n          if (statusCode == 101) {\n            if (data) {\n              if (common.isWebSocket(socket.headers)) {\n                getSender(socket).send(data, {\n                  mask: true,\n                  binary: binary\n                }, noop);\n              } else {\n                socket.write(data);\n              }\n              options.body = data = null;\n            }\n            socket.body = '';\n            drain(socket);\n          } else {\n            socket.destroy();\n          }\n          execCb && execCb(null, {\n            statusCode: statusCode,\n            headers: socket.headers || {}\n          });\n        }, true);\n      }\n    });\n  }\n}\n\nfunction handleHttp(options, cb, count, reqId) {\n  count = getReqCount(count);\n  if (options.protocol === 'https:') {\n    options.headers[common.HTTPS_FIELD] = 1;\n  }\n  options.protocol = null;\n  options.hostname = null;\n  options.host = PROXY_OPTS.host;\n  options.port = PROXY_OPTS.port;\n  var origOpts = options;\n  var lastIndex = count - 1;\n  for (var i = 0; i < count; i++) {\n    var execCb;\n    if (i === lastIndex) {\n      execCb = cb;\n    } else {\n      options = extend({}, origOpts);\n    }\n    var client = http.request(options, function(svrRes) {\n      if (!execCb) {\n        return drain(svrRes);\n      }\n      svrRes.on('error', execCb);\n      var buffer;\n      var enableStream;\n      var unzipStream = common.getUnzipStream(svrRes.headers);\n      var timer = reqId && setTimeout(function() {\n        composeData.setData(reqId, buffer, true);\n        enableStream = true;\n        timer = buffer = undefined;\n        handleResponse();\n        common.onResEnd(svrRes, function() {\n          var buf = composeData.getData(reqId);\n          if (buf) {\n            buf._hasW2End = true;\n          } else {\n            composeData.removeData(reqId);\n          }\n        });\n      }, 3000);\n      var handleResponse = function() {\n        if (buffer === null) {\n          return;\n        }\n        timer && clearTimeout(timer);\n        var headers = svrRes.headers;\n        if (typeof headers.trailer === 'string' && headers.trailer.indexOf(',') !== -1) {\n          headers.trailer = headers.trailer.split(',');\n        }\n        var result = {\n          statusCode: svrRes.statusCode,\n          headers: headers,\n          trailers: svrRes.trailers,\n          rawHeaderNames: getRawHeaderNames(svrRes.rawHeaders),\n          rawTrailerNames: getRawHeaderNames(svrRes.rawTrailers),\n          reqId: timer ? undefined : reqId\n        };\n        result.base64 = buffer && buffer.toString('base64');\n        execCb(null, result);\n        buffer = null;\n      };\n      if (unzipStream) {\n        unzipStream.on('error', function(err) {\n          drain(svrRes);\n          execCb(err);\n        });\n        svrRes.pipe(unzipStream);\n      } else {\n        unzipStream = svrRes;\n      }\n      unzipStream.on('data', function(data) {\n        if (enableStream) {\n          if (!composeData.setData(reqId, data)) {\n            enableStream = false;\n            drain(svrRes);\n            svrRes.unpipe(unzipStream);\n          }\n        } else if (buffer !== null) {\n          buffer = buffer ? Buffer.concat([buffer, data]) : data;\n          if (buffer.length > MAX_LENGTH) {\n            handleResponse();\n            if (unzipStream !== svrRes) {\n              drain(svrRes);\n              svrRes.unpipe(unzipStream);\n            }\n          }\n        }\n      });\n      unzipStream.on('end', handleResponse);\n    });\n    client.on('error', execCb || noop);\n    client.end(options.body);\n    options.body = null;\n  }\n}\n\nfunction getCharset(headers) {\n  var charset = headers && headers['x-whistle-charset'];\n  return charset || common.getCharset(headers['content-type']);\n}\n\nexports.handleRequest = function(req, res) {\n  var fullUrl = req.body.url;\n  if (!fullUrl || typeof fullUrl !== 'string') {\n    return res.json({ec: 0});\n  }\n\n  fullUrl = common.encodeNonLatin1Char(fullUrl.replace(/#.*$/, ''));\n  var options = common.parseUrl(common.setProtocol(fullUrl));\n  if (!options.host) {\n    return res.json({ec: 0});\n  }\n  var protocol = options.protocol;\n  if (protocol) {\n    options.protocol = protocol = protocol.toLowerCase();\n  }\n  var rawHeaderNames = {};\n  var clientId = req.headers[config.CLIENT_ID_HEADER];\n  var headers = parseHeaders(req.body.headers, rawHeaderNames, clientId);\n  var method = common.getMethod(req.body.method);\n  var isWebSocket = method === 'WEBSOCKET';\n  delete headers[config.WEBUI_HEAD];\n  headers[config.FROM_COM_HEADER] = '1';\n  if (req.body.enableProxyRules === false) {\n    headers[config.DISABLE_RULES_HEADER] = '1';\n  }\n  headers.host = options.host;\n  options.clientId = clientId;\n  headers[config.CLIENT_IP_HEADER] = req.headers[config.CLIENT_IP_HEADER] || '127.0.0.1';\n  headers[config.CLIENT_PORT_HEADER] = common.getClientPort(req, config);\n  options.method = method;\n\n  var isConn = common.isConnect(options);\n  var isWs = !isConn && (isWebSocket || common.isUpgrade(options, headers));\n  var useH2 = req.body.useH2 || req.body.isH2;\n  req.body.useH2 = false;\n  if (isWs) {\n    headers.connection = 'Upgrade';\n    headers.upgrade = (!isWebSocket && headers.upgrade) || 'websocket';\n    headers['sec-websocket-version'] = 13;\n    if (isWebSocket || common.isWebSocket(headers)) {\n      headers['sec-websocket-key'] = crypto.randomBytes(16).toString('base64');\n    }\n  } else {\n    delete headers.upgrade;\n    if (!isConn && ((useH2 && (protocol === 'https:' || protocol === 'http:')) || protocol === 'h2:' || protocol === 'http2:')) {\n      req.body.useH2 = true;\n      var isHttp = protocol === 'http:';\n      options.protocol = isHttp ? 'http:' : 'https:';\n      if (!headers[config.ALPN_PROTOCOL_HEADER]) {\n        headers[config.ALPN_PROTOCOL_HEADER] = (isHttp ? 'httpH2' : 'h2');\n      }\n    }\n  }\n  if (!req.body.noStore && req.body.needResponse && common.checkHistory(req.body)) {\n    handleComposerHistory(req.body);\n    dataCenter.saveData({\n      type: 'composer',\n      history: composerHistory\n    });\n  }\n\n  var getBody = function(cb) {\n    var base64 = req.body.base64;\n    var body = base64 || req.body.body;\n    if (!isWs) {\n      delete headers.trailer;\n    }\n    if (isWs || isConn || common.hasRequestBody(options)) {\n      body = body && common.toBuffer(body, base64 ? 'base64' : getCharset(headers));\n      options.body = body;\n      if (!isWs && !isConn && body && req.body.isGzip) {\n        gzip(body, function(err, gzipData) {\n          if (err) {\n            return cb(err);\n          }\n          headers['content-encoding'] = 'gzip';\n          if (LEN_HEADER in headers) {\n            headers[LEN_HEADER] = gzipData.length;\n          } else {\n            delete headers[LEN_HEADER];\n          }\n          options.body = gzipData;\n          cb();\n        });\n        return;\n      }\n      if (LEN_HEADER in headers) {\n        if (isWs || isConn) {\n          delete headers[LEN_HEADER];\n        } else {\n          headers[LEN_HEADER] = body ? body.length : '0';\n        }\n      }\n    } else {\n      delete headers[LEN_HEADER];\n    }\n    delete headers['content-encoding'];\n    cb();\n  };\n  getBody(function(err) {\n    options.headers = formatHeaders(headers, rawHeaderNames);\n    var done;\n    var needResponse = req.query.needResponse || req.body.needResponse;\n    var handleResponse = needResponse ? function(err, data) {\n      if (done) {\n        return;\n      }\n      done = true;\n      if (err) {\n        res.json({ec: 0, res: {\n          statusCode:  err.statusCode ? parseInt(err.statusCode, 10) : 502,\n          headers: '',\n          body: err.stack\n        }});\n        return;\n      }\n      common.sendGzip(req, res, {ec: 0, em: 'success', res: data || ''});\n    } : null;\n    if (err) {\n      return handleResponse && handleResponse(err);\n    }\n    var count = req.body.repeatCount;\n    count = count > 0 ? count : req.body.repeatTimes;\n    if (isWs) {\n      options.method = 'GET';\n      handleWebSocket(options, handleResponse, count);\n    } else if (isConn) {\n      handleConnect(options, handleResponse, count);\n    } else  {\n      handleHttp(options, handleResponse, count, req.body.reqId);\n    }\n    if (!handleResponse) {\n      res.json({ec: 0, em: 'success'});\n    }\n  });\n};\n\nexports.getHistory = function(req, res) {\n  common.sendGzip(req, res, composerHistory);\n};\n\nexports.setup = function(conf) {\n  config = conf;\n  PROXY_OPTS = {\n    host: config.host || '127.0.0.1',\n    port: config.port\n  };\n  propertiesStorage = new Storage(config.propertiesDir, null, false,  { composerHistory: true });\n  var history = propertiesStorage.readFile('composerHistory');\n  try {\n    composerHistory = JSON.parse(history);\n  } catch (e) {}\n  if (Array.isArray(composerHistory)) {\n    composerHistory = composerHistory.filter(common.checkHistory);\n    composerHistory = composerHistory.slice(0, MAX_HISTORY_LEN);\n  } else {\n    composerHistory = [];\n  }\n  dataCenter.saveComposeData(composerHistory);\n};\n"
  },
  {
    "path": "lib/service/data-center.js",
    "content": "var common = require('../util/common');\nvar request = require('../util/http-mgr').request;\nvar util = require('./util');\nvar extend = require('extend');\n\nvar requestData;\nvar BASE_URL = 'https://' + common.SERVICE_HOST;\nvar TEMP_FILE_RE = /\\b[\\da-f]{64}\\b/g;\nvar MAX_COUNT = 100;\nvar tempFiles = [];\nvar config;\nvar savedDir;\nvar savedTimer;\nvar curWhistleId;\nvar historyList;\n\nvar getQueryString = function(data) {\n  return '?type=' + encodeURIComponent(data.type) + (data.filename ? '&filename=' + encodeURIComponent(data.filename) : '');\n};\n\nvar getReqOptions = function(data, url) {\n  var body = Buffer.from(typeof data === 'string' ? data : JSON.stringify(data));\n  var len = body.length;\n  return len > 2 && {\n    method: 'POST',\n    url: (url || '/service/cgi/save') + getQueryString(data),\n    strictMode: true,\n    headers: {\n      'content-type': 'application/octet-stream',\n      'content-length': len\n    },\n    body: body\n  };\n};\n\nvar saveToService = function(data) {\n  if (!data.data) {\n    return;\n  }\n  requestData(getReqOptions(data));\n};\n\nvar shareData = function(data, cb) {\n  if (!data.data) {\n    return;\n  }\n  requestData(getReqOptions(data, '/service/cgi/share'), cb);\n};\n\nvar parseTempFiles = function(rules) {\n  if (!Array.isArray(rules)) {\n    return;\n  }\n  var isChanged = false;\n  var list = rules.map(function(rule) {\n    return rule && rule.value || '';\n  }).join('\\n').match(TEMP_FILE_RE);\n  list && list.forEach(function(file) {\n    if (tempFiles.indexOf(file) === -1) {\n      isChanged = true;\n      tempFiles.push(file);\n    }\n  });\n  if (isChanged) {\n    saveTempFiles();\n  }\n};\n\nvar saveComposeData = function(data) {\n  historyList = data || historyList;\n  if (!config.whistleToken || !Array.isArray(historyList)) {\n    return;\n  }\n  historyList.map(function(item) {\n    var newItem = extend({}, item);\n    var rawData = item.headers + '\\r\\n\\r\\n' + (item.base64 || item.body || '');\n    if (!item.dataHash && !item.base64Hash) {\n      item[item.base64 ? 'base64Hash' : 'dataHash'] = util.getHexHash(rawData);\n    }\n    delete newItem.headers;\n    delete newItem.body;\n    delete newItem.base64;\n    return newItem;\n  });\n};\n\nvar descSorter = function(a, b) {\n  var flag = b.time - a.time;\n  if (flag === 0) {\n    return 0;\n  }\n  return flag > 0 ? 1 : -1;\n};\n\nvar removeSavedData = function(filename) {\n  if (!filename || !config.whistleToken) {\n    return;\n  }\n};\n\nvar saveSavedData = function(filename, buffer) {\n  clearTimeout(savedTimer);\n  if (!config.whistleId) {\n    return;\n  }\n  util.getSavedList(savedDir, function(err, list) {\n    if (err) {\n      savedTimer = setTimeout(saveSavedData, 1000);\n      return;\n    }\n    list = list.sort(descSorter).slice(0, MAX_COUNT);\n  });\n};\n\nvar saveTempFiles = function() {\n  if (!tempFiles.length) {\n    return;\n  }\n};\n\nvar saveData = function(data) {\n  if (!config.whistleToken) {\n    return;\n  }\n  // Initialize curWhistleId on first save\n  if (!curWhistleId) {\n    curWhistleId = data.whistleId;\n    saveComposeData();\n    saveSavedData();\n  }\n  if (data.type === 'data') {\n    parseTempFiles(data.rules);\n    saveToService({\n      type: 'certs',\n      data: data.certs\n    });\n    saveToService({\n      type: 'rules',\n      data: data.rules\n    });\n    saveToService({\n      type: 'values',\n      data: data.values\n    });\n    saveToService({\n      type: 'plugins',\n      data: data.plugins\n    });\n  } else if (data.type === 'settings') {\n    saveToService({\n      type: 'networkSettings',\n      data: data.networkSettings\n    });\n    saveToService({\n      type: 'rulesSettings',\n      data: data.rulesSettings\n    });\n    saveToService({\n      type: 'valuesSettings',\n      data: data.valuesSettings\n    });\n  } else if (data.type === 'composer') {\n    saveComposeData(data.history);\n  } else if (data.type === 'savedData') {\n    saveSavedData(data.filename, data.buffer);\n  }\n};\n\nexports.saveData = saveData;\nexports.shareData = shareData;\n\nexports.removeSavedData = removeSavedData;\nexports.saveComposeData = saveComposeData;\n\nexports.forwardRequest = function(req, res) {\n  var index = req.url && req.url.indexOf('?');\n  var url = req.path + (index > -1 ? req.url.substring(index) : '');\n  requestData({\n    method: req.method,\n    url: url,\n    body: req,\n    responseType: 'stream'\n  }, function(err, svRes) {\n    if (err) {\n      return common.sendRes(res, 500, (err && err.message) || 'Internal Server Error');\n    }\n    res.writeHead(svRes.statusCode, svRes.headers);\n    svRes.pipe(res);\n  });\n};\n\nexports.requestData = function(opts, cb) {\n  return requestData(opts, cb);\n};\n\nexports.setup = function(options) {\n  config = options;\n  savedDir = config.SAVED_SESSIONS_PATH;\n  requestData = function(opts, cb) {\n    if (!opts) {\n      return cb && cb(new Error('Bad request'));\n    }\n    if (typeof opts === 'string') {\n      opts = { url: opts };\n    } else {\n      opts.pluginName = null;\n    }\n    opts.responseType = opts.responseType || 'json';\n    opts.url = BASE_URL + (opts.url[0] === '/' ? '' : '/') + opts.url;\n    return request(common.setInternalOptions(opts, {\n      PROXY_ID_HEADER: config.PROXY_ID_HEADER,\n      host: config.host,\n      port: config.port\n    }, true), cb || common.noop);\n  };\n  saveSavedData();\n};\n"
  },
  {
    "path": "lib/service/extract-saz.js",
    "content": "var AdmZip = require('adm-zip');\nvar parseString = require('xml2js').parseString;\nvar parseUrl = require('../util/parse-url-safe');\nvar common = require('../util/common');\nvar util = require('./util');\n\nfunction getMetaAttrs(meta) {\n  meta = meta && meta.Session;\n  if (!meta) {\n    return {};\n  }\n  var result = meta.SessionTimers && meta.SessionTimers[0];\n  result = (result && result.$) || {};\n  var SessionFlag =\n    meta.SessionFlags &&\n    meta.SessionFlags[0] &&\n    meta.SessionFlags[0].SessionFlag;\n  if (Array.isArray(SessionFlag)) {\n    SessionFlag.forEach(function (flag) {\n      flag = flag && flag.$;\n      if (!flag || typeof flag.N !== 'string') {\n        return;\n      }\n      result[flag.N] = flag.V || '';\n    });\n  }\n  return result;\n}\n\nfunction parseMetaInfo(result) {\n  var req = result.req;\n  if (!req) {\n    return false;\n  }\n  var port;\n  if (/^[^:/]+:\\/\\//.test(req.url)) {\n    var options = parseUrl(req.url);\n    if (!req.headers.host) {\n      req.headers.host = options.host;\n    }\n    req.isHttps = /^https:/i.test(req.url);\n    port = options.port || (req.isHttps ? 443 : 80);\n    req.url = options.path;\n  } else if (typeof req.headers.host !== 'string') {\n    req.headers.host = '';\n  }\n\n  var meta = getMetaAttrs(result.meta);\n  if (!result.startTime) {\n    var startTime = (result.startTime =\n      new Date(meta.ClientConnected).getTime() || 0);\n    if (meta.DNSTime >= 0) {\n      startTime = result.dnsTime = +startTime + +meta.DNSTime;\n    }\n    if (meta.ClientDoneRequest) {\n      var requestTime = new Date(meta.ClientDoneRequest).getTime() || 0;\n      startTime = result.requestTime = Math.max(startTime, requestTime);\n    }\n    if (meta.ClientBeginResponse) {\n      var responseTime = new Date(meta.ClientBeginResponse).getTime() || 0;\n      startTime = result.responseTime = Math.max(startTime, responseTime);\n    }\n    if (meta.ClientDoneResponse) {\n      var endTime = new Date(meta.ClientDoneResponse).getTime() || 0;\n      result.endTime = Math.max(endTime, startTime);\n    }\n  }\n  if (meta['x-ttfb'] >= 0) {\n    result.ttfb = +meta['x-ttfb'];\n  }\n  result.rules = result.rules || {};\n  var res = (result.res = result.res || {});\n  result.hostIp = res.ip = util.removeIPV6Prefix(meta['x-hostip']);\n  res.port = meta['x-serverport'] || port;\n  result.clientIp = req.ip = util.removeIPV6Prefix(meta['x-clientip']);\n  var clientPort = meta['x-clientport'];\n  if (clientPort) {\n    req.port = clientPort;\n  }\n  var comment = meta['ui-comments'];\n  if (comment && typeof comment === 'string') {\n    result.customData = result.customData || {};\n    result.customData.comment = comment;\n  }\n  var size = meta['x-transfer-size'] || meta['x-responsebodytransferlength'];\n  if (typeof size === 'string') {\n    size = parseInt(size.replace(/\\s*,\\s*/g, ''), 10);\n  }\n  if (size > -1) {\n    res.size = size;\n  }\n  if (req.method === 'CONNECT') {\n    result.url = req.url;\n    result.isHttps = true;\n  } else {\n    result.url =\n      'http' + (req.isHttps ? 's' : '') + '://' + req.headers.host + req.url;\n    if (/\\bwebsocket\\b/i.test(req.headers.upgrade)) {\n      result.url = result.url.replace(/^http/, 'ws');\n    }\n  }\n}\n\nfunction sortKeys(cur, next) {\n  return parseInt(cur, 10) - parseInt(next, 10);\n}\n\nmodule.exports = function (buffer, cb) {\n  var zip = new AdmZip(buffer);\n  var zipEntries = zip.getEntries();\n  var sessions = {};\n  var count = 0;\n  var execCallback = function () {\n    if (count <= 0) {\n      var result = [];\n      var wsLen = 0;\n      Object.keys(sessions)\n        .sort(sortKeys)\n        .forEach(function (key) {\n          var session = sessions[key];\n          if (session.req && session.meta) {\n            if (parseMetaInfo(session) !== false) {\n              result.push(session);\n            }\n          }\n          if (session._url) {\n            session.url = session._url;\n            delete session._url;\n          }\n          if (session.trailers && session.res) {\n            session.res.trailers = session.trailers;\n            session.res.rawTrailerNames = session.rawTrailerNames;\n            delete session.trailers;\n            delete session.rawTrailerNames;\n          }\n          var framesData = session.framesData;\n          if (framesData) {\n            delete session.framesData;\n            ++wsLen;\n            util.parseFrames(session.res, framesData, function (frames) {\n              session.frames = frames;\n              if (--wsLen === 0) {\n                wsLen = -1;\n                cb(result);\n              }\n            });\n          }\n        });\n      if (wsLen === 0) {\n        wsLen = -1;\n        cb(result);\n      }\n    }\n  };\n  zipEntries.forEach(function (entry) {\n    if (entry.isDirectory) {\n      return;\n    }\n    var entryName = entry.entryName;\n    var filename = entryName.substring(4);\n    var dashIndex = filename.lastIndexOf('_');\n    if (dashIndex <= 0) {\n      return;\n    }\n    var index = filename.substring(0, dashIndex);\n    filename = filename.substring(dashIndex + 1).toLowerCase();\n    if (\n      ['c.txt', 'm.xml', 's.txt', 'w.txt', 'whistle.json'].indexOf(filename) ===\n      -1\n    ) {\n      return;\n    }\n    var content = zip.readFile(entryName);\n    if (!content) {\n      return;\n    }\n    var result = (sessions[index] = sessions[index] || {});\n    ++count;\n    if (filename === 'c.txt') {\n      util.getReq(content, function (req) {\n        setImmediate(function () {\n          result.req = req;\n          --count;\n          execCallback();\n        });\n      });\n    } else if (filename === 'm.xml') {\n      parseString(content, function (err, meta) {\n        setImmediate(function () {\n          result.meta = meta;\n          --count;\n          execCallback();\n        });\n      });\n    } else if (filename === 'whistle.json') {\n      setImmediate(function () {\n        --count;\n        var data = util.parseJSON(String(content));\n        if (data) {\n          if (typeof data.realUrl === 'string') {\n            result.realUrl = data.realUrl;\n          }\n          if (common.isUrl(data.url)) {\n            result._url = data.url;\n          }\n          if (data.rules) {\n            result.rules = data.rules;\n          }\n          if (data.frames) {\n            result.frames = data.frames;\n          }\n          if (data.fwdHost) {\n            result.fwdHost = data.fwdHost;\n          }\n          if (data.useHttp) {\n            result.useHttp = true;\n          }\n          if (data.sniPlugin) {\n            result.sniPlugin = data.sniPlugin;\n          }\n          if (data.httpsTime > 0) {\n            result.httpsTime = data.httpsTime;\n          }\n          if (data.useH2) {\n            result.useH2 = true;\n          }\n          if (data.mark) {\n            result.mark = true;\n          }\n          if (data.captureError) {\n            result.captureError = true;\n          }\n          if (data.reqError) {\n            result.reqError = true;\n          }\n          if (data.resError) {\n            result.resError = true;\n          }\n          var times = data.times;\n          if (times) {\n            result.startTime = times.startTime;\n            result.dnsTime = times.dnsTime;\n            result.requestTime = times.requestTime;\n            result.responseTime = times.responseTime;\n            result.endTime = times.endTime;\n          }\n          result.trailers = data.trailers;\n          result.rawTrailerNames = data.rawTrailerNames;\n          if (data.version) {\n            result.version = data.version;\n          }\n          if (data.nodeVersion) {\n            result.nodeVersion = data.nodeVersion;\n          }\n          if (data.customData) {\n            result.customData = data.customData;\n          }\n        }\n        execCallback();\n      });\n    } else if (filename === 'w.txt') {\n      setImmediate(function () {\n        result.framesData = content;\n        --count;\n        execCallback();\n      });\n    } else {\n      util.getRes(content, function (res) {\n        setImmediate(function () {\n          result.res = res;\n          --count;\n          execCallback();\n        });\n      });\n    }\n  });\n  execCallback();\n};\n"
  },
  {
    "path": "lib/service/generate-saz.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar AdmZip = require('adm-zip');\nvar util = require('./util');\n\nvar fiddlerAssets = path.join(__dirname, '../../assets/fiddler/');\nvar fiddlerMeta = fs.readFileSync(fiddlerAssets + 'meta.xml', 'utf8');\nvar SPEC_XML_RE = /[<>&\"']/g;\nvar specMap = {\n  '<': '&lt;',\n  '>': '&gt;',\n  '&': '&amp;',\n  '\\'': '&apos;',\n  '\"': '&quot;'\n};\nvar META_NAMES = [\n  'ClientConnected',\n  'ClientDoneRequest',\n  'ServerGotRequest',\n  'ServerBeginResponse',\n  'ServerDoneResponse',\n  'ClientBeginResponse',\n  'ClientDoneResponse'\n];\nvar META_NAMES2 = ['GatewayTime', 'DNSTime', 'TCPConnectTime', 'ttfb', 'ttlb', 'transfer-size', 'clientip', 'hostip'];\n\nfunction filterSessions(sessions) {\n  return sessions.filter(function (item) {\n    return item && item.url && item.req && item.startTime >= 0;\n  });\n}\n\nfunction renderTpl(tpl, locals) {\n  locals = getMetaData(locals);\n  Object.keys(locals).forEach(function (name) {\n    tpl = tpl.replace('${' + name + '}', locals[name]);\n  });\n  return tpl;\n}\n\nfunction escapeXml(str) {\n  if (!str || typeof str !== 'string') {\n    return '';\n  }\n  return str.replace(SPEC_XML_RE, function(char) {\n    return specMap[char] || char;\n  });\n}\n\nfunction getMetaData(item) {\n  var meta = {\n    SID: item.index + 1\n  };\n  META_NAMES.forEach(function (name) {\n    meta[name] = '0001-01-01T00:00:00';\n  });\n  META_NAMES2.forEach(function (name) {\n    meta[name] = 0;\n  });\n  var comment = item.customData && item.customData.comment;\n  meta['ui-comments'] = escapeXml(comment);\n\n  meta.ClientConnected = util.toISOString(item.startTime);\n  if (item.dnsTime >= item.startTime) {\n    meta.DNSTime = item.dnsTime - item.startTime;\n  }\n  if (item.requestTime > 0) {\n    meta.ClientDoneRequest =\n      meta.ServerGotRequest =\n      meta.ServerBeginResponse =\n        util.toISOString(item.requestTime);\n  }\n\n  if (item.responseTime > 0) {\n    meta.ClientBeginResponse = util.toISOString(item.responseTime);\n    meta.ttfb = item.responseTime - item.startTime;\n  }\n  if (item.ttfb >= 0) {\n    meta.ttfb = item.ttfb;\n  }\n\n  if (item.endTime > 0) {\n    meta.ServerDoneResponse = meta.ClientDoneResponse = util.toISOString(\n      item.endTime\n    );\n    meta.ttlb = item.endTime - item.startTime;\n  }\n\n  var req = item.req || {};\n  var res = item.res || {};\n  if (res.size > 0) {\n    meta['transfer-size'] = res.size;\n  }\n  meta.clientip = req.ip || '127.0.0.1';\n  meta.hostip = res.ip || '';\n  meta.clientport = req.port || 0;\n  meta.serverport = res.port || 0;\n  return meta;\n}\n\nfunction getFiddler2Meta(item) {\n  return renderTpl(fiddlerMeta, item);\n}\n\nmodule.exports = function (body, callback) {\n  var sessions = body;\n  if (!Array.isArray(sessions)) {\n    sessions = util.parseJSON(body.sessions);\n    if (!Array.isArray(sessions)) {\n      return '';\n    }\n  }\n  sessions = filterSessions(sessions);\n  var index = 0;\n  var getName = function () {\n    var name = String(index);\n    ++index;\n    var paddingCount = 4 - name.length;\n    return paddingCount <= 0\n      ? name\n      : new Array(paddingCount + 1).join('0') + name;\n  };\n\n  var zip = new AdmZip();\n  sessions.map(function (item, index) {\n    item.req.url = item.url;\n    var req = util.getReqRaw(item.req);\n    var res =\n      !item.res || item.res.statusCode == null\n        ? Buffer.from('')\n        : util.getResRaw(item.res);\n    var name = getName();\n    item.index = index;\n    zip.addFile('raw/' + name + '_c.txt', req);\n    zip.addFile('raw/' + name + '_m.xml', Buffer.from(getFiddler2Meta(item)));\n    zip.addFile('raw/' + name + '_s.txt', res);\n    zip.addFile(\n      'raw/' + name + '_whistle.json',\n      Buffer.from(\n        JSON.stringify({\n          version: item.version,\n          nodeVersion: item.nodeVersion,\n          customData: item.customData,\n          url: item.url,\n          realUrl: item.realUrl,\n          fwdHost: item.fwdHost,\n          rules: item.rules,\n          frames: item.frames,\n          useHttp: item.useHttp,\n          httpsTime: item.httpsTime,\n          useH2: item.useH2,\n          mark: item.mark,\n          sniPlugin: item.sniPlugin,\n          trailers: item.res && item.res.trailers,\n          rawTrailerNames: item.res && item.res.rawTrailerNames,\n          captureError: item.captureError,\n          reqError: item.reqError,\n          resError: item.resError,\n          times: {\n            startTime: item.startTime,\n            dnsTime: item.dnsTime,\n            requestTime: item.requestTime,\n            responseTime: item.responseTime,\n            endTime: item.endTime\n          }\n        })\n      )\n    );\n  });\n  var done;\n  var handleCallback = function(err, body) {\n    if (done) {\n      return;\n    }\n    done = true;\n    callback(err, body);\n  };\n  return zip.toBuffer(function(body) {\n    handleCallback(null, body);\n  }, handleCallback);\n};\n"
  },
  {
    "path": "lib/service/index.js",
    "content": "var fork = require('pfork').fork;\nvar path = require('path');\nvar LRU = require('lru-cache');\nvar rulesUtil = require('../rules/util');\nvar ca = require('../https/ca');\nvar pluginMgr = require('../plugins');\nvar request = require('../util/http-mgr').request;\nvar config = require('../config');\n\nvar SCRIPT = path.join(__dirname, 'service.js');\nvar errors = new LRU({ max: 60, maxAge: 10000 });\nvar forkOptions;\nvar curChild;\nvar curOptions;\n\n\nvar getOptions = function() {\n  if (!forkOptions) {\n    forkOptions = {};\n    /*eslint no-console: \"off\"*/\n    Object.keys(config).forEach(function (name) {\n      var value = config[name];\n      var type = typeof value;\n      if (type == 'string' || type == 'number' || type === 'boolean') {\n        forkOptions[name] = value;\n      }\n    });\n    delete forkOptions.value;\n    forkOptions.script = SCRIPT;\n  }\n  return forkOptions;\n};\n\nfunction handleError(data) {\n  var error = data.error || 'unknown error';\n  var list = errors.get(data.clientId) || [];\n  if (list.indexOf(error) !== -1) {\n    return;\n  }\n  list.push(error);\n  if (list.length > 12) {\n    list.shift();\n  }\n  errors.set(data.clientId, list);\n}\n\nfunction loadService(callback) {\n  if (curChild) {\n    return callback(null, curOptions, curChild);\n  }\n  fork(getOptions(), function (err, options, child) {\n    if (err) {\n      return callback(err);\n    }\n    config.whistleId = options && options.whistleId;\n    curOptions = options;\n    curChild = child;\n    child.once('close', function () {\n      curChild = null;\n    });\n    child.on('data', function (data) {\n      if (!data) {\n        return;\n      }\n      var type = data.type;\n      if (type === 'w2NetworkInterfacesChange') {\n        return process.emit('w2NetworkInterfacesChange');\n      }\n      if (type === 'whistleIdChange') {\n        config.whistleId = data.whistleId;\n        config.hasWhistleToken = data.hasWhistleToken;\n        child.sendData({ type: 'whistleId', whistleId: config.whistleId, hasWhistleToken: config.hasWhistleToken });\n        return;\n      }\n      if (type === 'error') {\n        return handleError(data);\n      }\n      if (type === 'installPlugins' && config.installPlugins) {\n        return config.installPlugins(data.data);\n      }\n    });\n    callback(null, options, child);\n  }\n  );\n}\n\nfunction formatData(list) {\n  if (Array.isArray(list)) {\n    return list.map(function (item) {\n      return {\n        name: item.name,\n        value: item.data\n      };\n    });\n  }\n  return Object.keys(list).map(function (key) {\n    return list[key];\n  });\n}\n\nvar notEqual = function(oldData, newData) {\n  if (!oldData) {\n    return true;\n  }\n  var newLen;\n  var len;\n  var i;\n  var newItem;\n  var oldItem;\n  if (Array.isArray(oldData)) {\n    newLen = newData.length;\n    if (!newLen) {\n      return false;\n    }\n    len = oldData.length;\n    if (len !== newLen) {\n      return true;\n    }\n    for (i = 0; i < len; i++) {\n      oldItem = oldData[i];\n      newItem = newData[i];\n      if (oldItem.name !== newItem.name || oldItem.data !== newItem.data) {\n        return true;\n      }\n    }\n  } else {\n    var newKeys = Object.keys(newData);\n    newLen = newKeys.length;\n    if (!newLen) {\n      return false;\n    }\n    var oldKeys = Object.keys(oldData);\n    len = oldKeys.length;\n    if (len !== newLen) {\n      return true;\n    }\n    for (i = 0; i < len; i++) {\n      var key = oldKeys[i];\n      newItem = newData[key];\n      if (!newItem) {\n        return true;\n      }\n      oldItem = oldData[key];\n      if (oldItem.key !== newItem.key || oldItem.cert !== newItem.cert ||\n        oldItem.version !== newItem.version || oldItem.registry !== newItem.registry ||\n        oldItem.type !== newItem.type) {\n        return true;\n      }\n    }\n  }\n  return false;\n};\n\nvar preDataMap = {};\nvar saveData = function(type, data) {\n  if (!notEqual(preDataMap[type], data)) {\n    return;\n  }\n  var str;\n  try {\n    str = JSON.stringify(formatData(data));\n  } catch (e) {}\n  if (!str || str.length < 3) {\n    return;\n  }\n  var body = { type: 'data' };\n  body[type] = str;\n  var sendRequest = function(isRetry) {\n    request({\n      method: 'POST',\n      url: 'http://127.0.0.1:' + curOptions.port + '/cgi-bin/service/save',\n      strictMode: true,\n      headers: {\n        'content-type': 'application/json'\n      },\n      body: body\n    }, function (err) {\n      if (err) {\n        !isRetry && sendRequest(true);\n      } else {\n        preDataMap[type] = data;\n      }\n    });\n  };\n  sendRequest();\n};\n\nfunction sendData() {\n  loadService(function (err) {\n    setTimeout(sendData, 3600);\n    if (err || !config.hasWhistleToken) {\n      return;\n    }\n    var rulesData = rulesUtil.rules.getAllData().list;\n    var valuesData = rulesUtil.values.list();\n    var certsData = ca.getPureCertFiles();\n    var pluginsData = {};\n    var plugins = pluginMgr.getPlugins();\n    Object.keys(plugins).sort().forEach(function (name) {\n      var plugin = plugins[name];\n      if (plugin && !plugin.isDev && !plugin.notUn && !plugin.isProj) {\n        pluginsData[plugin.moduleName] = {\n          name: plugin.moduleName,\n          version: plugin.version,\n          registry: plugin.registry\n        };\n      }\n    });\n    saveData('rules', rulesData);\n    saveData('values', valuesData);\n    saveData('certs', certsData);\n    saveData('plugins', pluginsData);\n  });\n}\n\nprocess.nextTick(sendData);\n\nmodule.exports = loadService;\n\nconfig.getInstallPluginErrors = function(clientId) {\n  var list = errors.get(clientId);\n  errors.del(clientId);\n  return list;\n};\n"
  },
  {
    "path": "lib/service/install.js",
    "content": "require('../util/patch');\nvar plugin = require('../../bin/plugin');\n\nprocess.on('data', function(data) {\n  if (data.type === 'installPlugins' && Array.isArray(data.argv)) {\n    plugin.install('npm', data.argv, function(e) {\n      data.clientId && process.sendData({\n        type: 'error',\n        clientId: data.clientId,\n        data: (typeof e === 'string' ? e : e && e.message) || 'Install failed'\n      });\n    });\n  }\n});\n\nmodule.exports = function(_, callback) {\n  callback();\n};\n"
  },
  {
    "path": "lib/service/service.js",
    "content": "require('../util/patch');\nvar express = require('express');\nvar fork = require('pfork').fork;\nvar bodyParser = require('body-parser');\nvar multer = require('multer2');\nvar fs = require('fs');\nvar fse = require('fs-extra2');\nvar path = require('path');\nvar gzip = require('zlib').gzip;\nvar util = require('./util');\nvar extractSaz = require('./extract-saz');\nvar generateSaz = require('./generate-saz');\nvar getServer = require('hagent').getServer;\nvar dataCenter = require('./data-center');\nvar composer = require('./composer');\nvar handleComposeData = require('./compose-data');\nvar Limiter = require('async-limiter');\nvar common = require('../util/common');\n\nprocess.env.NODE_TLS_REJECT_UNAUTHORIZED = '1';\n\nvar saveData = dataCenter.saveData;\nvar shareData = dataCenter.shareData;\nvar requestData = dataCenter.requestData;\nvar forwardRequest = dataCenter.forwardRequest;\n\nvar INSTALL_SCRIPT = path.join(__dirname, 'install.js');\nvar installChild;\n\nvar TEMP_PLUGINS_PATH = path.join(common.getWhistlePath(), '.temp_plugins');\nvar SESSIONS_FILE_RE = /\\.(txt|json|saz)$/i;\nvar limiter = new Limiter({ concurrency: 10 });\nvar MAX_PLUGINS = 10;\nvar TEMP_FILES_PATH;\nvar SAVED_SESSIONS_PATH;\nvar LIMIT_SIZE = 1024 * 1024 * 128;\nvar MAX_TEMP_SIZE = 1024 * 1024 * 12;\nvar MAX_PLUGIN_SIZE = MAX_TEMP_SIZE;\nvar MAX_TEMP_PLUGINS_AGE = 1000 * 60 * 10; // 10 minutes\nvar TEMP_FILE_RE = /^[\\da-f]{64}$/;\nvar CR = '\\r';\nvar jsonParser = bodyParser.json({ limit: LIMIT_SIZE });\nvar urlencodedParser = bodyParser.urlencoded({ extended: true, limit: '1mb'});\nvar PLUGIN_SEP = /\\s*,\\s*/;\nvar config;\nvar curWhistleId;\nvar hasWhistleToken;\nvar whistleIdFile;\nvar storage = multer.memoryStorage();\nvar INVALID_NAME_RE = /[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c'<>:\"\\\\/|?*]+/g;\nvar SPACE_RE = /\\s+/g;\nvar upload = multer({\n  storage: storage,\n  fieldSize: LIMIT_SIZE\n});\nvar promises = {};\nvar hasTempPluginsDir;\n\nvar ensureTempPluginsDir = function() {\n  if (!hasTempPluginsDir) {\n    try {\n      fse.ensureDirSync(TEMP_PLUGINS_PATH);\n      hasTempPluginsDir = true;\n    } catch (e) {}\n  }\n};\n\nensureTempPluginsDir();\n\nprocess.on('data', function(data) {\n  if (data) {\n    if (data.type === 'whistleId') {\n      curWhistleId = data.whistleId;\n      hasWhistleToken = data.hasWhistleToken;\n    } else {\n      saveData(data);\n    }\n  }\n});\n\nfunction getFilename(filename, time, count) {\n  filename = common.isString(filename) ? filename.replace(INVALID_NAME_RE, '').replace(SPACE_RE, ' ').substring(0, 64).trim() : '';\n  time = time || Date.now();\n  return common.getMonth(time) + '/' + filename + '_' + count + '_' + time;\n}\n\nfunction saveSessions(data, cb) {\n  var count = Array.isArray(data.sessions) && data.sessions.length;\n  if (!count) {\n    return cb();\n  }\n  limiter.push(function (done) {\n    gzip(JSON.stringify(data.sessions), function(err, buf) {\n      done();\n      if (err) {\n        return cb(err);\n      }\n      var filename = getFilename(data.filename, 0, count);\n      util.writeFile(path.join(SAVED_SESSIONS_PATH, filename), buf, function(e) {\n        !e && saveData({ type: 'savedData', filename: filename, buffer: buf } );\n        cb(e);\n      });\n    });\n  });\n}\n\nfunction getContent(options) {\n  var value = options.value;\n  if (value && typeof value === 'string') {\n    return value;\n  }\n  var base64 = options.base64;\n  if (base64) {\n    try {\n      return Buffer.from(base64, 'base64');\n    } catch(e) {}\n  }\n  return '';\n}\n\nfunction getFile(filename, cb) {\n  common.getStat(filename, function(err, stat) {\n    if (err ? err.code === 'ENOENT' : !stat.isFile()) {\n      return cb();\n    }\n    if (err) {\n      return cb(err.message || 'Error');\n    }\n    if (stat.size > MAX_TEMP_SIZE) {\n      return cb('File is too large to load');\n    }\n    fs.readFile(filename, function(e, data) {\n      if (e) {\n        return cb(e.message || 'Error');\n      }\n      cb(null, data);\n    });\n  });\n}\n\nfunction getTempFiles(list, cb) {\n  var len = list.length;\n  if (!len) {\n    return cb(list);\n  }\n  if (len > 11) {\n    len = 11;\n    list = list.slice(0, 11);\n  }\n  var result = [];\n  var execCb = function() {\n    --len;\n    if (len === 0) {\n      cb(result);\n    }\n  };\n  list = list.forEach(function(filename) {\n    if (!TEMP_FILE_RE.test(filename)) {\n      return execCb();\n    }\n    filename = path.join(TEMP_FILES_PATH, filename);\n    getFile(filename, function(err, data) {\n      if (data) {\n        result.push(data.toString('base64'));\n      }\n      execCb();\n    });\n  });\n}\n\nfunction handleError(clientId, e) {\n  if (clientId) {\n    e = (typeof e === 'string' ? e : e && e.message) || 'Install failed';\n    process.sendData({ type: 'error', error: e.substring(0, 512), clientId: clientId });\n  }\n}\n\nfunction writeTempPlugin(plugin, data, cb) {\n  if (!data) {\n    return cb();\n  }\n  plugin = plugin.split('/').pop();\n  var tempPath = path.join(TEMP_PLUGINS_PATH, plugin);\n  ensureTempPluginsDir();\n  util.writeFile(tempPath, data, function(e) {\n    cb(e, e ? null : tempPath);\n  });\n}\n\nfunction cleanTempPlugins() {\n  fs.readdir(TEMP_PLUGINS_PATH, function(err, files) {\n    setTimeout(cleanTempPlugins, MAX_TEMP_PLUGINS_AGE);\n    if (err) {\n      return;\n    }\n    var now = Date.now();\n    files.forEach(function(file) {\n      if (!common.TGZ_FILE_NAME_RE.test(file)) {\n        return;\n      }\n      fs.stat(path.join(TEMP_PLUGINS_PATH, file), function(err, stats) {\n        if (err) {\n          return;\n        }\n        if (Math.abs(now - stats.mtime.getTime()) < MAX_TEMP_PLUGINS_AGE) {\n          return;\n        }\n        fs.unlink(path.join(TEMP_PLUGINS_PATH, file), common.noop);\n      });\n    });\n  });\n}\n\ncleanTempPlugins();\n\nfunction loadTgzPlugins(clientId, tgzPlugins, cb) {\n  var len = tgzPlugins.length;\n  if (!len) {\n    return cb();\n  }\n  var result = [];\n  var execCb = function() {\n    if (--len === 0) {\n      cb(result);\n    }\n  };\n\n  tgzPlugins.forEach(function(plugin) {\n    var p = promises[plugin];\n    if (p) {\n      // 同一 plugin 避免重复请求\n      return p.then(execCb);\n    }\n    promises[plugin] = new Promise(function(resolve) {\n      requestData({\n        url: '/service/cgi/plugin?name=' + encodeURIComponent(plugin),\n        maxLength: MAX_PLUGIN_SIZE,\n        responseType: 'buffer',\n        strictMode: true\n      }, function(err, data) {\n        writeTempPlugin(plugin, data, function(e, file) {\n          file && result.push('file:' + file);\n          execCb();\n          err = err || e;\n          err && handleError(clientId, err.message || 'Install plugin \\'' + plugin + '\\' failed');\n          delete promises[plugin];\n          resolve();\n        });\n      });\n    });\n  });\n}\n\nfunction execInstallPlugins(argv, clientId) {\n  if (installChild) {\n    return installChild.sendData({ type: 'installPlugins', argv: argv, clientId: clientId });\n  }\n  fork({\n    script: INSTALL_SCRIPT,\n    debugMode: config.debugMode\n  }, function (err, _, child) {\n    if (err) {\n      return handleError(clientId, err);\n    }\n    installChild = child;\n    child.once('close', function () {\n      installChild = null;\n    });\n    child.on('data', function (data) {\n      if (data && data.type === 'error') {\n        return handleError(data.clientId, data.data);\n      }\n    });\n    installChild.sendData({ type: 'installPlugins', argv: argv, clientId: clientId });\n  });\n}\n\nfunction installPlugins(data, clientId) {\n  var argv = data.pkgs.map(function(item) {\n    return item.name + (item.version ? '@' + item.version : '');\n  });\n  if (data.whistleDir) {\n    argv.push('--dir=' + data.whistleDir);\n  }\n  if (data.registry) {\n    argv.push('--registry=' + data.registry);\n  }\n  execInstallPlugins(argv, clientId);\n}\n\nfunction sessionsHandler() {\n  var app = express();\n  app.use(function (req, res, next) {\n    req.on('error', abort);\n    res.on('error', abort);\n    function abort() {\n      res.destroy();\n    }\n    next();\n  });\n  app.get('/service/*', forwardRequest);\n  app.post('/cgi-bin/service/save', jsonParser, function(req, res) {\n    if (util.isShareType(req.body.type)) {\n      shareData(req.body, function(err, data) {\n        res.json(err ? {ec: 2, data, em: err.message || 'Error'} : data);\n      });\n      return;\n    }\n    saveData(req.body);\n    res.json({ec: 0});\n  });\n  app.post('/cgi-bin/service/login', jsonParser, function(req, res) {\n    requestData({\n      method: 'POST',\n      url: '/service/cgi/login',\n      strictMode: true,\n      headers: {\n        'content-type': 'application/json'\n      },\n      body: req.body\n    }, function(err, data) {\n      if (err) {\n        return res.json({ec: 2, em: err.message || 'Error'});\n      }\n      config.whistleId = data.whistleId;\n      config.whistleToken = data.token;\n      process.sendData({ type: 'whistleIdChange', whistleId: data.whistleId, hasWhistleToken: !!data.token });\n      res.json({ec: 0});\n      try {\n        var text = common.encryptAES(data.whistleId + CR + data.token + CR + Date.now(), config.clientId, config.clientIdNo);\n        fs.writeFile(whistleIdFile, text, function(e) {\n          if (e) {\n            fs.writeFile(whistleIdFile, text, common.noop);\n          }\n        });\n      } catch (e) {}\n    });\n  });\n  app.post('/cgi-bin/plugins/install', urlencodedParser, function(req, res) {\n    var plugins = req.body.plugins;\n    var clientId = req.body.clientId;\n    plugins = common.isString(plugins) ? plugins.trim().split(PLUGIN_SEP) : null;\n    plugins = plugins && common.getPlugins(plugins, true);\n    var count = plugins ? plugins.length : 0;\n    if (count) {\n      if (count > MAX_PLUGINS) {\n        plugins = plugins.slice(0, MAX_PLUGINS);\n        count = MAX_PLUGINS;\n      }\n      var curTgzPlugins = [];\n      plugins = plugins.filter(function(name) {\n        if (!name.indexOf('file:')) {\n          curTgzPlugins.push(name.substring(5));\n          return false;\n        }\n        return true;\n      });\n      loadTgzPlugins(clientId, curTgzPlugins, function(tgzPlugins) {\n        plugins = plugins.concat(tgzPlugins);\n        if (!plugins.length) {\n          return;\n        }\n        var registry = common.getRegistry(req.body.registry);\n        if (config.hasInstaller) {\n          var pkgs = plugins.map(function(name) {\n            if (common.WHISTLE_PLUGIN_RE.test(name)) {\n              return {\n                name: RegExp.$1,\n                version: RegExp.$2\n              };\n            }\n            return { name: name };\n          });\n          process.sendData({ type: 'installPlugins', data: {\n            registry: registry,\n            pkgs: pkgs\n          } });\n        } else {\n          registry && plugins.push('--registry=' + registry);\n          execInstallPlugins(plugins, clientId);\n        }\n      });\n      return res.json({ ec: 0, count: count });\n    }\n    var data = common.parsePlugins(req.body);\n    if (data) {\n      if (config.hasInstaller) {\n        process.sendData({ type: 'installPlugins', data: data });\n      } else {\n        installPlugins(data, clientId);\n      }\n    }\n    res.json({ ec: 0, count: data ? data.pkgs.length : 0 });\n  });\n  app.post('/cgi-bin/service/logout', function(req, res) {\n    config.whistleId = undefined;\n    config.whistleToken = undefined;\n    process.sendData({ type: 'whistleIdChange' });\n    res.json({ec: 0});\n  });\n  app.post('/cgi-bin/saved/save', jsonParser, function(req, res) {\n    saveSessions(req.body, function(err) {\n      res.json({ec: err ? 2 : 0, em: err ? err.message || 'Error' : undefined});\n    });\n  });\n  app.post('/cgi-bin/composer', jsonParser, composer.handleRequest);\n  app.get('/cgi-bin/compose-data', handleComposeData);\n  '';\n  app.get('/cgi-bin/saved/list', function(req, res) {\n    util.getSavedList(SAVED_SESSIONS_PATH, function(err, list) {\n      if (err) {\n        return res.json({ ec: 2, em: err.message } );\n      }\n      common.sendGzip(req, res, { ec: 0, list: list });\n    });\n  });\n  app.post('/cgi-bin/saved/remove', jsonParser, function(req, res) {\n    var filename = getFilename(req.body.filename, +req.body.time, +req.body.count);\n    fs.unlink(path.join(SAVED_SESSIONS_PATH, filename), function(err) {\n      dataCenter.removeSavedData(filename.substring(filename.indexOf('/') + 1));\n      if (err && err.code !== 'ENOENT') {\n        return res.json({ ec: 2, em: err.message });\n      }\n      res.json({ ec: 0 });\n    });\n  });\n  app.get('/cgi-bin/saved/sessions', function(req, res) {\n    var filename = getFilename(req.query.filename, +req.query.time, +req.query.count);\n    res.writeHead(200, {\n      'Content-Type': 'application/json',\n      'Content-Encoding': 'gzip'\n    });\n    fs.createReadStream(path.join(SAVED_SESSIONS_PATH, filename))\n      .on('error', function(err) {\n        res.emit('error', err);\n      })\n      .pipe(res);\n  });\n  app.get('/cgi-bin/temp/get', function(req, res) {\n    var files = req.query.files;\n    if (files && typeof files === 'string') {\n      return getTempFiles(files.split(','), function(list) {\n        common.sendGzip(req, res, { ec: 0, list: list });\n      });\n    }\n    var filename = req.query.filename;\n    if (TEMP_FILE_RE.test(filename)) {\n      filename = path.join(TEMP_FILES_PATH, filename);\n    }\n    getFile(filename, function(em, data) {\n      if (em) {\n        return res.json({ ec: 2, em: em });\n      }\n      common.sendGzip(req, res, { ec: 0, value: data && data + '' });\n    });\n  });\n  app.get('/cgi-bin/history', composer.getHistory);\n  app.use('/cgi-bin/temp/create',\n    jsonParser,\n    function(req, res) {\n      util.writeTempFile(TEMP_FILES_PATH, getContent(req.body), function(e, filename) {\n        if (e) {\n          return res.json({ ec: 2, em: e.message });\n        }\n        res.json({ ec: 0, filepath: 'temp/' + filename });\n      });\n    }\n  );\n  app.use(\n    '/cgi-bin/sessions/import',\n    upload.single('importSessions'),\n    function (req, res) {\n      var file = req.file;\n      var suffix;\n      if (file && SESSIONS_FILE_RE.test(file.originalname)) {\n        suffix = RegExp.$1.toLowerCase();\n      }\n      if (!suffix || !Buffer.isBuffer(file.buffer)) {\n        return res.json([]);\n      }\n      if (suffix !== 'saz') {\n        var sessions = util.parseJSON(file.buffer + '');\n        return common.sendGzip(req, res, Array.isArray(sessions) ? sessions : []);\n      }\n      try {\n        extractSaz(file.buffer, function (data) {\n          common.sendGzip(req, res, data);\n        });\n      } catch (e) {\n        common.sendRes(req, 500, e.stack);\n      }\n    }\n  );\n  app.use(bodyParser.urlencoded({ extended: true, limit: LIMIT_SIZE }));\n  app.use(bodyParser.json());\n  app.use('/cgi-bin/sessions/export', function (req, res) {\n    var body = req.body;\n    var type = body.exportFileType;\n    var download = function(content) {\n      res.attachment(util.getFilename(type, body.exportFilename)).send(content || body.sessions);\n    };\n    if (type !== 'Fiddler') {\n      return download();\n    }\n    generateSaz(body, function(e, body) {\n      if (e) {\n        return common.sendRes(res, 500, e.stack);\n      }\n      download(body);\n    });\n  });\n  return app;\n}\n\nfunction updateWhistleId() {\n  if (config && (config.whistleId !== curWhistleId || !config.whistleToken !== !hasWhistleToken)) {\n    process.sendData({ type: 'whistleIdChange', whistleId: config.whistleId, hasWhistleToken: !!config.whistleToken });\n  }\n}\n\nvar preList;\n(function updateSystyemInfo() {\n  var curList = [];\n  var isChanged = !preList;\n  common.walkInterfaces(function (info) {\n    var addr = info.address.toLowerCase();\n    curList.push(addr);\n    isChanged = isChanged || preList.indexOf(addr) === -1;\n  });\n  isChanged = isChanged || curList.length !== preList.length;\n  preList = curList;\n  if (isChanged && process.sendData) {\n    process.sendData({ type: 'w2NetworkInterfacesChange' });\n  }\n  updateWhistleId();\n  setTimeout(updateSystyemInfo, 2000);\n})();\n\nfunction checkLogin(whistleId, token) {\n  if (!whistleId || !token) {\n    return;\n  }\n  // config.whistleId = whistleId;\n  setTimeout(function() {\n    // config.whistleToken = token;\n    updateWhistleId();\n  }, 2000);\n}\n\nfunction readWhistleLoginFile(file) {\n  try {\n    var text = common.decryptAES(common.readFileTextSync(file).trim(), config.clientId, config.clientIdNo).split(CR);\n    return text.length === 3 && text;\n  } catch (e) {}\n}\n\nfunction checkWhistleId(cb) {\n  var text = config.disableWebUI ? null\n    : readWhistleLoginFile(whistleIdFile) || (config.storage && readWhistleLoginFile(path.join(config.baseDir, '.whistle_id')));\n  text && checkLogin(text[0], text[1]);\n  cb();\n}\n\nmodule.exports = function (options, callback) {\n  config = options;\n  whistleIdFile = path.join(options.baseDir, options.storage || '', '.whistle_id');\n  TEMP_FILES_PATH = options.TEMP_FILES_PATH;\n  SAVED_SESSIONS_PATH = options.SAVED_SESSIONS_PATH;\n  dataCenter.setup(options);\n  composer.setup(options);\n  checkWhistleId(function() {\n    getServer(function (server, port) {\n      server.on('request', sessionsHandler());\n      callback(null, { port: port, whistleId: config.whistleId });\n    });\n  });\n};\n"
  },
  {
    "path": "lib/service/util.js",
    "content": "var crypto = require('crypto');\nvar path = require('path');\nvar fs = require('fs');\nvar fse = require('fs-extra2');\nvar zlib = require('../util/zlib');\nvar common = require('../util/common');\n\nvar STATUS_CODES = require('http').STATUS_CODES || {};\nvar wsParser = require('ws-parser');\n\nvar CRLF = Buffer.from('\\r\\n');\nvar TYPE_RE = /(request|response)-length:/i;\nvar frameIndex = 100000;\nvar TYPES = ['whistle', 'Fiddler', 'har'];\n\nfunction dechunkify(body) {\n  var result = [];\n  var index;\n  while ((index = indexOfBuffer(body, CRLF)) > 0) {\n    var size = parseInt(body.slice(0, index).toString(), 16) || 0;\n    if (!size) {\n      break;\n    }\n    index += 2;\n    result.push(body.slice(index, (index += size)));\n    body = body.slice(index + 2);\n  }\n  return result.length ? Buffer.concat(result) : body;\n}\n\nfunction getMethod(method) {\n  if (typeof method !== 'string') {\n    return 'GET';\n  }\n  return method.trim().toUpperCase() || 'GET';\n}\n\nfunction getHeadersRaw(headers, rawHeaderNames) {\n  var result = [];\n  if (headers) {\n    rawHeaderNames = rawHeaderNames || {};\n    Object.keys(headers).forEach(function (name) {\n      var value = headers[name];\n      var key = rawHeaderNames[name] || name;\n      if (!Array.isArray(value)) {\n        result.push(key + ': ' + value);\n        return;\n      }\n      value.forEach(function (val) {\n        result.push(key + ': ' + val);\n      });\n    });\n  }\n  return result;\n}\n\nfunction decodeRaw(headers, data) {\n  var body = getBodyBuffer(data);\n  var raw = Buffer.from(headers.join('\\r\\n') + '\\r\\n\\r\\n');\n  return body ? Buffer.concat([raw, body]) : raw;\n}\n\nfunction removeEncodingFields(headers) {\n  if (headers) {\n    delete headers['content-encoding'];\n    delete headers['transfer-encoding'];\n  }\n}\n\nfunction getBodyBuffer(data) {\n  if (data.base64) {\n    try {\n      return Buffer.from(data.base64 + '', 'base64');\n    } catch (e) {}\n    return Buffer.from(data.base64 + '');\n  }\n  if (data.body) {\n    return Buffer.from(data.body + '');\n  }\n}\n\nfunction getReqRaw(req) {\n  removeEncodingFields(req.headers);\n  var headers = getHeadersRaw(req.headers, req.rawHeaderNames);\n  var url = String(req.url || '').replace(/^ws/, 'http');\n  headers.unshift([getMethod(req.method), url, 'HTTP/1.1'].join(' '));\n  return decodeRaw(headers, req);\n}\n\nexports.getReqRaw = getReqRaw;\n\nfunction getResRaw(res) {\n  removeEncodingFields(res.headers);\n  var headers = getHeadersRaw(res.headers, res.rawHeaderNames);\n  var statusCode = res.statusCode === 'aborted' ? 502 : res.statusCode;\n  var statusMessage = !statusCode\n    ? ''\n    : res.statusMessage || STATUS_CODES[statusCode] || 'unknown';\n  headers.unshift(['HTTP/1.1', statusCode, statusMessage].join(' '));\n  return decodeRaw(headers, res);\n}\n\nexports.getResRaw = getResRaw;\n\nvar BODY_SEP = Buffer.from('\\r\\n\\r\\n');\n\nfunction getBodyOffset(raw) {\n  var index = indexOfBuffer(raw, BODY_SEP);\n  if (index !== -1) {\n    return [index, index + 4];\n  }\n}\nfunction indexOfBuffer(buf, subBuf, start) {\n  start = start || 0;\n  if (buf.indexOf) {\n    return buf.indexOf(subBuf, start);\n  }\n\n  var subLen = subBuf.length;\n  if (subLen) {\n    for (var i = start, len = buf.length - subLen; i <= len; i++) {\n      var j = 0;\n      for (; j < subLen; j++) {\n        if (subBuf[j] !== buf[i + j]) {\n          break;\n        }\n      }\n      if (j == subLen) {\n        return i;\n      }\n    }\n  }\n\n  return -1;\n}\n\nfunction getBody(body, headers, callback) {\n  if (body) {\n    var chunked = headers['transfer-encoding'];\n    if (typeof chunked === 'string') {\n      chunked = chunked.trim().toLowerCase();\n    }\n    if (chunked === 'chunked') {\n      body = dechunkify(body);\n    }\n  }\n\n  zlib.unzip(headers['content-encoding'], body, function (err, result) {\n    if (!err && result) {\n      body = result;\n    }\n    return callback(body && body.toString('base64'));\n  });\n}\n\nfunction parseRawData(raw, callback) {\n  var offset = getBodyOffset(raw);\n  var body = '';\n  if (offset) {\n    body = raw.slice(offset[1]);\n    raw = raw.slice(0, offset[0]);\n  }\n  raw = raw.toString();\n  raw = raw.trim().split(/\\r\\n?|\\n/);\n  var statusLine = raw.shift().split(/\\s+/);\n  var firstLine = statusLine.splice(0, 2);\n  firstLine[2] = statusLine.join(' ');\n  var headers = {};\n  var rawHeaderNames = {};\n  raw.forEach(function (line) {\n    var index = line.indexOf(':');\n    if (index === -1) {\n      return;\n    }\n    var name = line.substring(0, index).trim();\n    if (!name) {\n      return;\n    }\n    var key = name.toLowerCase();\n    var value = headers[key];\n    var val = line.substring(index + 1).trim();\n    if (value != null) {\n      if (Array.isArray(value)) {\n        value.push(val);\n      } else {\n        value = [value, val];\n      }\n    } else {\n      value = val;\n    }\n    rawHeaderNames[key] = name;\n    headers[key] = value;\n  });\n\n  getBody(body, headers, function (base64) {\n    callback({\n      firstLine: firstLine,\n      headers: headers,\n      size: base64 ? base64.length : 0,\n      rawHeaderNames: rawHeaderNames,\n      base64: base64\n    });\n  });\n}\n\nfunction getReq(raw, callback) {\n  raw = parseRawData(raw, function (raw) {\n    var method = raw.firstLine[0] || 'GET';\n    callback(\n      raw\n        ? {\n          method: method,\n          httpVersion: '1.1',\n          rawHeaderNames: raw.rawHeaderNames,\n          url: raw.firstLine[1],\n          headers: raw.headers,\n          size: /^get$/i.test(method) ? 0 : raw.size,\n          base64: raw.base64\n        }\n        : null\n    );\n  });\n}\n\nexports.getReq = getReq;\n\nfunction getRes(raw, callback) {\n  parseRawData(\n    raw,\n    function (raw) {\n      callback(\n        raw\n          ? {\n            statusCode: raw.firstLine[1],\n            httpVersion: '1.1',\n            rawHeaderNames: raw.rawHeaderNames,\n            statusMessage: raw.firstLine[2],\n            headers: raw.headers,\n            size: raw.size,\n            base64: raw.base64\n          }\n          : {}\n      );\n    },\n    true\n  );\n}\n\nexports.getRes = getRes;\n\nfunction parseJSON(str) {\n  try {\n    return JSON.parse(str);\n  } catch (e) {}\n}\n\nexports.parseJSON = parseJSON;\n\nfunction formatDate() {\n  var date = new Date();\n  var result = [];\n  result.push(date.getFullYear());\n  result.push(common.padLeft(date.getMonth() + 1));\n  result.push(common.padLeft(date.getDate()));\n  result.push(common.padLeft(date.getHours()));\n  result.push(common.padLeft(date.getMinutes()));\n  result.push(common.padLeft(date.getSeconds()));\n  result.push(common.padLeft(date.getMilliseconds(), 3));\n  return result.join('');\n}\n\nfunction getFilename(type, filename) {\n  if (TYPES.indexOf(type) === -1) {\n    type = 'whistle';\n  }\n  if (typeof filename !== 'string') {\n    filename = '';\n  }\n  if (type === 'whistle') {\n    if (filename) {\n      if (!/\\.(json|txt)$/i.test(filename)) {\n        filename += '.txt';\n      }\n    } else {\n      filename = 'network_' + formatDate() + '.txt';\n    }\n  } else if (type === 'har') {\n    if (filename) {\n      if (!/\\.har$/i.test(filename)) {\n        filename += '.har';\n      }\n    } else {\n      filename = 'network_' + formatDate() + '.har';\n    }\n  } else {\n    if (filename) {\n      if (!/\\.saz$/i.test(filename)) {\n        filename += '.saz';\n      }\n    } else {\n      filename = 'network_' + formatDate() + '.saz';\n    }\n  }\n  return filename;\n}\n\nexports.getFilename = getFilename;\n\nvar ONE_MINUTE = 60 * 1000;\nfunction toISOString(time) {\n  var date = new Date();\n  var offet = -date.getTimezoneOffset();\n  time += offet * ONE_MINUTE;\n  offet /= 60;\n  time = time >= 0 ? new Date(time) : new Date();\n  return (\n    time.toISOString().slice(0, -1) +\n    '0000' +\n    (offet >= 0 ? '+' : '-') +\n    common.padLeft(Math.abs(offet)) +\n    ':00'\n  );\n}\n\nexports.toISOString = toISOString;\n\nfunction removeIPV6Prefix(ip) {\n  if (typeof ip != 'string') {\n    return '';\n  }\n\n  return ip.indexOf('::ffff:') === 0 ? ip.substring(7) : ip;\n}\n\nexports.removeIPV6Prefix = removeIPV6Prefix;\n\nfunction getIndex() {\n  if (frameIndex > 10000000) {\n    frameIndex = 100000;\n  }\n  return ++frameIndex;\n}\n\nfunction noop() {}\n\nfunction resolveFrames(res, frames, callback) {\n  var len = frames.length;\n  var result = [];\n  if (!len) {\n    return callback(result);\n  }\n  res.headers = res.headers || {};\n  var receiver = wsParser.getReceiver(res);\n  var execCallback = function () {\n    if (receiver) {\n      receiver.onData = noop;\n      receiver = null;\n      callback(result);\n    }\n  };\n  var index = 0;\n  receiver.onerror = execCallback;\n  receiver.onclose = execCallback;\n  receiver.onData = function (chunk, opts) {\n    var frame = frames[index];\n    ++index;\n    if (frame) {\n      result.push({\n        frameId: frame.frameId,\n        isClient: frame.type === 'request',\n        mask: opts.mask,\n        base64: chunk.toString('base64'),\n        compressed: opts.compressed,\n        length: opts.length,\n        unzipLen: chunk && opts.compressed ? chunk.length : undefined,\n        opcode: opts.opcode\n      });\n    }\n    if (!frame || len === index) {\n      setImmediate(execCallback);\n    }\n  };\n  setTimeout(execCallback, 3000);\n  frames.forEach(function (frame) {\n    receiver.add(frame.bin);\n  });\n}\n\nfunction parseFrames(res, content, callback) {\n  var end = content.indexOf(CRLF, 0);\n  var start = 2;\n  var frames = [];\n\n  while (end !== -1) {\n    var line = content.slice(start, end).toString();\n    if (TYPE_RE.test(line)) {\n      var frame = { type: RegExp.$1.toLowerCase() };\n      frame.length = line.substring(line.indexOf(':') + 1).trim();\n      start = content.indexOf(CRLF, start + 90);\n      if (start === -1) {\n        break;\n      }\n      start += 2;\n      end = content.indexOf(CRLF, start);\n      if (end === -1) {\n        break;\n      }\n      line = content.slice(start, end).toString();\n      var time = new Date(\n        line.substring(line.indexOf(':') + 1).trim() || 0\n      ).getTime();\n      frame.frameId = time + '-' + getIndex();\n      start = end + 4;\n      end = content.indexOf(CRLF, start);\n      if (end === -1) {\n        break;\n      }\n      frame.bin = content.slice(start, end);\n      frames.push(frame);\n    }\n    start = end + 2;\n    end = content.indexOf(CRLF, start);\n  }\n  resolveFrames(res, frames, callback);\n}\n\nexports.parseFrames = parseFrames;\n\nfunction getHexHash(ctn, key) {\n  return crypto\n    .createHmac('sha256', key || 'whistle_temp_files')\n    .update(ctn || '').digest('hex').toLowerCase();\n}\n\nfunction descSorter(a, b) {\n  return a > b ? -1 : 1;\n}\n\nvar MAX_SESSIONS_FILES = 2000;\nvar SAVED_SESSIONS_FILE_RE = /_([1-9]\\d*)_(\\d+)$/;\nvar DIR_RE = /^\\d{6}$/;\n\nexports.SAVED_SESSIONS_FILE_RE = SAVED_SESSIONS_FILE_RE;\n\nfunction getFileList(files) {\n  var result = [];\n  files.forEach(function(file, i) {\n    if (SAVED_SESSIONS_FILE_RE.test(file)) {\n      var count = RegExp.$1;\n      var time = RegExp.$2;\n      result.push({\n        filename: file.slice(0,  - time.length - count.length - 2),\n        count: +count,\n        time: +time\n      });\n    }\n  });\n  return result;\n}\n\nfunction readSessionsFiles(dirs, cb, result) {\n  var first = !result;\n  result = result || [];\n  var dir = dirs.pop();\n  if (!dir) {\n    return cb(null, result);\n  }\n  fs.readdir(dir, function(err, files) {\n    if (err) {\n      return cb(err);\n    }\n    var list = getFileList(files);\n    result = result.concat(list);\n    if (result.length >= MAX_SESSIONS_FILES) {\n      return cb(null, first ? result : result.slice(0, MAX_SESSIONS_FILES));\n    }\n    readSessionsFiles(dirs, cb, result);\n  });\n}\n\nexports.getSavedList = function(savedDir, cb) {\n  fs.readdir(savedDir, function(err, dirs) {\n    if (err) {\n      return cb(err);\n    }\n    if (!dirs || !dirs.length) {\n      return cb(null, []);\n    }\n    var list = [];\n    dirs.forEach(function(dir) {\n      if (DIR_RE.test(dir)) {\n        list.push(dir);\n      }\n    });\n    list = list.sort(descSorter).slice(0, 12).map(function(dir) {\n      return path.join(savedDir, dir);\n    });\n    readSessionsFiles(list, cb);\n  });\n};\n\nvar SHARE_TYPE_RE = /Share$/;\n\nexports.isShareType = function(type) {\n  return SHARE_TYPE_RE.test(type);\n};\n\nfunction writeRetry(filepath, data, callback, retry) {\n  fse.outputFile(filepath, data, function(e) {\n    if (!e || retry) {\n      return callback(e);\n    }\n    writeRetry(filepath, data, callback, true);\n  });\n}\n\nfunction writeFile(filepath, data, callback) {\n  common.getStat(filepath, function(_, stat) {\n    if (!stat || !stat.isFile()) {\n      return writeRetry(filepath, data, callback);\n    }\n    callback();\n  });\n}\n\nexports.writeFile = writeFile;\n\nfunction writeTempFile(dir, value, callback) {\n  var filename = getHexHash(value);\n  writeFile(path.join(dir, filename), value, function(err) {\n    callback(err, filename);\n  });\n}\n\nexports.writeTempFile = writeTempFile;\n"
  },
  {
    "path": "lib/socket-mgr.js",
    "content": "var pluginMgr = require('./plugins');\nvar wsParser = require('ws-parser');\nvar util = require('./util');\nvar rules = require('./rules');\nvar extend = require('extend');\n\nvar pendingReqList = [];\nvar INTERVAL = 22 * 1000;\nvar proxy;\nvar index = 0;\nvar MAX_PAYLOAD = 1024 * 1024;\nvar conns = {};\nvar PING = Buffer.from('iQA=', 'base64');\nvar PONG = Buffer.from('ioAn6ubf', 'base64');\nvar PAUSE_STATUS = 1;\nvar IGNORE_STATUS = 2;\nvar MAX_COMPOSE_FRAME_COUNT = 5;\n\nfunction getFrameId() {\n  ++index;\n  if (index > 999) {\n    index = 0;\n  }\n  if (index > 99) {\n    return Date.now() + '-' + index;\n  }\n  if (index > 9) {\n    return Date.now() + '-0' + index;\n  }\n  return Date.now() + '-00' + index;\n}\n\nexports = module.exports = function (p) {\n  proxy = p;\n};\n\nfunction handleSocketEnd(req, res, callback) {\n  util.onSocketEnd(req, callback);\n  util.onSocketEnd(res, callback);\n}\n\nfunction handleClose(req, res, justTunnel) {\n  handleSocketEnd(req, res, function (err) {\n    var ctx = conns[req.reqId];\n    ctx && ctx.clearup();\n    var closed = req._hasClosed;\n    req._hasClosed = true;\n    // 确保两个连接都关掉才行\n    if (closed) {\n      req.emit('_closed');\n    }\n    if (req.customParser) {\n      !closed && removePending(req.reqId);\n      return;\n    }\n    if (closed && !justTunnel) {\n      req._emittedClosed = true;\n      proxy.emit('frame', {\n        reqId: req.reqId,\n        frameId: getFrameId(),\n        closed: true,\n        code: req._errorCode || res._errorCode,\n        err: err && err.message\n      });\n    }\n  });\n}\n\nfunction getStatus(ctx, status, name) {\n  status = parseInt(status, 10);\n  name = name || 'receiveStatus';\n  var oldStatus = ctx[name] || 0;\n  status = ctx[name] = status > 0 || status < 3 ? status : 0;\n  return status !== oldStatus ? status : -1;\n}\n\nfunction setConnStatus(ctx, status, statusObj, name) {\n  statusObj.pause = statusObj.ignore = undefined;\n  status = getStatus(ctx, status, name);\n  if (status === 1) {\n    statusObj.pause = true;\n  } else {\n    if (status === 2) {\n      statusObj.ignore = true;\n      statusObj.chunk = null;\n      statusObj.ignoring = !!statusObj.callback;\n    }\n    statusObj.emitData && statusObj.emitData();\n    if (statusObj.callback) {\n      statusObj.addToReceiver && statusObj.addToReceiver();\n      statusObj.callback(null, statusObj.chunk);\n      statusObj.addToReceiver = null;\n      statusObj.callback = null;\n      statusObj.chunk = null;\n    }\n  }\n  statusObj.changePauseState && statusObj.changePauseState();\n}\n\nfunction initStatus(ctx, enable) {\n  if (enable.pauseSend) {\n    ctx.setSendStatus(PAUSE_STATUS);\n  } else if (enable.ignoreSend) {\n    ctx.setSendStatus(IGNORE_STATUS);\n  }\n  if (enable.pauseReceive) {\n    ctx.setReceiveStatus(PAUSE_STATUS);\n  } else if (enable.ignoreReceive) {\n    ctx.setReceiveStatus(IGNORE_STATUS);\n  }\n}\n\nfunction removePending(reqId) {\n  var index = pendingReqList.indexOf(reqId);\n  if (index !== -1) {\n    pendingReqList.splice(index, 1);\n  }\n}\n\nfunction pipeStream(src, target, useSrc) {\n  if (!src || !target) {\n    return src || target;\n  }\n  src.pipe(target);\n  return useSrc ? src : target;\n}\n\nfunction emitDataToProxy(req, chunk, fromClient, ignore) {\n  if (req.hideFrame || req._emittedClosed) {\n    return;\n  }\n  proxy.emit('frame', {\n    reqId: req.reqId,\n    frameId: getFrameId(),\n    isClient: fromClient,\n    length: chunk.length,\n    ignore: ignore,\n    bin: chunk\n  });\n}\n\nfunction handleConnSend(ctx, reqTrans, sendStatus, frameCtx, eventName) {\n  var req = ctx.req;\n  var res = ctx.res;\n  var hasEvent = ctx.hasEvent;\n  var writer = res.pipeWriter || res;\n  var url = ctx.url;\n  ctx.sendToServer = function (data, opts) {\n    data = execHandleFrame(frameCtx, data, opts, true);\n    if (!data) {\n      return false;\n    }\n    writer.write(data);\n    emitDataToProxy(req, data, true);\n  };\n  sendStatus.emitData = function () {\n    if (sendStatus.chunk) {\n      emitDataToProxy(req, sendStatus.chunk, true, sendStatus.ignore);\n      sendStatus.chunk = null;\n    }\n  };\n  reqTrans._transform = function (chunk, _, cb) {\n    hasEvent && proxy.emit(eventName, url);\n    if (sendStatus.pause) {\n      sendStatus.chunk = chunk;\n      sendStatus.callback = cb;\n      return;\n    }\n    chunk = execHandleFrame(frameCtx, chunk, {}, true);\n    if (chunk) {\n      emitDataToProxy(req, chunk, true, sendStatus.ignore);\n      if (sendStatus.ignore) {\n        chunk = null;\n      }\n    }\n    cb(null, chunk || null);\n  };\n}\n\nfunction handleConnReceive(ctx, resTrans, receiveStatus, frameCtx, eventName) {\n  var req = ctx.req;\n  var hasEvent = ctx.hasEvent;\n  var url = ctx.url;\n  var writer = req.pipeWriter || req;\n  ctx.sendToClient = function (data, opts) {\n    data = execHandleFrame(frameCtx, data, opts);\n    if (!data) {\n      return false;\n    }\n    writer.write(data);\n    emitDataToProxy(req, data);\n  };\n  receiveStatus.emitData = function () {\n    if (receiveStatus.chunk) {\n      emitDataToProxy(\n        req,\n        receiveStatus.chunk,\n        undefined,\n        receiveStatus.ignore\n      );\n      receiveStatus.chunk = null;\n    }\n  };\n\n  resTrans._transform = function (chunk, _, cb) {\n    hasEvent && proxy.emit(eventName, url);\n    if (receiveStatus.pause) {\n      receiveStatus.chunk = chunk;\n      receiveStatus.callback = cb;\n      return;\n    }\n    chunk = execHandleFrame(frameCtx, chunk, {});\n    if (chunk) {\n      emitDataToProxy(req, chunk, undefined, receiveStatus.ignore);\n      if (receiveStatus.ignore) {\n        chunk = null;\n      }\n    }\n    cb(null, chunk || null);\n  };\n}\n\nfunction clearupStatus(conns, reqId, sendStatus, receiveStatus) {\n  delete conns[reqId];\n  sendStatus.callback = null;\n  receiveStatus.callback = null;\n  sendStatus.addToReceiver = null;\n  receiveStatus.addToReceiver = null;\n  clearInterval(sendStatus.timer);\n  clearInterval(receiveStatus.timer);\n}\n\nfunction getBinary(data, len) {\n  return len > MAX_PAYLOAD ? data.slice(0, MAX_PAYLOAD) : data;\n}\n\nfunction drainData(status, receiver) {\n  status.data.forEach(function (item) {\n    status.sender.send(item.data, item);\n    receiver.onData(item.data, item, true);\n  });\n  status.data = [];\n  receiver.ping();\n}\n\nfunction handleFrame(receiver, socket, status, chunk, cb, toServer) {\n  if (!receiver.existsCacheData) {\n    status.ignoring = status.ignore;\n    if (status.pause) {\n      status.callback = cb;\n      status.chunk = chunk;\n      status.addToReceiver = function () {\n        receiver.add(chunk);\n      };\n      drainData(status, receiver);\n      return;\n    }\n    if (status.ignore) {\n      drainData(status, receiver);\n    }\n  }\n  var toRead = receiver.add(chunk);\n  if (status.ignoring) {\n    if (!status.ignore && toRead >= 0) {\n      status.ignoring = false;\n      socket.write(chunk.slice(toRead));\n    }\n    chunk = null;\n  } else if (\n    toRead >= 0 &&\n    (status.pause || status.ignore || status.data.length)\n  ) {\n    if (toRead) {\n      var readAll = toRead === chunk.length;\n      status.chunk = readAll ? null : chunk.slice(toRead);\n      socket.write(readAll ? chunk : chunk.slice(0, toRead));\n    } else {\n      status.chunk = chunk;\n    }\n    if (status.pause) {\n      status.callback = cb;\n      drainData(status, receiver);\n      return;\n    }\n    if (status.ignore) {\n      status.ignoring = true;\n      chunk = null;\n      drainData(status, receiver);\n    }\n  }\n  if (chunk && status.timer) {\n    clearInterval(status.timer);\n    status.timer = null;\n  }\n  cb(null, chunk);\n}\n\nfunction clearTimer(status) {\n  if (!status.ignore && !status.pause) {\n    clearInterval(status.timer);\n    status.timer = null;\n  }\n}\n\nfunction getReceiver(ctx, fromServer) {\n  try {\n    return wsParser.getReceiver(ctx.res, fromServer, undefined, ctx.noDecompress);\n  } catch (e) { }\n}\n\nfunction getSender(socket, toServer) {\n  try {\n    return wsParser.getSender(socket, toServer);\n  } catch (e) { }\n}\n\nfunction execHandleFrame(frameCtx, data, opts, toServer) {\n  if (!frameCtx || !data) {\n    return data;\n  }\n  var name = toServer ? 'handleSendToServerFrame' : 'handleSendToClientFrame';\n  var filter = frameCtx[name];\n  if (typeof filter !== 'function') {\n    return data;\n  }\n  try {\n    opts = opts || {};\n    data = filter(data, opts);\n    if (data) {\n      data = util.toBuffer(data, opts.charset);\n      opts.length = data.length;\n    }\n    return data;\n  } catch (e) {\n    return Buffer.from(((e && e.message) || 'Error: unknown') + ' (' + name + ')');\n  }\n}\n\nfunction handleWsSend(ctx, reqTrans, sendStatus, handleTransform, frameCtx, eventName) {\n  var res = ctx.res;\n  var reqReceiver = getReceiver(ctx, false);\n  var hasFilter = frameCtx && typeof frameCtx.handleSendToServerFrame === 'function';\n  var hideFrame = ctx.hideFrame;\n  var reqSender = getSender(hasFilter ? reqTrans : res.pipeWriter || res, true);\n  if (!reqReceiver || !reqSender || (hideFrame && !hasFilter)) {\n    reqTrans._transform = handleTransform;\n    return;\n  }\n  var req = ctx.req;\n  var url = ctx.url;\n  var curEvent = eventName || 'wsRequest';\n  var hasEvent = ctx.hasEvent;\n  var reqId = req.reqId;\n\n  sendStatus.sender = reqSender;\n  util.onSocketEnd(res, function() {\n    reqReceiver.flush(function() {\n      reqReceiver.cleanup();\n    });\n  });\n  ctx.sendToServer = function (data, opts) {\n    if (sendStatus.data.length > MAX_COMPOSE_FRAME_COUNT) {\n      return false;\n    }\n    data = execHandleFrame(frameCtx, data, opts, true);\n    if (!data) {\n      return false;\n    }\n    opts.data = data;\n    sendStatus.data.push(opts);\n    if (\n      hasFilter ||\n      sendStatus.ignoring ||\n      sendStatus.callback ||\n      !reqReceiver.existsCacheData\n    ) {\n      drainData(sendStatus, reqReceiver);\n    }\n  };\n  reqReceiver.ping = function () {\n    if (eventName || sendStatus.timer || req.disable.pong) {\n      return;\n    }\n    res.write(PONG);\n    sendStatus.timer = setInterval(function () {\n      res.write(PONG);\n      clearTimer(sendStatus);\n    }, INTERVAL);\n  };\n  reqReceiver.onclose = function (code) {\n    ctx.req._errorCode = code;\n  };\n  reqReceiver.onData = function (data, opts, drain) {\n    var ignore;\n    if (drain !== true && hasFilter) {\n      data = execHandleFrame(frameCtx, data, opts);\n      if (!data) {\n        return;\n      }\n      ignore = sendStatus.ignore;\n      !ignore && reqSender.send(data, opts);\n    }\n    if (hideFrame) {\n      return;\n    }\n    var opcode = opts.opcode;\n    if (!opcode) {\n      opcode = opts.binary ? 2 : 1;\n    }\n    proxy.emit('frame', {\n      reqId: reqId,\n      frameId: getFrameId(),\n      isClient: true,\n      mask: eventName ? undefined : opts.mask,\n      ignore: ignore || (opts.data ? undefined : sendStatus.ignoring),\n      bin: getBinary(data, opts.length),\n      compressed: eventName ? undefined : opts.compressed,\n      notDecompressed: opts.notDecompressed,\n      length: opts.length,\n      unzipLen: data && opts.compressed ? data.length : undefined,\n      opcode: eventName ? undefined : opcode\n    });\n  };\n  reqReceiver.onerror = function (err) {\n    if (hideFrame || req._emittedClosed) {\n      return;\n    }\n    req._emittedClosed = true;\n    proxy.emit('frame', {\n      reqId: reqId,\n      frameId: getFrameId(),\n      isClient: true,\n      err: err.message,\n      bin: ''\n    });\n  };\n  if (hasFilter) {\n    var emitData = reqTrans.emit;\n    var write = reqTrans.write;\n    reqTrans.write = function (chunk, opts, cb) {\n      if ((chunk = util.toBuffer(chunk))) {\n        if (opts === 'binary') {\n          emitData.call(this, 'data', chunk, opts);\n          return;\n        }\n        return write.call(this, chunk, opts, cb);\n      }\n    };\n    reqTrans.emit = function (type, chunk) {\n      if (type === 'data' && chunk) {\n        return reqReceiver.add(chunk);\n      }\n      return emitData.apply(this, arguments);\n    };\n    sendStatus.changePauseState = function() {\n      if (sendStatus.pause) {\n        reqTrans.pause();\n      } else {\n        reqTrans.resume();\n        drainData(sendStatus, reqReceiver);\n      }\n    };\n    return;\n  }\n  reqTrans._transform = function (chunk, _, cb) {\n    hasEvent && proxy.emit(curEvent, url);\n    handleFrame(reqReceiver, res, sendStatus, chunk, cb, true);\n  };\n}\n\nfunction handleWsReceive(ctx, resTrans, receiveStatus, handleTransform, frameCtx, eventName) {\n  var req = ctx.req;\n  var resReceiver = getReceiver(ctx, true);\n  var hasFilter = frameCtx && typeof frameCtx.handleSendToClientFrame === 'function';\n  var hideFrame = ctx.hideFrame;\n  var resSender = getSender(hasFilter ? resTrans : req.pipeWriter || req);\n  if (!resReceiver || !resSender || (hideFrame && !hasFilter)) {\n    resTrans._transform = handleTransform;\n    return;\n  }\n  var url = ctx.url;\n  var curEvent = eventName || 'wsRequest';\n  var hasEvent = ctx.hasEvent;\n  var reqId = req.reqId;\n  receiveStatus.sender = resSender;\n  util.onSocketEnd(req, function() {\n    resReceiver.flush(function() {\n      resReceiver.cleanup();\n    });\n  });\n  ctx.sendToClient = function (data, opts) {\n    if (receiveStatus.data.length > MAX_COMPOSE_FRAME_COUNT) {\n      return false;\n    }\n    data = execHandleFrame(frameCtx, data, opts);\n    if (!data) {\n      return false;\n    }\n    opts.data = data;\n    receiveStatus.data.push(opts);\n    if (\n      hasFilter ||\n      receiveStatus.ignoring ||\n      receiveStatus.callback ||\n      !resReceiver.existsCacheData\n    ) {\n      drainData(receiveStatus, resReceiver);\n    }\n  };\n  resReceiver.ping = function () {\n    if (eventName || receiveStatus.timer || req.disable.ping) {\n      return;\n    }\n    req.write(PING);\n    receiveStatus.timer = setInterval(function () {\n      req.write(PING);\n      clearTimer(receiveStatus);\n    }, INTERVAL);\n  };\n  resReceiver.onclose = function (code) {\n    ctx.res._errorCode = code;\n  };\n  resReceiver.onData = function (data, opts, drain) {\n    var ignore;\n    if (drain !== true && hasFilter) {\n      data = execHandleFrame(frameCtx, data, opts);\n      if (!data) {\n        return;\n      }\n      ignore = receiveStatus.ignore;\n      !ignore && resSender.send(data, opts);\n    }\n    if (hideFrame) {\n      return;\n    }\n    var opcode = opts.opcode;\n    if (!opcode) {\n      opcode = opts.binary ? 2 : 1;\n    }\n    proxy.emit('frame', {\n      reqId: reqId,\n      frameId: getFrameId(),\n      bin: getBinary(data, opts.length),\n      mask: eventName ? undefined : opts.mask,\n      ignore: ignore || (opts.data ? undefined : receiveStatus.ignoring),\n      compressed: eventName ? undefined : opts.compressed,\n      notDecompressed: opts.notDecompressed,\n      length: opts.length,\n      unzipLen: data && opts.compressed ? data.length : undefined,\n      opcode: eventName ? undefined : opcode\n    });\n  };\n  resReceiver.onerror = function (err) {\n    if (hideFrame || req._emittedClosed) {\n      return;\n    }\n    req._emittedClosed = true;\n    proxy.emit('frame', {\n      reqId: reqId,\n      frameId: getFrameId(),\n      err: err.message,\n      bin: ''\n    });\n  };\n  if (hasFilter) {\n    var emitData = resTrans.emit;\n    var write = resTrans.write;\n    resTrans.write = function (chunk, opts, cb) {\n      if ((chunk = util.toBuffer(chunk))) {\n        if (opts === 'binary') {\n          emitData.call(this, 'data', chunk, opts);\n          return;\n        }\n        return write.call(this, chunk, opts, cb);\n      }\n    };\n    resTrans.emit = function (type, chunk) {\n      if (type === 'data' && chunk) {\n        return resReceiver.add(chunk);\n      }\n      return emitData.apply(this, arguments);\n    };\n    receiveStatus.changePauseState = function() {\n      if (receiveStatus.pause) {\n        resTrans.pause();\n      } else {\n        resTrans.resume();\n        drainData(receiveStatus, resReceiver);\n      }\n    };\n    return;\n  }\n  resTrans._transform = function (chunk, _, cb) {\n    hasEvent && proxy.emit(curEvent, url);\n    handleFrame(resReceiver, req, receiveStatus, chunk, cb);\n  };\n}\n\nfunction getContext(req, res, hasEvent, sendStatus, receiveStatus, noDecompress) {\n  var reqId = req.reqId;\n  var ctx = (conns[reqId] = {\n    customParser: req.customParser,\n    req: req,\n    res: res,\n    hasEvent: hasEvent,\n    url: req.fullUrl,\n    noDecompress: noDecompress,\n    charset: util.getCharset(res.headers['content-type']) || '',\n    clearup: function () {\n      clearupStatus(conns, reqId, sendStatus, receiveStatus);\n    },\n    setSendStatus: function (status) {\n      setConnStatus(ctx, status, sendStatus, 'sendStatus');\n    },\n    setReceiveStatus: function (status) {\n      setConnStatus(ctx, status, receiveStatus);\n    }\n  });\n  initStatus(ctx, req.enable);\n  return ctx;\n}\n\nexports.setContext = getContext;\n\nexports.removeContext = function (req) {\n  delete conns[req.reqId];\n};\n\nfunction cacheFrames(frames, data) {\n  if (frames.length < MAX_COMPOSE_FRAME_COUNT) {\n    frames.push(data);\n  }\n  return frames;\n}\n\nfunction setFrameScript(opts, data, toServer) {\n  opts = opts ? extend({}, opts) : {};\n  opts.frameScript = true;\n  opts.length = data.length;\n  if (toServer) {\n    opts.mask = true;\n  }\n  return opts;\n}\n\nfunction formatFrameScriptArgs(data, opts, toServer) {\n  data = util.toBuffer(data, opts && opts.charset);\n  return data && [data, setFrameScript(opts, data, toServer)];\n}\n\nfunction getFrameSender(options, toServer) {\n  return function (data, opts) {\n    var ctx = options.ctx;\n    if (ctx === false) {\n      return;\n    }\n    var args = formatFrameScriptArgs(data, opts, toServer);\n    if (!args) {\n      return;\n    }\n    if (!ctx) {\n      var key = toServer ? 'clientFrames' : 'serverFrames';\n      options[key] = cacheFrames(options[key] || [], args);\n      return;\n    }\n    var name = toServer ? 'sendToServer' : 'sendToClient';\n    ctx[name] && ctx[name](args[0], args[1]);\n  };\n}\n\nfunction getFrameCtx(options, callback) {\n  var req = options.req;\n  rules.getFrameScriptCtx(req.rules.frameScript, req, options.res, {\n    sendToClient: getFrameSender(options),\n    sendToServer: getFrameSender(options, true)\n  }, callback);\n}\n\nfunction cleanCacheFrames(ctx, options) {\n  var clientFrames = options.clientFrames;\n  var serverFrames = options.serverFrames;\n  if (clientFrames && ctx.sendToServer) {\n    clientFrames.forEach(function (item) {\n      ctx.sendToServer(item[0], item[1]);\n    });\n    options.clientFrames = null;\n  }\n  if (serverFrames && ctx.sendToClient) {\n    serverFrames.forEach(function (item) {\n      ctx.sendToClient(item[0], item[1]);\n    });\n    options.serverFrames = null;\n  }\n}\n\nexports.handleUpgrade = function (req, res) {\n  var hide = util.isHide(req);\n  var customParser = req.customParser;\n  var hideFrame = hide || customParser;\n  handleClose(req, res, hideFrame || req.isPluginReq);\n  if (req.isPluginReq) {\n    return req.pipe(res).pipe(req);\n  }\n  var url = req.fullUrl;\n  var reqId = req.reqId;\n  var sendStatus = { data: [] };\n  var receiveStatus = { data: [] };\n  var emitError = function (err) {\n    req.emit('error', err);\n  };\n  var reqTrans = util.createTransform();\n  var resTrans = util.createTransform();\n  var noDecompress = util.isDisable(req, 'wsDecompress');\n\n  req._noDecompress = noDecompress;\n  reqTrans.on('error', emitError);\n  resTrans.on('error', emitError);\n  res.headers = res.headers || {};\n  req.wsExts = res.headers['sec-websocket-extensions'] || '';\n  var options = { req: req, res: res };\n  pluginMgr.getWsPipe(\n    req,\n    res,\n    function (reqRead, reqWrite, resRead, resWrite) {\n      customParser && removePending(reqId);\n      getFrameCtx(options, function(frameCtx) {\n        if (req._hasClosed) {\n          return;\n        }\n        var hasEvent = util.listenerCount(proxy, 'wsRequest');\n        var handleTransform = function (chunk, _, cb) {\n          hasEvent && proxy.emit('wsRequest', url);\n          cb(null, chunk);\n        };\n        if (hideFrame && !frameCtx) {\n          reqTrans._transform = resTrans._transform = handleTransform;\n          options.ctx = false;\n        } else {\n          reqTrans.headers = resTrans.headers = res.headers;\n          if (reqWrite) {\n            reqWrite.headers = res.headers;\n            res.pipeWriter = reqWrite;\n          }\n          if (resWrite) {\n            resWrite.headers = res.headers;\n            req.pipeWriter = resWrite;\n          }\n          var ctx = options.ctx = (conns[reqId] = getContext(\n            req,\n            res,\n            hasEvent,\n            sendStatus,\n            receiveStatus,\n            noDecompress\n          ));\n          ctx.hideFrame = req.hideFrame = hideFrame;\n          handleWsSend(ctx, reqTrans, sendStatus, handleTransform, frameCtx);\n          handleWsReceive(ctx, resTrans, receiveStatus, handleTransform, frameCtx);\n          cleanCacheFrames(ctx, options);\n        }\n        pipeStream(req, reqRead)\n        .pipe(reqTrans)\n        .pipe(pipeStream(reqWrite, res, true));\n        pipeStream(res, resRead)\n        .pipe(resTrans)\n        .pipe(pipeStream(resWrite, req, true));\n      });\n    }\n  );\n};\n\nexports.handleConnect = function (req, res, isUpgrade) {\n  var eventName = isUpgrade ? 'wsRequest' : 'tunnelRequest';\n  var hasEvent = util.listenerCount(proxy, eventName);\n  var isConn = isUpgrade || req.inspectFrames;\n  var customParser = req.customParser;\n  var hide = util.isHide(req);\n  var hideFrame = customParser || hide;\n  handleClose(req, res, hideFrame || req.isPluginReq || !isConn);\n  if (req.isPluginReq || (!isConn && !hasEvent)) {\n    return req.pipe(res).pipe(req);\n  }\n  var url = req.fullUrl;\n  var reqTrans = util.createTransform();\n  var resTrans = util.createTransform();\n  var handleTransform = function (chunk, _, cb) {\n    hasEvent && proxy.emit(eventName, url);\n    cb(null, chunk);\n  };\n  var emitError = function (err) {\n    req.emit('error', err);\n  };\n  reqTrans.on('error', emitError);\n  resTrans.on('error', emitError);\n\n  if (!isConn) {\n    reqTrans._transform = resTrans._transform = handleTransform;\n    return req.pipe(reqTrans).pipe(res).pipe(resTrans).pipe(req);\n  }\n  var reqId = req.reqId;\n  var sendStatus = { data: [] };\n  var receiveStatus = { data: [] };\n  res.headers = res.headers || req.headers;\n  req._isUpgrade = isUpgrade;\n  var options = { req: req, res: res };\n  pluginMgr.getTunnelPipe(\n    req,\n    res,\n    function (reqRead, reqWrite, resRead, resWrite) {\n      customParser && removePending(reqId);\n      getFrameCtx(options, function(frameCtx) {\n        if (req._hasClosed) {\n          return;\n        }\n        if (hideFrame && !frameCtx) {\n          reqTrans._transform = resTrans._transform = handleTransform;\n          options.ctx = false;\n        } else {\n          req.wsExts = res.wsExts = reqTrans.wsExts = resTrans.wsExts = '';\n          if (reqWrite) {\n            reqWrite.wsExts = '';\n            res.pipeWriter = reqWrite;\n          }\n          if (resWrite) {\n            resWrite.wsExts = '';\n            req.pipeWriter = resWrite;\n          }\n          var ctx = options.ctx = getContext(req, res, hasEvent, sendStatus, receiveStatus);\n          ctx.hideFrame = req.hideFrame = hideFrame;\n          if (reqRead && reqWrite) {\n            handleWsSend(ctx, reqTrans, sendStatus, handleTransform, frameCtx, eventName);\n          } else {\n            handleConnSend(ctx, reqTrans, sendStatus, frameCtx, eventName);\n          }\n          if (resRead && resWrite) {\n            handleWsReceive(ctx, resTrans, receiveStatus, handleTransform, frameCtx, eventName);\n          } else {\n            handleConnReceive(ctx, resTrans, receiveStatus, frameCtx, eventName);\n          }\n          cleanCacheFrames(ctx, options);\n        }\n\n        pipeStream(req, reqRead)\n        .pipe(reqTrans)\n        .pipe(pipeStream(reqWrite, res, true));\n        pipeStream(res, resRead)\n        .pipe(resTrans)\n        .pipe(pipeStream(resWrite, req, true));\n      });\n    }\n  );\n};\n\nfunction destroy(reqId) {\n  var ctx = conns[reqId];\n  if (!ctx) {\n    return;\n  }\n  delete conns[reqId];\n  ctx.req.destroy();\n  ctx.res.destroy();\n}\n\nexports.abort = destroy;\nexports.destroy = destroy;\nexports.destroyAll = function() {\n  Object.keys(conns).forEach(destroy);\n};\n\nexports.getStatus = function (reqId) {\n  var ctx = reqId && conns[reqId];\n  if (!ctx) {\n    return;\n  }\n  return {\n    sendStatus: ctx.sendStatus,\n    receiveStatus: ctx.receiveStatus\n  };\n};\n\nexports.removePending = function (req) {\n  removePending(req.reqId);\n};\n\nexports.setPending = function (req) {\n  var reqId = req.customParser && req.reqId;\n  if (reqId && pendingReqList.indexOf(reqId) === -1) {\n    pendingReqList.push(reqId);\n    if (pendingReqList.length > 2000) {\n      pendingReqList = pendingReqList.slice(-1600);\n    }\n  }\n};\n\nexports.exists = function (reqId) {\n  return reqId && conns[reqId];\n};\n\nexports.getData = function (reqId) {\n  var ctx = reqId && conns[reqId];\n  if (ctx) {\n    var result = {\n      sendStatus: ctx.sendStatus,\n      receiveStatus: ctx.receiveStatus,\n      toServer: ctx.toServerData,\n      toClient: ctx.toClientData\n    };\n    delete ctx.toServerData;\n    delete ctx.toClientData;\n    return result;\n  }\n  return pendingReqList.indexOf(reqId) === -1 ? undefined : 1;\n};\n\nexports.changeStatus = function (data) {\n  var ctx = conns[data.reqId];\n  if (!ctx) {\n    return;\n  }\n  if (data.sendStatus >= 0) {\n    ctx.setSendStatus(data.sendStatus);\n  } else {\n    ctx.setReceiveStatus(data.receiveStatus);\n  }\n  return true;\n};\n\nfunction getBuffer(data, charset) {\n  if (data.base64) {\n    try {\n      return Buffer.from(data.base64, 'base64');\n    } catch (e) {}\n  } else if (data.text) {\n    return util.toBuffer(data.text, charset);\n  }\n}\n\nexports.sendData = function (data) {\n  var ctx = conns[data.reqId];\n  if (!ctx) {\n    return;\n  }\n  var buf = getBuffer(data, ctx.charset);\n  if (!buf) {\n    return;\n  }\n  var toServer = data.target === 'server';\n  var binary = data.type === 'bin';\n  data = { binary: binary };\n  if (ctx.customParser) {\n    var name = toServer ? 'toServerData' : 'toClientData';\n    var dataList = (ctx[name] = ctx[name] || []);\n    if (dataList.length > MAX_COMPOSE_FRAME_COUNT) {\n      return false;\n    }\n    data.base64 = buf.toString('base64');\n    dataList.push(data);\n    return;\n  }\n  if (!(toServer ? ctx.sendToServer : ctx.sendToClient)) {\n    return;\n  }\n  data.binary = binary;\n  data.length = buf.length;\n  data.composer = true;\n  if (toServer) {\n    data.mask = true;\n    ctx.sendToServer(buf, data);\n  } else {\n    ctx.sendToClient(buf, data);\n  }\n};\n"
  },
  {
    "path": "lib/tunnel.js",
    "content": "var socks = require('sockx');\nvar parseUrl = require('./util/parse-url-safe');\nvar net = require('net');\nvar extend = require('extend');\nvar LRU = require('lru-cache');\nvar EventEmitter = require('events').EventEmitter;\nvar hparser = require('hparser');\nvar dispatch = require('./https');\nvar util = require('./util');\nvar rules = require('./rules');\nvar socketMgr = require('./socket-mgr');\nvar rulesUtil = require('./rules/util');\nvar common = require('./util/common');\nvar ca = require('./https/ca');\nvar pluginMgr = require('./plugins');\nvar config = require('./config');\n\nvar hasCustomCerts = ca.hasCustomCerts;\nvar existsCustomCert = ca.existsCustomCert;\nvar IP_CACHE = new LRU({ max: 600 });\nvar LOCALHOST = '127.0.0.1';\nvar X_RE = /^x/;\nvar STATUS_CODES = require('http').STATUS_CODES || {};\nvar getRawHeaderNames = hparser.getRawHeaderNames;\nvar formatHeaders = hparser.formatHeaders;\nvar getRawHeaders = hparser.getRawHeaders;\nvar CRONET_RE = /\\bCronet\\b/;\nvar SYS_HOST_RE = /^(?:[^/:]+\\.)?(?:apple|cdn-apple|icloud|office|office365|mzstatic|apple-cloudkit|microsoft|parallels)\\.(?:com|cn|com\\.cn)$/;\nvar TUNNEL_RE = /^tunnel:\\/\\//;\nvar clientIpKey = config.CLIENT_IP_HEADER;\n\nfunction emitAborted(data, reqEmitter, code) {\n  data.reqError = true;\n  data.res.statusCode = code || 'aborted';\n  data.req.body = 'aborted';\n  reqEmitter.emit('abort', data);\n}\n\nfunction tunnelProxy(server, proxy, type) {\n  proxy.getTunnelIp = function (id) {\n    return IP_CACHE.get(id);\n  };\n  var fromHttpServer = type === 2;\n  var fromHttpsServer = type === 1;\n  server.on('connect', function (req, reqSocket) {\n    //ws, wss, https proxy\n    var headers = req.headers;\n    if (headers[config.WEBUI_HEAD]) {\n      delete headers[config.WEBUI_HEAD];\n      reqSocket.destroy();\n      return;\n    }\n    req.fromHttpServer = reqSocket.fromHttpServer = fromHttpServer;\n    req.fromHttpsServer = reqSocket.fromHttpsServer = fromHttpsServer;\n    var tunnelUrl = (req.fullUrl = util.setProtocol(\n      util.isTunnelHost(req.url) ? req.url : headers.host,\n      true\n    ));\n    var options;\n    var socketErr;\n    var _emitError;\n    var _parseUrl = function (_url, port) {\n      _url = _url || tunnelUrl;\n      options = req.options = parseUrl(_url);\n      var curPort = options.port;\n      options.port = options.port || port || 443;\n      if (!curPort && options.host) {\n        if (options.host.indexOf(':') === -1) {\n          options.host += ':' + options.port;\n        } else if (net.isIPv6(options.host)) {\n          options.host = '[' + options.host + ']:' + options.port;\n        }\n      }\n      return options;\n    };\n    _parseUrl();\n    tunnelUrl = req.fullUrl = 'tunnel://' + options.host;\n    proxy.emit('_request', tunnelUrl);\n    var resSocket, proxyClient, respond, reqEmitter, data, originPort;\n    var reqData, resData, res, rollBackTunnel, buf;\n    req.isTunnel = true;\n    req.method = util.toUpperCase(req.method) || 'CONNECT';\n    var clientInfo = util.parseClientInfo(req);\n    reqSocket._disabledProxyRules = req._disabledProxyRules;\n    reqSocket.clientIp = req.clientIp = util.getClientIp(req, clientInfo[0]);\n    reqSocket.clientPort = req.clientPort =\n      clientInfo[1] || util.getClientPort(req);\n    req._remoteAddr = clientInfo[2] || util.getRemoteAddr(req);\n    req._remotePort = clientInfo[3] || util.getRemotePort(req);\n    delete headers[config.CLIENT_PORT_HEADER];\n    var now = Date.now();\n    req.reqId = reqSocket.reqId = util.getReqId(now);\n    reqSocket.headers = headers;\n    reqSocket.fromTunnel = req.fromTunnel;\n    reqSocket.fromComposer = req.fromComposer;\n    req.isPluginReq = reqSocket.isPluginReq = util.checkPluginReqOnce(req, true);\n    util.onSocketEnd(reqSocket, function (err) {\n      // 处理可能的证书校验出错\n      if (config.captureData && !util.checkHideProp(req.enable || '', req.disable || '', 'CaptureError') && dispatch.getTunnelDataOnce(reqSocket)) {\n        resData = {headers: {}};\n        data = {\n          id: req.reqId,\n          url: options.host,\n          fc: req.fromComposer ? 1 : undefined,\n          startTime: now,\n          dnsTime: now,\n          captureError: true,\n          rules: req.rules,\n          req: {\n            _clientId: util.getComposerClientId(headers),\n            ip: req.clientIp,\n            port: req.clientPort,\n            method: req.method,\n            httpVersion: req.httpVersion || '1.1',\n            headers: headers,\n            rawHeaderNames: getRawHeaderNames(req.rawHeaders) || {}\n          },\n          res: resData,\n          isHttps: true\n        };\n        data.endTime = data.responseTime = data.requestTime = Date.now();\n        reqEmitter = new EventEmitter();\n        proxy.emit('request', reqEmitter, data);\n        emitAborted(data, reqEmitter, 'captureError');\n        pluginMgr.resolveWhistlePlugins(req);\n        pluginMgr.postStats(req);\n        pluginMgr.postStats(req, resData);\n      }\n      socketErr = err;\n      if (req.isLogRequests) {\n        --util.proc.tunnelRequests;\n      }\n      req.isLogRequests = false;\n      if (_emitError) {\n        _emitError(err);\n      } else {\n        reqSocket.destroy();\n      }\n    });\n    var hostname = options.hostname;\n    var isICloudDB = hostname === 'p22-ckdatabase.icloud.com';\n    var isIPHost = !isICloudDB && net.isIP(hostname);\n    var policy = headers[config.WHISTLE_POLICY_HEADER];\n    var weakTunnel = policy == 'weakTunnel';\n    var useTunnelPolicy = weakTunnel || policy == 'tunnel';\n    var inspect = useTunnelPolicy;\n    useTunnelPolicy = useTunnelPolicy || policy == 'connect';\n    var enableTunnelAck =\n      useTunnelPolicy && req.headers[common.ACK_HEADER];\n    var isLocalUIUrl = !useTunnelPolicy && config.isLocalUIUrl(hostname);\n    if (isLocalUIUrl ? isIPHost : util.isLocalHost(hostname)) {\n      isLocalUIUrl =\n        options.port == config.port || options.port == config.uiport;\n    }\n    var _rules = {};\n    if (isICloudDB || isLocalUIUrl) {\n      req.enable = req.disable = req._filters = _rules;\n    } else {\n      _rules = rules.initRules(req);\n    }\n    req.rules = reqSocket.rules = _rules;\n    reqSocket._globalPluginVars = req._globalPluginVars;\n    reqSocket.isLocalUIUrl = isLocalUIUrl;\n    rules.resolveRulesFile(req, function () {\n      var filter = req._filters;\n      var disable = req.disable;\n      var isDisabledProps = function() {\n        return disable.intercept || disable.https || disable.capture;\n      };\n      var isDisableIntercept = function () {\n        return (\n          isICloudDB ||\n          useTunnelPolicy ||\n          isDisabledProps() ||\n          (req.isPluginReq && policy !== 'capture')\n        );\n      };\n      var isCustomIntercept = function () {\n        return (\n          filter.https ||\n          filter.capture ||\n          filter.intercept ||\n          policy === 'intercept' ||\n          policy === 'capture'\n        );\n      };\n      var isWebPort =\n        options.port == 80 || options.port == 443 || isCustomIntercept();\n      var matchCustomCert;\n      var isEnableIntercept = function () {\n        if (isCustomIntercept()) {\n          return true;\n        }\n        if (!rulesUtil.properties.isEnableCapture()) {\n          matchCustomCert = !!existsCustomCert(hostname);\n          return matchCustomCert;\n        }\n        var ua = headers['user-agent'];\n        return ua ? !CRONET_RE.test(ua) : !SYS_HOST_RE.test(hostname);\n      };\n      var isIntercept = function () {\n        if (isLocalUIUrl || ((!isDisableIntercept() && isEnableIntercept()) ||\n          (weakTunnel && !isDisabledProps() && isCustomIntercept()))) {\n          return true;\n        }\n        if (!isIPHost || !hasCustomCerts()) {\n          return false;\n        }\n        reqSocket.useProxifier =\n          (config.proxifier2 || (config.proxifier && isWebPort)) &&\n          !disable.proxifier;\n        return reqSocket.useProxifier;\n      };\n      var resolvedPlugin;\n      if (isIntercept()) {\n        if (util.isAuthCapture(req)) {\n          req.justAuth = true;\n          resolvedPlugin = true;\n        }\n      } else {\n        resolvedPlugin = true;\n      }\n      var plugin = resolvedPlugin ? pluginMgr.resolveWhistlePlugins(req) : null;\n      var handlePluginRules = function (_rulesMgr) {\n        if (_rulesMgr) {\n          req.pluginRules = _rulesMgr;\n          req.curUrl = tunnelUrl;\n          util.mergeRules(req, _rulesMgr.resolveReqRules(req));\n          util.filterWeakRule(req);\n          plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));\n        } else {\n          util.filterWeakRule(req);\n        }\n        filter = req._filters;\n        disable = req.disable;\n      };\n      if (req.whistlePlugins) {\n        if (\n          matchCustomCert ||\n          (matchCustomCert == null && existsCustomCert(hostname))\n        ) {\n          req._existsCustomCert = true;\n          req._enableCapture = true;\n        } else if (isEnableIntercept()) {\n          req._enableCapture = true;\n        }\n      }\n      pluginMgr.getTunnelRules(req, function (_rulesMgr) {\n        handlePluginRules(_rulesMgr);\n        policy = headers[config.WHISTLE_POLICY_HEADER];\n        if (!reqSocket._hasError && !req._authForbidden &&\n          !req.fromComposer && (req._forceCapture || isIntercept())) {\n          reqSocket.rulesHeaders = req.rulesHeaders;\n          reqSocket.enable = req.enable;\n          reqSocket.disable = req.disable;\n          reqSocket.tunnelHostname = hostname;\n          reqSocket.rules = _rules;\n          reqSocket._pluginVars = req._pluginVars;\n          dispatch(\n            reqSocket,\n            function (chunk) {\n              if (\n                isLocalUIUrl ||\n                (isIPHost &&\n                  util.isLocalAddress(hostname) &&\n                  options.port == config.port)\n              ) {\n                return reqSocket.destroy();\n              }\n              buf = chunk;\n              rollBackTunnel = true;\n              ensureLoadRules();\n            },\n            !req.enable.socket && isWebPort\n          );\n          util.setEstablished(reqSocket);\n          return;\n        }\n\n        ensureLoadRules();\n\n        function ensureLoadRules() {\n          if (!req.justAuth) {\n            if (resolvedPlugin) {\n              return handleTunnel();\n            }\n          } else {\n            req.justAuth = false;\n          }\n          if (!resolvedPlugin) {\n            plugin = pluginMgr.resolveWhistlePlugins(req);\n          }\n          pluginMgr.getTunnelRules(req, function (_rulesMgr2) {\n            handlePluginRules(_rulesMgr2);\n            handleTunnel();\n          });\n        }\n\n        function handleTunnel() {\n          if (req.isLogRequests !== false) {\n            ++util.proc.tunnelRequests;\n            ++util.proc.totalTunnelRequests;\n            req.isLogRequests = true;\n          }\n          var reqRawHeaderNames = getRawHeaderNames(req.rawHeaders) || {};\n          var enable = req.enable;\n          inspect =\n            inspect || util.isInspect(enable) || util.isCustomParser(req);\n          reqData = {\n            _clientId: util.getComposerClientId(headers),\n            ip: req.clientIp,\n            port: req.clientPort,\n            method: req.method,\n            httpVersion: req.httpVersion || '1.1',\n            headers: headers,\n            rawHeaderNames: reqRawHeaderNames\n          };\n          resData = { headers: {} };\n          reqEmitter = new EventEmitter();\n          data = {\n            id: req.reqId,\n            url: options.host,\n            fc: req.fromComposer ? 1 : undefined,\n            startTime: Date.now(),\n            rules: _rules,\n            req: reqData,\n            res: resData,\n            isHttps: true,\n            inspect: inspect,\n            rulesHeaders: req.rulesHeaders\n          };\n          if (util.showPluginReq(req) && !util.isHide(req)) {\n            data.abort = emitError;\n            if (req.isPluginReq) {\n              data.isPR = 1;\n            }\n            proxy.emit('request', reqEmitter, data);\n          }\n          if (reqSocket._hasError) {\n            return emitError(socketErr);\n          }\n          util.parseRuleJson(_rules.reqHeaders, function (reqHeaders) {\n            if (reqSocket._hasError) {\n              return emitError(socketErr);\n            }\n            _emitError = emitError;\n            var customXFF;\n            if (reqHeaders) {\n              reqHeaders = util.lowerCaseify(reqHeaders, reqRawHeaderNames);\n              customXFF = reqHeaders[clientIpKey];\n              delete reqHeaders[clientIpKey];\n              extend(headers, reqHeaders);\n            }\n\n            if (disable.clientIp || disable.clientIP) {\n              delete headers[clientIpKey];\n            } else {\n              var forwardedFor = util.getMatcherValue(_rules.forwardedFor);\n              if (net.isIP(forwardedFor)) {\n                headers[clientIpKey] = forwardedFor;\n              } else if (net.isIP(customXFF)) {\n                headers[clientIpKey] = customXFF;\n              } else if (util.isLocalAddress(req.clientIp)) {\n                delete headers[clientIpKey];\n              } else {\n                headers[clientIpKey] = req.clientIp;\n              }\n            }\n\n            pluginMgr.postStats(req);\n            if (util.needAbortReq(req)) {\n              return reqSocket.destroy();\n            }\n\n            res = util.getStatusCodeFromRule(_rules);\n            if (res) {\n              var statusCode = res.statusCode;\n              util.deleteReqHeaders(req);\n              if (statusCode == 200) {\n                resSocket = util.getEmptyRes();\n                var reqDelay = util.getMatcherValue(_rules.reqDelay);\n                data.requestTime = data.dnsTime = Date.now();\n                if (reqDelay > 0) {\n                  setTimeout(handleConnect, reqDelay);\n                } else {\n                  handleConnect();\n                }\n                return;\n              }\n              return sendEstablished(statusCode);\n            }\n\n            pluginMgr.loadPlugin(\n              req.isPluginReq ? null : plugin,\n              function (err, ports) {\n                if (reqSocket._hasError) {\n                  return emitError(socketErr);\n                }\n                if (err) {\n                  return sendEstablished(500);\n                }\n                var tunnelPort = ports && ports.tunnelPort;\n                var proxyUrl;\n                if (tunnelPort) {\n                  proxyUrl = 'proxy://127.0.0.1:' + tunnelPort;\n                  reqSocket.customParser = req.customParser =\n                    util.getParserStatus(req);\n                  pluginMgr.addSessionInfo(req, _rules);\n                  headers[config.PLUGIN_HOOK_NAME_HEADER] =\n                    config.PLUGIN_HOOKS.TUNNEL;\n                  socketMgr.setPending(req);\n                  data.reqPlugin = 1;\n                }\n\n                var realUrl = _rules.rule && _rules.rule.url;\n                if (realUrl) {\n                  var isHttp;\n                  if (util.isUrl(realUrl)) {\n                    isHttp = realUrl[4] === ':';\n                    realUrl =\n                      'tunnel' + realUrl.substring(realUrl.indexOf(':'));\n                  }\n                  if (TUNNEL_RE.test(realUrl) && realUrl != tunnelUrl) {\n                    _parseUrl(realUrl, isHttp ? 80 : 443);\n                    tunnelUrl = 'tunnel://' + options.host;\n                    data.realUrl = tunnelUrl.replace('tunnel://', '');\n                  }\n                }\n                originPort = options.port;\n                if (_rules.ua) {\n                  var ua = util.getMatcherValue(_rules.ua);\n                  headers['user-agent'] = ua;\n                }\n                rules.getProxy(\n                  tunnelUrl,\n                  proxyUrl ? null : req,\n                  function (err, hostIp, hostPort) {\n                    var isInternalProxy;\n                    var proxyRule = (!proxyUrl && _rules.proxy) || '';\n                    if (!proxyUrl && proxyRule) {\n                      proxyUrl = util.rule.getMatcher(proxyRule);\n                    }\n                    var isXProxy;\n                    if (proxyUrl) {\n                      isXProxy = X_RE.test(proxyUrl);\n                      isInternalProxy = proxyRule.isInternal || util.isInternalProxy(req);\n                      var isSocks = proxyRule.isSocks;\n                      var isHttpsProxy = proxyRule.isHttps;\n                      var _url = 'http:' + util.removeProtocol(proxyUrl);\n                      data.proxy = true;\n                      getServerIp(_url, function (ip) {\n                        options = _parseUrl(\n                          _url,\n                          isSocks ? 1080 : isHttpsProxy ? 443 : 80\n                        );\n                        options.auth = options.auth || req._pacAuth;\n                        var isProxyPort = util.isProxyPort(options.port);\n                        if (isProxyPort && util.isLocalAddress(ip)) {\n                          return emitError(\n                            new Error(\n                              'Self loop (' + util.joinIpPort(ip, config.port) + ')'\n                            )\n                          );\n                        }\n                        var handleProxy = function (proxySocket, _res) {\n                          resSocket = proxySocket;\n                          res = _res;\n                          // 通知插件连接建立成功的回调\n                          handleConnect();\n                          handleError(resSocket);\n                          delete headers['x-whistle-frame-parser'];\n                        };\n                        var dstOptions = parseUrl(tunnelUrl);\n                        dstOptions.proxyHost = ip;\n                        dstOptions.proxyPort = parseInt(options.port, 10);\n                        dstOptions.port = dstOptions.port || 443;\n                        resData.port = dstOptions.proxyPort;\n                        dstOptions.host = dstOptions.hostname;\n                        util.setClientId(\n                          headers,\n                          req.enable,\n                          req.disable,\n                          req.clientIp,\n                          isInternalProxy\n                        );\n                        util.deleteReqHeaders(req);\n                        var _headers = extend({}, headers);\n                        _headers.host = util.joinIpPort(dstOptions.hostname, dstOptions.port || 443);\n                        if (disable.proxyUA) {\n                          delete _headers['user-agent'];\n                        }\n                        dstOptions.headers = formatHeaders(\n                          _headers,\n                          reqRawHeaderNames\n                        );\n                        if (isSocks) {\n                          dstOptions.proxyPort = options.port || 1080;\n                          dstOptions.localDNS = false;\n                          dstOptions.auths = config.getAuths(options);\n                        } else {\n                          dstOptions.proxyPort = options.port || 80;\n                          dstOptions.proxyAuth = options.auth;\n                          if (isProxyPort || util.isLocalPHost(req, true)) {\n                            _headers[config.WEBUI_HEAD] = 1;\n                          }\n                          _headers[common.ACK_HEADER] = 1;\n                        }\n                        var netMgr = isSocks ? socks : config;\n                        var reqDelay = util.getMatcherValue(_rules.reqDelay);\n                        util.setProxyHost(req, dstOptions, true);\n                        if (isHttpsProxy) {\n                          dstOptions.proxyServername = options.hostname;\n                        }\n                        var connectProxy = function () {\n                          if (respond) {\n                            return;\n                          }\n                          if (req.isPluginReq) {\n                            var host = util.joinIpPort(ip, dstOptions.proxyPort);\n                            if (req._phost && req._phost.host) {\n                              IP_CACHE.set(\n                                req.isPluginReq,\n                                req._phost.host + ', ' + host\n                              );\n                            } else {\n                              IP_CACHE.set(req.isPluginReq, host);\n                            }\n                          } else if (req._phost) {\n                            resData.phost = req._phost.host;\n                          }\n                          resData.port = dstOptions.proxyPort;\n                          req.serverPort = reqSocket.serverPort = resData.port;\n                          try {\n                            if (!tunnelPort && req._phost && req._proxyTunnel) {\n                              dstOptions.proxyTunnelPath = util.removeProtocol(\n                                tunnelUrl,\n                                true\n                              );\n                            }\n                            var s = netMgr.connect(dstOptions, handleProxy);\n                            proxyClient = s._sock || s;\n                            s.on('error', function (err) {\n                              if (isXProxy) {\n                                resData.phost = undefined;\n                                tunnel();\n                              } else {\n                                emitError(err);\n                              }\n                            });\n                          } catch (e) {\n                            emitError(e);\n                          }\n                        };\n                        if (reqDelay > 0) {\n                          setTimeout(connectProxy, reqDelay);\n                        } else {\n                          connectProxy();\n                        }\n                      });\n                    } else {\n                      tunnel(hostIp, hostPort);\n                    }\n                  }\n                );\n              }\n            );\n          }, req);\n        }\n        var retryConnect;\n        var retryXHost = 0;\n        var newIp;\n        function tunnel(hostIp, hostPort) {\n          getServerIp(\n            tunnelUrl,\n            function (ip, port) {\n              if (port) {\n                req.hostIp = resData.ip = util.joinIpPort(ip, port);\n                resData.port = port;\n              } else {\n                req.hostIp = resData.ip = ip;\n                // 不要赋值给port，否则重试时getServerIp会有端口\n                resData.port = port || originPort;\n              }\n              if (respond) {\n                return;\n              }\n              if (req.isPluginReq) {\n                IP_CACHE.set(req.isPluginReq, req.hostIp);\n              }\n              req.serverPort = reqSocket.serverPort = resData.port;\n              resSocket = net.connect(resData.port, ip, handleConnect);\n              if (retryConnect) {\n                handleError(resSocket);\n              } else {\n                retryConnect = function (err) {\n                  if (!newIp && (newIp = util.getLocalhostIP(err, req, hostname, ip))) {\n                    ip = newIp;\n                  } else if (\n                    retryXHost < 2 &&\n                    _rules.host &&\n                    X_RE.test(_rules.host.matcher)\n                  ) {\n                    ++retryXHost;\n                    retryConnect = false;\n                    if (retryXHost > 1) {\n                      req.curUrl = tunnelUrl;\n                      rules.lookupHost(req, function (_err, _ip) {\n                        if (_err) {\n                          return emitError(_err);\n                        }\n                        tunnel(_ip, originPort);\n                      });\n                      return;\n                    }\n                  }\n                  tunnel(ip, port);\n                };\n                var retried;\n                resSocket.on('error', function (err) {\n                  if (!retried) {\n                    retried = true;\n                    this.destroy && this.destroy();\n                    !respond &&\n                      !reqSocket._hasError &&\n                      retryConnect &&\n                      retryConnect(err);\n                  }\n                });\n              }\n            },\n            hostIp,\n            hostPort\n          );\n        }\n\n        function handleConnect() {\n          if (reqSocket._hasError) {\n            return emitError(socketErr);\n          }\n          if (retryConnect) {\n            resSocket.removeListener('error', retryConnect);\n            handleError(resSocket);\n            retryConnect = null;\n          }\n          reqSocket.inspectFrames = inspect;\n          reqSocket.fullUrl = tunnelUrl;\n          reqSocket.enable = req.enable;\n          reqSocket.disable = req.disable;\n          reqSocket._filters = req._filters;\n          reqSocket.hostIp = req.hostIp;\n          reqSocket.method = req.method;\n          reqSocket.headerRulesMgr = req.headerRulesMgr;\n          reqSocket.clientPort = req.clientPort;\n          reqSocket.globalValue = req.globalValue;\n          reqSocket._pluginVars = req._pluginVars;\n          resSocket.statusCode = resData.statusCode;\n          pluginMgr.resolvePipePlugin(reqSocket, function () {\n            if (reqSocket._hasError) {\n              return emitError(socketErr);\n            }\n            data.pipe = reqSocket._pipeRule;\n            if (reqSocket._pipePluginPorts) {\n              reqSocket.inspectFrames = data.inspect = true;\n              reqSocket.customParser = false;\n            }\n            var connHandler = function () {\n              if (buf) {\n                var _pipe = reqSocket.pipe;\n                reqSocket.pipe = function (stream) {\n                  if (buf) {\n                    stream.write(buf);\n                    buf = null;\n                  }\n                  return _pipe.apply(this, arguments);\n                };\n              }\n              socketMgr.handleConnect(reqSocket, resSocket);\n            };\n            var handleEstablished = function () {\n              if (useTunnelPolicy) {\n                sendEstablished(200, connHandler);\n              } else {\n                connHandler();\n                sendEstablished();\n              }\n            };\n            var resDelay = util.getMatcherValue(_rules.resDelay);\n            if (resDelay > 0) {\n              setTimeout(handleEstablished, resDelay);\n            } else {\n              handleEstablished();\n            }\n          });\n        }\n\n        function getServerIp(url, callback, hostIp, hostPort, proxyRule) {\n          var hostHandler = function (err, ip, port, host) {\n            if (host) {\n              (proxyRule || _rules).host = host;\n            }\n            data.requestTime = data.dnsTime = Date.now();\n            req.hostIp = resData.ip = ip || LOCALHOST;\n            reqEmitter.emit('send', data);\n            err ? emitError(err) : callback(ip, port);\n          };\n          if (hostIp) {\n            hostHandler(null, hostIp, hostPort);\n          } else {\n            req.curUrl = url;\n            rules.resolveHost(\n              req,\n              hostHandler,\n              req.pluginRules,\n              req.rulesFileMgr,\n              req.headerRulesMgr\n            );\n          }\n        }\n\n        function handleError(socket) {\n          socket.on('error', emitError);\n        }\n\n        function sendEstablished(code, cb) {\n          if (res) {\n            code = res.statusCode || 200;\n            if (!res.headers['proxy-agent']) {\n              res.headers['proxy-agent'] = config.appName;\n              res.rawHeaders = res.rawHeaders || [];\n              res.rawHeaders.push('proxy-agent', 'Proxy-Agent');\n            }\n          } else {\n            code = code || 200;\n            res = {\n              statusCode: code,\n              headers: {\n                'proxy-agent': config.appName\n              },\n              rawHeaders: ['proxy-agent', 'Proxy-Agent']\n            };\n          }\n          var tunnelAck = enableTunnelAck && cb && code == 200;\n          if (tunnelAck) {\n            res.headers[common.ALLOW_ACK] = 1;\n          }\n          var resHeaders = res.headers;\n          pluginMgr.getResRules(req, res, function () {\n            if (util.needAbortRes(req)) {\n              return reqSocket.destroy();\n            }\n            var reqRules = req.rules;\n            util.parseRuleJson(\n              rollBackTunnel ? null : reqRules.resHeaders,\n              function (newResHeaders) {\n                if (rollBackTunnel) {\n                  reqSocket.resume();\n                  cb && cb();\n                } else {\n                  var rawHeaderNames = getRawHeaderNames(res.rawHeaders) || {};\n                  if (newResHeaders) {\n                    util.lowerCaseify(newResHeaders, rawHeaderNames);\n                    extend(resHeaders, newResHeaders);\n                  }\n                  var responseFor = util.getMatcherValue(reqRules.responseFor);\n                  if (responseFor) {\n                    resHeaders['x-whistle-response-for'] = responseFor;\n                  }\n                  util.setResponseFor(reqRules, resHeaders, req, req.hostIp, req._phost);\n                  util.deleteResHeaders(req, resHeaders);\n                  code = util.getMatcherValue(reqRules.replaceStatus) || code;\n                  var isSuccess = code == 200;\n                  var message =\n                    isSuccess\n                      ? 'Connection Established'\n                      : STATUS_CODES[code] || 'unknown';\n                  var statusLine = ['HTTP/1.1', code, message].join(' ');\n                  var curHeaders = resHeaders;\n                  var rawData = statusLine;\n                  if (req.fromComposer) {\n                    curHeaders = extend({}, resHeaders);\n                    curHeaders['x-whistle-req-id'] = req.reqId;\n                    util.setFramesMode(curHeaders, isSuccess && inspect);\n                  }\n                  curHeaders = getRawHeaders(formatHeaders(curHeaders, rawHeaderNames));\n                  if (curHeaders) {\n                    rawData += '\\r\\n' + curHeaders;\n                  }\n                  rawData += '\\r\\n\\r\\n';\n                  try {\n                    if (code != 200) {\n                      reqSocket.end(rawData, cb);\n                    } else {\n                      if (tunnelAck) {\n                        reqSocket.write(rawData);\n                        reqSocket.once('data', function (chunk) {\n                          buf = chunk.length > 1 ? chunk.slice(1) : null;\n                          reqSocket.pause();\n                          cb();\n                        });\n                      } else {\n                        reqSocket.write(\n                          rawData,\n                          cb &&\n                            function () {\n                              setTimeout(cb, 16);\n                            }\n                        );\n                      }\n                    }\n                  } catch (e) {\n                    reqSocket.emit('error', e);\n                  }\n                }\n                if (reqEmitter) {\n                  respond = true;\n                  data.responseTime = data.endTime = Date.now();\n                  resData.rawHeaderNames = rawHeaderNames;\n                  resData.statusCode = code || 200;\n                  resData.ip = resData.ip || LOCALHOST;\n                  resData.headers = resHeaders;\n                  reqEmitter.emit('response', data);\n                  reqEmitter.emit('end', data);\n                }\n                pluginMgr.postStats(req, res);\n              },\n              req\n            );\n          });\n          return reqSocket;\n        }\n        var reqDestroyed, resDestroyed;\n        function emitError(err) {\n          if (!reqDestroyed) {\n            reqDestroyed = true;\n            reqSocket.destroy();\n          }\n          if (resSocket && !resDestroyed) {\n            resDestroyed = true;\n            resSocket.destroy();\n          }\n          if (proxyClient) {\n            proxyClient.destroy && proxyClient.destroy();\n            proxyClient = null;\n          }\n          if (respond) {\n            return;\n          }\n          respond = true;\n          if (!reqEmitter) {\n            return;\n          }\n          data.responseTime = data.endTime = Date.now();\n\n          if (!resData.ip) {\n            req.hostIp = resData.ip = LOCALHOST;\n          }\n\n          if (!err && !resData.statusCode) {\n            var code;\n            if (res) {\n              code = res.statusCode ? 'aborted' + ' (' + res.statusCode + ')' : null;\n              if (res.headers) {\n                resData.headers = res.headers;\n              }\n            }\n            emitAborted(data, reqEmitter, code);\n          } else {\n            data.resError = true;\n            resData.statusCode = resData.statusCode || 502;\n            resData.body = err ? util.getErrorStack(err) : 'Aborted';\n            util.emitError(reqEmitter, data);\n          }\n          resData.headers = { 'x-server': 'whistle' };\n          pluginMgr.postStats(req, resData);\n        }\n      });\n    });\n  });\n\n  return server;\n}\n\nmodule.exports = tunnelProxy;\n"
  },
  {
    "path": "lib/upgrade.js",
    "content": "var handleWebsocket = require('./https').handleWebsocket;\nvar hparser = require('hparser');\nvar net = require('net');\nvar pluginMgr = require('./plugins');\nvar util = require('./util');\nvar config = require('./config');\nvar common = require('./util/common');\n\nvar formatHeaders = hparser.formatHeaders;\nvar getRawHeaderNames = hparser.getRawHeaderNames;\nvar getRawHeaders = hparser.getRawHeaders;\nvar PLUGIN_PATH_RE =\n  /^\\/(\\.\\.\\.whistle-path\\.5b6af7b9884e1165\\.\\.\\.\\/\\/\\/)?(whistle|plugin)\\.([^/?#]+)\\/?/;\n\nfunction getPluginNameByReq(req, socket, callback) {\n  var host = req.headers.host;\n  var index = host.indexOf(':');\n  var port = req.isHttps ? 443 : 80;\n  if (index !== -1) {\n    port = host.substring(index + 1);\n    host = host.substring(0, index);\n  }\n  var isUIPort = port == config.port || port == config.uiport;\n  var isWebUI = config.isWebUIHost(host) ? (!net.isIP(host) || isUIPort) : (isUIPort && util.isLocalHost(host));\n  var pluginName;\n  var isPluginUrl;\n  var internalPath;\n  if (!config.pureProxy && PLUGIN_PATH_RE.test(req.url)) {\n    internalPath = RegExp['$&'];\n    req.isInternalUrl = !!RegExp.$1;\n    isPluginUrl = RegExp.$2 === 'plugin';\n    pluginName = RegExp.$3;\n  } else if (!isWebUI) {\n    pluginName =\n      (isUIPort || !net.isIP(host)) && config.getPluginNameByHost(host);\n  } else if (!req.url.indexOf('/service/')) {\n    req.isHttps = true;\n    req.headers[util.REAL_HOST_HEADER] = common.SERVICE_HOST;\n    req._isInternalReq = true;\n    socket.fullUrl = req.fullUrl = util.getFullUrl(req);\n    socket.isPluginReq = true;\n    socket._isPureInternalReq = true;\n    return callback();\n  }\n  if (!pluginName) {\n    return callback();\n  }\n  var plugin = isPluginUrl\n    ? pluginMgr.getPluginByName(pluginName)\n    : pluginMgr.getPlugin(pluginName + ':');\n  if (!isWebUI && !plugin) {\n    return callback();\n  }\n  if (internalPath) {\n    req.url = '/' + req.url.substring(internalPath.length);\n  }\n  pluginMgr.loadPlugin(plugin, function (err, ports) {\n    var uiPort = ports && ports.uiPort;\n    if (err || !uiPort) {\n      var msg = [\n        'HTTP/1.1',\n        err ? 500 : 404,\n        err ? 'Internal Server Error' : 'Not Found'\n      ].join(' ');\n      err = err ? '<pre>' + util.encodeHtml(err) + '</pre>' : 'Not Found';\n      var length = Buffer.byteLength(err);\n      err = [\n        msg,\n        'Content-Type: text/html; charset=utf8',\n        'Content-Length: ' + length,\n        '\\r\\n',\n        err\n      ].join('\\r\\n');\n    }\n    callback(err, uiPort);\n  });\n}\n\nfunction upgradeHandler(req, socket) {\n  util.removeSpecPath(req);\n  ++util.proc.allWsRequests;\n  ++util.proc.totalAllWsRequests;\n  var done, reqDestroyed, resDestroyed, resSocket;\n  function destroy(err) {\n    if (resSocket) {\n      if (!resDestroyed) {\n        resDestroyed = true;\n        resSocket.destroy(err);\n      }\n    } else if (!reqDestroyed) {\n      reqDestroyed = true;\n      socket.destroy(err);\n    }\n    if (socket.isLogRequests) {\n      --util.proc.wsRequests;\n    }\n    socket.isLogRequests = false;\n    if (!done) {\n      done = true;\n      --util.proc.allWsRequests;\n    }\n  }\n  var headers = req.headers;\n  util.onSocketEnd(socket, destroy);\n  util.addTunnelData(socket, headers);\n  socket._clientId = util.getComposerClientId(headers);\n  var getBuffer = function (method, newHeaders, path) {\n    var rawData = (method || 'GET') + ' ' + (path || socket.url || req.url) + ' ' + 'HTTP/1.1';\n    newHeaders = formatHeaders(newHeaders || headers, req.rawHeaders);\n    if (newHeaders = getRawHeaders(newHeaders)) {\n      rawData += '\\r\\n' + newHeaders;\n    }\n    return Buffer.from(rawData + '\\r\\n\\r\\n');\n  };\n  var sniPlugin = headers[util.SNI_PLUGIN_HEADER];\n  if (sniPlugin) {\n    socket.sniPlugin = req.sniPlugin = sniPlugin;\n    delete headers[util.SNI_PLUGIN_HEADER];\n  }\n  req.isPluginReq = socket.isPluginReq = util.checkPluginReqOnce(req);\n  util.handleForwardedProps(req);\n  var isHttps = socket.isHttps || req.isHttps || !!headers[config.HTTPS_FIELD];\n  req.isHttps = socket.isHttps = isHttps;\n  // format headers\n  req.isWs = true;\n  socket.fullUrl = util.getFullUrl(req);\n  socket._fwdHost = req._fwdHost;\n  socket.headers = headers;\n  getPluginNameByReq(req, socket, function (err, uiPort) {\n    if (err) {\n      return socket.write(err);\n    }\n    var clientInfo = util.parseClientInfo(req);\n    var clientIp = clientInfo[0] || util.getClientIp(req);\n    var clientPort = clientInfo[1] || util.getClientPort(req);\n    socket._disabledProxyRules = req._disabledProxyRules;\n    socket.clientIp = clientIp;\n    socket.clientPort = clientPort;\n    socket._remoteAddr = clientInfo[2] || util.getRemoteAddr(req);\n    socket._remotePort = clientInfo[3] || util.getRemotePort(req);\n    delete headers[config.CLIENT_PORT_HEADER];\n    if (uiPort) {\n      util.addClientInfo(socket);\n      headers[config.PLUGIN_HOOK_NAME_HEADER] = config.PLUGIN_HOOKS.UI;\n      socket.pause();\n      util.connect(\n        {\n          port: uiPort || config.port,\n          host: '127.0.0.1'\n        },\n        function (err, s) {\n          resSocket = s;\n          if (err || socket._hasError) {\n            return destroy(err);\n          }\n          resSocket.on('error', destroy);\n          resSocket.write(getBuffer(req.method));\n          socket.pipe(resSocket).pipe(socket);\n          socket.resume();\n        }\n      );\n      return;\n    }\n    // 不能放到上面，否则转发后的协议将丢失\n    delete headers[config.HTTPS_FIELD];\n    socket.rawHeaderNames = getRawHeaderNames(req.rawHeaders);\n    socket.url = req.url;\n    socket.fromTunnel = req.fromTunnel;\n    socket.fromComposer = req.fromComposer;\n    socket.enableXFF = req.enableXFF;\n    socket._isInternalReq = req._isInternalReq;\n    socket.isInternalUrl = req.isInternalUrl;\n    delete socket.headers[config.CLIENT_PORT_HEADER];\n    socket.getBuffer = function (newHeaders, path) {\n      return getBuffer(null, newHeaders, path);\n    };\n    handleWebsocket(socket, clientIp, clientPort);\n  });\n}\n\nmodule.exports = function (server) {\n  server.on('upgrade', upgradeHandler);\n};\n"
  },
  {
    "path": "lib/util/common.js",
    "content": "var os = require('os');\nvar path = require('path');\nvar fse = require('fs-extra2');\nvar fs = require('fs');\nvar iconv = require('iconv-lite');\nvar qs = require('querystring');\nvar zlib = require('zlib');\nvar net = require('net');\nvar http = require('http');\nvar crypto = require('crypto');\nvar json5 = require('json5');\nvar extend = require('extend');\nvar tls = require('tls');\nvar https = require('https');\nvar zlibx = require('./zlib');\nvar pkgConf = require('../../package.json');\nvar isUtf8 = require('./is-utf8');\nvar PassThrough = require('stream').PassThrough;\nvar parseUrl = require('./parse-url');\n\nvar ALGORITHM = 'aes-256-cbc';\nvar gzip = zlib.gzip;\nvar LOCALHOST = '127.0.0.1';\nvar CONN_TIMEOUT = 30000;\nvar LINE_END_RE = /\\r\\n|\\n|\\r/;\nvar HEAD_RE = /^head$/i;\nvar HOME_DIR_RE = /^[~～]\\//;\nvar REGISTRY_RE = /^--registry=https?:\\/\\/[^/?]/;\nvar UP_PATH_REGEXP = /(?:^|[\\\\/])\\.\\.(?:[\\\\/]|$)/;\nvar RSLASH_RE = /\\\\/g;\nvar DIR_RE = /^--dir=(.+)$/;\nvar UTF8_OPTIONS = { encoding: 'utf8' };\nvar GZIP_THRESHOLD = 512;\nvar ILLEGAL_TRAILERS = [\n  'host',\n  'transfer-encoding',\n  'content-length',\n  'cache-control',\n  'te',\n  'max-forwards',\n  'authorization',\n  'set-cookie',\n  'content-encoding',\n  'content-type',\n  'content-range',\n  'trailer',\n  'connection',\n  'upgrade',\n  'http2-settings',\n  'proxy-connection',\n  'transfer-encoding',\n  'keep-alive'\n];\nvar REMOTE_URL_RE = /^\\s*((?:git[+@]|github:)[^\\s]+\\/whistle\\.[a-z\\d_-]+(?:\\.git)?|https?:\\/\\/[^\\s]+)\\s*$/i;\nvar WHISTLE_PLUGIN_RE = /^((?:@[\\w.~-]+\\/)?whistle\\.[a-z\\d_-]+)(?:\\@([\\w.^~*-]*))?$/;\nvar TGZ_WHISTLE_PLUGIN_RE = /(?:^(?:file:)?|[\\\\/])(?:[\\w.~-]+-)?whistle\\.[a-z\\d_-]+-\\d+\\.\\d+\\.\\d+[\\w.-]*\\.(?:tgz|tar(?:\\.gz)?)$/;\nvar HTTP_RE = /^https?:\\/\\/[^/?]/;\nvar SEP_RE = /\\s*[|,;\\s]+\\s*/;\nvar RAW_CRLF_RE = /\\\\n|\\\\r/g;\nvar AUTH_RE = /^Basic /i;\nvar STREAM_OPTS = { highWaterMark: 1 };\nvar REAL_HOST_HEADER = 'x-whistle-real-host';\nvar supportsBr = zlib.createBrotliDecompress && zlib.createBrotliCompress;\nvar TIMEOUT_ERR = new Error('Timeout');\nvar noop = function () {};\nvar PATH_SEP_RE = /[\\\\/]/;\nvar CUR_PATH_RE = /^\\.[\\\\/]/;\nvar CLIENT_PORT_HEADER = 'x-whistle-client-port';\nvar ALLOW_ACK = 'x-whistle-allow-tunnel-ack';\n\nexports.ALLOW_ACK = ALLOW_ACK;\nexports.CONN_TIMEOUT = CONN_TIMEOUT;\nexports.TGZ_FILE_NAME_RE = /^(?:[\\w.~-]+-)?whistle\\.[a-z\\d_-]+-\\d+\\.\\d+\\.\\d+[\\w.-]*\\.(?:tgz|tar(?:\\.gz)?)$/;\nexports.parseUrl = parseUrl;\nexports.supportsBr = supportsBr;\nexports.SERVICE_HOST = 'admin.weso.pro';\nexports.REMOTE_URL_RE = REMOTE_URL_RE;\nexports.WHISTLE_PLUGIN_RE = WHISTLE_PLUGIN_RE;\nexports.TIMEOUT_ERR = TIMEOUT_ERR;\nexports.TEMP_PATH_RE = /^temp\\/([\\da-f]{64}|blank)(\\.[\\w.-]{1,20})?/;\nexports.REAL_HOST_HEADER = REAL_HOST_HEADER;\nexports.CLIENT_PORT_HEADER = CLIENT_PORT_HEADER;\nexports.ALPN_PROTOCOL_HEADER = 'x-whistle-alpn-protocol';\nexports.CLIENT_ID_HEADER = 'x-whistle-client-id';\nexports.CLIENT_IP_HEADER = 'x-forwarded-for';\nexports.HTTPS_FIELD = 'x-whistle-https-request';\nexports.ACK_HEADER = 'x-whistle-request-tunnel-ack';\nexports.ORIGIN_HOST_HEADER = 'x-whistle-origin-host';\nexports.AUTH_URL = 'x-auth-html-url';\nexports.AUTH_STATUS = 'x-auth-status';\nexports.formatPathSep = function (str) {\n  return str.replace(RSLASH_RE, '/');\n};\n\nfunction getTgzUrl(url, autoComplete) {\n  if (!TGZ_WHISTLE_PLUGIN_RE.test(url)) {\n    return;\n  }\n  if (HTTP_RE.test(url)) {\n    return url;\n  }\n  var index = url.indexOf(':');\n  if (index !== -1 && url.substring(0, index) === 'file') {\n    url = url.substring(index + 1);\n  }\n  if (autoComplete && (!PATH_SEP_RE.test(url) || CUR_PATH_RE.test(url))) {\n    url = path.join(process.cwd(), url);\n  }\n  return 'file:' + url;\n}\n\nfunction getPureUrl(url) {\n  var index = url.indexOf('?');\n  url = index === -1 ? url : url.substring(0, index);\n  index = url.indexOf('#');\n  return  index === -1 ? url : url.substring(0, index);\n}\n\nexports.getPureUrl = getPureUrl;\n\nexports.existsUpPath = function (str) {\n  return UP_PATH_REGEXP.test(str);\n};\n\nfunction readFileTextSync(file, safe, opts, retry) {\n  try {\n    return fs.readFileSync(file, opts);\n  } catch (e) {}\n  if (retry === true) {\n    return;\n  }\n  return safe ? readFileTextSync(file, opts, safe, true) :\n      fs.readFileSync(file, opts);\n}\n\nexports.readFileTextSync = function(file, safe) {\n  return readFileTextSync(file, safe !== false, UTF8_OPTIONS);\n};\n\nexports.readFileBufferSync = function(file, safe) {\n  return readFileTextSync(file, safe !== false);\n};\n\nexports.getPlugins = function(argv, isInstall, restArgv) {\n  var result = [];\n  argv.forEach(function(name, i) {\n    if (WHISTLE_PLUGIN_RE.test(name)) {\n      return result.push(name);\n    }\n    if (!isInstall || argv[i - 1] === '--registry') {\n      restArgv && restArgv.push(name);\n      return;\n    }\n    if (REMOTE_URL_RE.test(name)) {\n      return result.push(name);\n    }\n    var tgzUrl = getTgzUrl(name, restArgv);\n    if (tgzUrl) {\n      result.push(tgzUrl);\n    } else {\n      restArgv && restArgv.push(name);\n    }\n  });\n  return result;\n};\n\nexports.isPluginAddr = function(name) {\n  return REMOTE_URL_RE.test(name) || TGZ_WHISTLE_PLUGIN_RE.test(name);\n};\n\nexports.parsePlugins = function(data) {\n  var cmd = typeof data === 'string' ? data : (data && data.cmd);\n  if (!isString(cmd) || cmd.length > 2048) {\n    return;\n  }\n  var pkgs;\n  var registry;\n  var dir;\n  var isDir;\n  cmd.trim().split(SEP_RE).forEach(function(name) {\n    name = name.trim();\n    if (WHISTLE_PLUGIN_RE.test(name)) {\n      pkgs = pkgs || [];\n      pkgs.push({\n        name: RegExp.$1,\n        version: RegExp.$2\n      });\n    } else if (!registry && REGISTRY_RE.test(name)) {\n      registry = name.substring(11, 1035);\n    } else if (!dir) {\n      if (DIR_RE.test(name)) {\n        dir = RegExp.$1.trim();\n      } else if (isDir) {\n        dir = name;\n      } else if (name === '--dir') {\n        isDir = true;\n      }\n    }\n  });\n  return pkgs && {\n    registry: registry,\n    whistleDir: dir,\n    pkgs: pkgs\n  };\n};\n\nfunction readJson(filePath, callback) {\n  fse.readJson(filePath, function (err, json) {\n    if (!err || err.code === 'ENOENT') {\n      return callback(err, json);\n    }\n    fse.readJson(filePath, callback);\n  });\n}\n\nexports.readJson = readJson;\n\nvar PKG_NAME_RE = /[/\\\\]package\\.json$/;\n\nexports.getPeerPlugins = function (pkgs, root, cb) {\n  var peerPlugins = [];\n  var curPlugins = {};\n  var len = pkgs.length;\n  if (typeof pkgs === 'string') {\n    pkgs = [{ name: pkgs }];\n  } else if (!Array.isArray(pkgs)) {\n    pkgs = [pkgs];\n  }\n  if (typeof root === 'function') {\n    cb = root;\n    root = null;\n  }\n  pkgs.forEach(function(pkg) {\n    curPlugins[pkg.name] = 1;\n  });\n  var index = len;\n  for (var i = 0; i < len; i++) {\n    var pkg = pkgs[i];\n    var pkgFile = pkg.root || root;\n    if (!PKG_NAME_RE.test(pkgFile)) {\n      pkgFile = path.join(pkgFile, 'node_modules', pkgs[i].name, 'package.json');\n    }\n    readJson(pkgFile, function (err, pkg) {\n      pkg = !err && pkg && pkg.whistleConfig;\n      if (pkg) {\n        var list = pkg.peerPluginList || pkg.peerPlugins;\n        if (Array.isArray(list) && list.length < 16) {\n          list.forEach(function (pkgName) {\n            pkgName = typeof pkgName === 'string' ? pkgName.trim() : null;\n            if (WHISTLE_PLUGIN_RE.test(pkgName)) {\n              pkgName = RegExp.$1;\n              var version = RegExp.$2;\n              if (!curPlugins[pkgName]) {\n                curPlugins[pkgName] = 1;\n                peerPlugins.push({\n                  name: pkgName,\n                  version\n                });\n              }\n            }\n          });\n        }\n      }\n      if (--index <= 0) {\n        cb(peerPlugins);\n      }\n    });\n  }\n};\n\nfunction isHead(req) {\n  return HEAD_RE.test(req.method);\n}\n\nexports.isHead = isHead;\n\n\nexports.getUpdateUrl = function(conf) {\n  var url = conf.updateUrl;\n  if (url && REMOTE_URL_RE.test(url) && url.length < 2100) {\n    return url;\n  }\n};\n\nexports.noop = noop;\n\nfunction removeIPV6Prefix(ip) {\n  if (!isString(ip)) {\n    return '';\n  }\n  return ip.indexOf('::ffff:') === 0 ? ip.substring(7) : ip;\n}\n\nexports.removeIPV6Prefix = removeIPV6Prefix;\n\nfunction hasBody(res, req) {\n  if (req && isHead(req)) {\n    return false;\n  }\n  var statusCode = res.statusCode;\n  return !(\n    statusCode == 204 ||\n    (statusCode >= 300 && statusCode < 400) ||\n    (100 <= statusCode && statusCode <= 199)\n  );\n}\n\nexports.hasBody = hasBody;\n\nfunction isEmptyObject(a) {\n  return !a || !Object.keys(a).length;\n}\n\nexports.isEmptyObject = isEmptyObject;\n\nfunction lowerCaseify(obj, rawNames) {\n  var result = {};\n  if (!obj) {\n    return result;\n  }\n  Object.keys(obj).forEach(function (name) {\n    var value = obj[name];\n    if (value !== undefined) {\n      var key = name.toLowerCase();\n      result[key] = Array.isArray(value) ? value : value + '';\n      if (rawNames) {\n        rawNames[key] = name;\n      }\n    }\n  });\n  return result;\n}\n\nexports.lowerCaseify = lowerCaseify;\n\nexports.removeIllegalTrailers = function (headers) {\n  ILLEGAL_TRAILERS.forEach(function (key) {\n    delete headers[key];\n  });\n};\n\nexports.addTrailerNames = function (\n  res,\n  newTrailers,\n  rawNames,\n  delTrailers,\n  req\n) {\n  if (!hasBody(res, req) || isEmptyObject(newTrailers)) {\n    return;\n  }\n  var headers = res.headers;\n  delete headers['content-length'];\n  delete headers['transfer-encoding'];\n  var nameMap = {};\n  var curTrailers = headers.trailer;\n  if (curTrailers) {\n    if (typeof curTrailers === 'string') {\n      nameMap[curTrailers.toLowerCase()] = curTrailers;\n    } else if (Array.isArray(curTrailers)) {\n      curTrailers.forEach(function (key) {\n        if (isString(key)) {\n          nameMap[key.toLowerCase()] = key;\n        }\n      });\n    }\n  }\n  Object.keys(newTrailers).forEach(function (key) {\n    var lkey = key.toLowerCase();\n    if (\n      (!delTrailers || !delTrailers[lkey]) &&\n      ILLEGAL_TRAILERS.indexOf(lkey) === -1\n    ) {\n      nameMap[lkey] = key;\n    }\n  });\n  if (rawNames && !rawNames.trailer) {\n    rawNames.trailer = 'Trailer';\n  }\n  headers.trailer = Object.keys(nameMap).map(function (key) {\n    return nameMap[key];\n  });\n};\n\nexports.onResEnd = function (res, callback) {\n  var state = res._readableState || '';\n  if (state.endEmitted) {\n    return callback();\n  }\n  res.on('end', callback);\n};\n\nvar UPGRADE_RE = /^\\s*upgrade\\s*$/i;\nvar WS_RE = /^\\s*websocket\\s*$/i;\nvar CONNECT_RE = /^\\s*CONNECT\\s*$/i;\nvar CONNECT_PROTOS = 'connect:,socket:,tunnel:,conn:,tls:,tcp:'.split(',');\n\nexports.isUpgrade = function(options, headers) {\n  var p = options.protocol;\n  if (p === 'ws:' || p === 'wss:' || options.method === 'UPGRADE') {\n    return true;\n  }\n  return headers && UPGRADE_RE.test(headers.connection);\n};\n\nexports.isWebSocket = function(headers) {\n  return headers && WS_RE.test(headers.upgrade);\n};\n\nexports.isConnect = function(options) {\n  return (\n    CONNECT_RE.test(options.method) ||\n    CONNECT_PROTOS.indexOf(options.protocol) !== -1\n  );\n};\n\nvar NO_PROTO_RE = /[^a-zA-Z0-9.-]/;\n\nfunction hasProtocol(url) {\n  var index = isString(url) ? url.indexOf('://') : -1;\n  if (index <= 0) {\n    return false;\n  }\n  return !NO_PROTO_RE.test(url.substring(0, index));\n}\n\nfunction setProtocol(url, isHttps) {\n  return hasProtocol(url) ? url : (isHttps ? 'https://' : 'http://') + url;\n}\n\nfunction getProtocol(url) {\n  return hasProtocol(url) ? url.substring(0, url.indexOf('://') + 1) : null;\n}\n\nfunction removeProtocol(url, clear) {\n  return hasProtocol(url)\n    ? url.substring(url.indexOf('://') + (clear ? 3 : 1))\n    : url;\n}\n\nfunction replaceProtocol(url, protocol) {\n  return (protocol || 'http:') + removeProtocol(url);\n}\n\nfunction formatUrl(pattern) {\n  var queryString = '';\n  var queryIndex = pattern.indexOf('?');\n  if (queryIndex != -1) {\n    queryString = pattern.substring(queryIndex);\n    pattern = pattern.substring(0, queryIndex);\n  }\n  var index = pattern.indexOf('://');\n  index = pattern.indexOf('/', index == -1 ? 0 : index + 3);\n  return (index == -1 ? pattern + '/' : pattern) + queryString;\n}\n\nexports.hasProtocol = hasProtocol;\nexports.setProtocol = setProtocol;\nexports.getProtocol = getProtocol;\nexports.removeProtocol = removeProtocol;\nexports.replaceProtocol = replaceProtocol;\nexports.formatUrl = formatUrl;\n\nvar QUERY_RE = /\\/[^/]*(?:\\?.*)?$/;\nexports.getAbsUrl = function(url, fullUrl) {\n  if (HTTP_RE.test(url)) {\n    return formatUrl(url);\n  }\n  if (url[0] === '/') {\n    var index = fullUrl.indexOf('/', 8);\n    url = fullUrl.substring(0, index) + url;\n    return formatUrl(url);\n  }\n  url = fullUrl.replace(QUERY_RE, '') + '/' + url;\n  return formatUrl(url);\n};\n\nfunction getHomedir() {\n  //默认设置为`~`，防止Linux在开机启动时Node无法获取homedir\n  return (\n    (typeof os.homedir == 'function'\n      ? os.homedir()\n      : process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME']) ||\n    '~'\n  );\n}\n\nexports.getHomedir = getHomedir;\n\nfunction getHomePath(dir) {\n  if (!dir || !HOME_DIR_RE.test(dir)) {\n    return dir;\n  }\n  return path.join(getHomedir(), '.' + dir.substring(1));\n}\n\nexports.getHomePath = getHomePath;\n\nfunction getDefaultWhistlePath() {\n  return path.join(getHomedir(), '.WhistleAppData');\n}\n\nfunction getWhistlePath() {\n  return getHomePath(process.env.WHISTLE_PATH) ||  getDefaultWhistlePath();\n}\n\nexports.getDefaultWhistlePath = getDefaultWhistlePath;\nexports.getWhistlePath = getWhistlePath;\n\nfunction getLogFile(name) {\n  var whistlePath = getWhistlePath();\n  fse.ensureDirSync(whistlePath);\n  return path.join(whistlePath, pkgConf.name + (name ? '-' + name : '') + '.log');\n}\n\nexports.getLogFile = getLogFile;\n\nfunction padLeft(n, len) {\n  n = n + '';\n  var rest = (len || 2) - n.length;\n  return rest <= 0 ? n : '0'.repeat(rest) + n;\n}\n\nexports.padLeft = padLeft;\n\nexports.getMonth = function(time) {\n  var date = new Date(time || Date.now());\n  return date.getFullYear() + padLeft(date.getMonth() + 1);\n};\n\nfunction getDate() {\n  var date = new Date();\n  return date.getFullYear() + padLeft(date.getMonth() + 1) + padLeft(date.getDate());\n}\n\nexports.writeLogSync = function(msg) {\n  try {\n    fs.writeFileSync(getLogFile(), msg, { flag: 'a' });\n  } catch (e) {\n    msg += '\\r\\n' +  e.stack;\n    try {\n      fs.writeFileSync(getLogFile('error'), msg, { flag: 'a' });\n    } catch (e) {\n      try {\n        fs.writeFileSync(getLogFile(getDate()), msg + '\\r\\n' +  e.stack, { flag: 'a' });\n      } catch (e) {}\n    }\n  }\n};\n\nvar SPEC_TAGS = {\n  '&': '&amp;',\n  '<': '&lt;',\n  '>': '&gt;',\n  '\"': '&quot;',\n  '\\'': '&#39;',\n  '`': '&#96;'\n};\nvar SPEC_TAG_RE = /[&<>\"'`]/g;\n\nfunction transformTag(tag) {\n  return SPEC_TAGS[tag] || tag;\n}\n\nexports.encodeHtml = function(str) {\n  return isString(str) ? str.replace(SPEC_TAG_RE, transformTag) : str;\n};\n\nvar SEP_RE_G = /^[^\\n\\r\\S]*```+\\s*$/mg;\n\nexports.wrapRuleValue = function(key, value, size, policy) {\n  if (!key || value == null) {\n    return '';\n  }\n  if (!isString(value)) {\n    return '\\n``` ' + key + '\\n\\n```\\n';\n  }\n  var isOverSize = size > 0 && value.length > size;\n  if (isOverSize) {\n    if (policy === 'ignore') {\n      return '';\n    }\n    if (policy === 'empty') {\n      return '\\n``` ' + key + '\\n\\n```\\n';\n    }\n    value = value.substring(0, size);\n  }\n  var list = value.match(SEP_RE_G);\n  if (!list) {\n    return  '\\n``` ' + key + '\\n' + value + '\\n```\\n';\n  }\n  list = list.map(function(item) {\n    return item.trim().length;\n  });\n  var count = Math.max.apply(null, list) + 1;\n  var sep = typeof '`'.repeat === 'function' ? '`'.repeat(count) : '```````````';\n  return  '\\n' + sep + ' ' + key + '\\n' + value + '\\n' + sep + '\\n';\n};\n\nexports.isGroup = function(name) {\n  return name && name[0] === '\\r';\n};\n\nvar UTF8_RE = /^utf-?8$/i;\nfunction bufferToString(buffer, encoding) {\n  if (typeof encoding === 'string') {\n    encoding = encoding.trim();\n  } else {\n    encoding = null;\n  }\n  if (Buffer.isBuffer(buffer) && !UTF8_RE.test(encoding) && !isUtf8(buffer)) {\n    try {\n      buffer = iconv.encode(buffer, encoding || 'GB18030');\n    } catch (e) {}\n  }\n  return String(buffer || '');\n}\n\nexports.bufferToString = bufferToString;\n\nvar URLENCODED_RE = /application\\/x-www-form-urlencoded/i;\nvar POST_RE = /^post$/i;\n\nfunction isUrlEncoded(req) {\n  var type = req.method && req.headers && req.headers['content-type'];\n  return type && POST_RE.test(req.method) && URLENCODED_RE.test(type);\n}\n\nexports.isUrlEncoded = isUrlEncoded;\n\nfunction readStream(stream, callback) {\n  var buffer = null;\n  stream.on('data', function(chunk) {\n    buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;\n  });\n  stream.once('end', function() {\n    var buf;\n    var text;\n    var json;\n    var err;\n    var jsonErr;\n    stream.zlib = zlibx;\n    var encoding = stream.headers && stream.headers['content-encoding'];\n    var getBuffer = function(cb) {\n      if (err || buf !== undefined) {\n        return cb(err, buf);\n      }\n      zlibx.unzip(encoding, buffer, function(e, ctn) {\n        err = e;\n        buf = ctn || null;\n        cb(err, buf);\n      });\n    };\n    var getText = function(cb, ecd) {\n      if (err || text != null) {\n        return cb(err, text);\n      }\n      getBuffer(function(e, ctn) {\n        err = e;\n        text = bufferToString(ctn, ecd);\n        cb(err, text);\n      });\n    };\n    stream.getBuffer = getBuffer;\n    stream.getText = getText;\n    stream.getJson = function(cb, ecd) {\n      getText(function() {\n        if (err || jsonErr || !text || json !== undefined) {\n          return cb(err || jsonErr, json);\n        }\n        try {\n          if (isUrlEncoded(stream)) {\n            json = qs.parse(text);\n          } else {\n            text = text.trim();\n            if (text[0] === '[' || text[0] === '{') {\n              json = JSON.parse(text) || null;\n            } else {\n              json = null;\n            }\n          }\n        } catch (e) {\n          jsonErr = e;\n        }\n        cb(jsonErr, json);\n      }, ecd);\n    };\n    stream._readRawBuffer_ = buffer;\n    callback(buffer);\n  });\n}\n\nexports.readStream = readStream;\n\nexports.wrapReadStream = function(stream) {\n  var callbacks = [];\n  var rawBuffer;\n  var getRawBuffer = function(callback) {\n    if (rawBuffer !== undefined || !callbacks) {\n      return callback(rawBuffer);\n    }\n    callbacks.push(callback);\n    callbacks.length === 1 && readStream(stream, function(buffer) {\n      rawBuffer = buffer;\n      callbacks.forEach(function(cb) {\n        cb(buffer);\n      });\n      callbacks = null;\n    });\n  };\n  stream.zlib = zlibx;\n  stream.getBuffer = function(callback) {\n    getRawBuffer(function() {\n      stream.getBuffer(callback);\n    });\n  };\n  stream.getText = function(callback) {\n    getRawBuffer(function() {\n      stream.getText(callback);\n    });\n  };\n  stream.getJson = function(callback) {\n    getRawBuffer(function() {\n      stream.getJson(callback);\n    });\n  };\n  stream.getRawBuffer = getRawBuffer;\n  return stream;\n};\n\nexports.readJsonSync = function(filepath) {\n  try {\n    return fse.readJsonSync(filepath);\n  } catch (e) {\n    if (e.code === 'ENOENT') {\n      return;\n    }\n  }\n  try {\n    return fse.readJsonSync(filepath);\n  } catch (e) {}\n};\n\nfunction isUrl(url) {\n  return HTTP_RE.test(url);\n}\n\nexports.isUrl = isUrl;\n\nexports.getRegistry = function(registry) {\n  if (!registry || !isUrl(registry) || registry.length > 1024) {\n    return;\n  }\n  return registry;\n};\n\nfunction copyFile(src, dest, callback, retry) {\n  var execCb = function (e) {\n    if (e && !retry) {\n      copyFile(src, dest, callback, true);\n    } else {\n      callback(e);\n    }\n  };\n  if (typeof fs.copyFile === 'function') {\n    fs.copyFile(src, dest, execCb);\n  } else {\n    fse.copy(src, dest, execCb);\n  }\n}\n\nexports.copyFile = copyFile;\n\nexports.getHostPort = function(str) {\n  if (!str || (typeof str !== 'string' && typeof str !== 'number')) {\n    return;\n  }\n  if (/^(?:([\\w.-]+):)?([1-9]\\d{0,4})$/.test(str) || /^\\[([\\w.:]+)\\]:([1-9]\\d{0,4})$/.test(str)) {\n    return {\n      host: RegExp.$1,\n      port: parseInt(RegExp.$2, 10)\n    };\n  }\n};\n\nexports.onSocketEnd = function(socket, callback) {\n  var execCallback = function (err) {\n    socket._hasError = true;\n    if (callback) {\n      callback(err);\n      callback = null;\n    }\n  };\n  if (socket.aborted || socket.destroyed || socket._hasError) {\n    return execCallback();\n  }\n  socket.on('error', execCallback);\n  socket.once('close', execCallback);\n  socket.once('end', execCallback);\n  socket.once('timeout', execCallback);\n};\n\nexports.parseAuth = function(auth) {\n  var result = {};\n  if (!auth) {\n    return result;\n  }\n  auth = AUTH_RE.test(auth) ? auth.substring(6).trim() : auth;\n  result.auth = auth;\n  auth = Buffer.from(auth, 'base64').toString();\n  var index = auth.indexOf(':');\n  if (index === -1) {\n    result.name = auth;\n    result.pass = '';\n  } else {\n    result.name = auth.substring(0, index);\n    result.pass = auth.substring(index + 1);\n  }\n  return result;\n};\n\nexports.formatHost = parseUrl.formatHost;\n\nexports.createTransform = function() {\n  return new PassThrough(STREAM_OPTS);\n};\n\nfunction trimUrl(url) {\n  if (!url) {\n    return url;\n  }\n  var index = url.indexOf('://');\n  if (index === -1) {\n    return url.trim();\n  }\n  index += 3;\n  var protocol = url.substring(0, index);\n  return protocol + url.substring(index).trim();\n}\n\nexports.trimUrl = trimUrl;\n\nfunction getRuleValue(rule) {\n  return rule.value || rule.path;\n}\n\nexports.getRuleValue = getRuleValue;\n\nfunction getMatcher(rule) {\n  return trimUrl(rule && (getRuleValue(rule) || rule.matcher));\n}\n\nexports.getMatcher = getMatcher;\n\nfunction getMatcherValue(rule) {\n  rule = getMatcher(rule);\n  return rule && removeProtocol(rule, true);\n}\n\nexports.getMatcherValue = getMatcherValue;\n\nvar SPEC_CHAR_RE = /[|\\\\{}()[\\]^$+?.]/g;\nvar SPEC_STAR_RE = /[|\\\\{}()[\\]^$+?*.]/g;\n\nexports.escapeRegExp = function (str, withStar) {\n  if (!str) {\n    return '';\n  }\n  return str.replace(withStar ? SPEC_STAR_RE : SPEC_CHAR_RE, '\\\\$&');\n};\n\nfunction joinIpPort(ip, port) {\n  if (!port) {\n    return ip;\n  }\n  if (net.isIPv6(ip)) {\n    ip = '[' + ip + ']';\n  }\n  return ip + ':' + port;\n}\n\nexports.joinIpPort = joinIpPort;\n\nvar TUNNEL_HOST_RE = /^[^:\\/]+\\.[^:\\/]+:\\d+$/;\nvar TUNNEL_IPV6_HOST_RE = /^\\[[^\\/]+\\]:\\d+$/;\n\nexports.isTunnelHost = function(host) {\n  return host && (TUNNEL_HOST_RE.test(host) || TUNNEL_IPV6_HOST_RE.test(host));\n};\n\nvar MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER + '';\nvar MIN_SAFE_INTEGER = Math.abs(Number.MIN_SAFE_INTEGER) + '';\nvar DIG_RE = /^([+-]?)([1-9]\\d{0,15})$/;\nvar NUM_RE = /^(?:0|[1-9]\\d*)$/;\nvar ARR_RE = /\\[(0|[1-8]\\d{0,15}|9\\d{0,14})\\]$/;\nvar DOT_RE = /(\\\\+)\\./g;\nvar CR_RE = /\\r/g;\n\nfunction isString(str) {\n  return str && typeof str === 'string';\n}\n\nexports.isString = isString;\n\nfunction isSafeNumStr(str) {\n  if (str == '0') {\n    return true;\n  }\n  if (!DIG_RE.test(str)) {\n    return false;\n  }\n  var sign = RegExp.$1 === '-';\n  var num = RegExp.$2;\n  if (num.length < 16) {\n    return true;\n  }\n  return num <= (sign ? MIN_SAFE_INTEGER : MAX_SAFE_INTEGER);\n}\n\nexports.isSafeNumStr = isSafeNumStr;\n\nfunction deleteProp(obj, key) {\n  try {\n    if (!Array.isArray(obj)) {\n      delete obj[key];\n      return;\n    }\n    if (NUM_RE.test(key)) {\n      obj.splice(key, 1);\n    }\n  } catch (e) {}\n}\n\nexports.deleteProp = deleteProp;\n\n// \"key\"\n// [n]\n// [key]\n// k.[n].\"k[n]\".k[n]\nfunction parseKey(key) {\n  if (!key) {\n    return key;\n  }\n  var first = key[0];\n  var len = key.length - 1;\n  var last = key[len];\n  if (first === '\"' && last === '\"') {\n    return key.substring(1, len);\n  }\n  var result = ARR_RE.exec(key);\n  if (!result) {\n    return key;\n  }\n  var list = [];\n  while(result) {\n    key = key.slice(0, -result[0].length);\n    list.unshift(+result[1]);\n    result = ARR_RE.exec(key);\n  }\n  if (key) {\n    list.unshift(key);\n  }\n  return list;\n}\n\nfunction replaceDot(_, slash) {\n  var len = slash.length;\n  slash = slash.substring(0, Math.floor(len / 2));\n  return slash + (len % 2 ? '\\r' : '.');\n}\n\nfunction parseKeys(key) {\n  if (!isString(key)) {\n    return key;\n  }\n  key = key.trim();\n  if (key.indexOf('.') === -1) {\n    return parseKey(key);\n  }\n  key = key.replace(DOT_RE, replaceDot);\n  if (key.indexOf('.') === -1) {\n    return parseKey(key.replace(CR_RE, '.'));\n  }\n  var result = [];\n  key.split('.').forEach(function(k) {\n    k = parseKey(k.trim().replace(CR_RE, '.'));\n    if (Array.isArray(k)) {\n      result = result.concat(k);\n    } else {\n      result.push(k);\n    }\n  });\n  return result;\n}\n\nexports.deleteProps = function(obj, key) {\n  key = parseKeys(key);\n  if (!Array.isArray(key)) {\n    return deleteProp(obj, key);\n  }\n  var len = key.length;\n  if (!len) {\n    return;\n  }\n  if (len === 1) {\n    return deleteProp(obj, key[0]);\n  }\n  --len;\n  for (var i = 0; i <= len; i++) {\n    if (!obj) {\n      return;\n    }\n    var k = key[i];\n    if (i === len) {\n      return deleteProp(obj, k);\n    }\n    obj = obj[k];\n  }\n};\n\nfunction replaceCrLf(char) {\n  return char === '\\\\r' ? '\\r' : '\\n';\n}\n\nfunction parseLine(line) {\n  var index = line.indexOf(': ');\n  if (index === -1) {\n    index = line.indexOf(':');\n    if (index === -1) {\n      index = line.indexOf('=');\n    }\n  }\n  var name, value;\n  if (index != -1) {\n    name = line.substring(0, index).trim();\n    value = line.substring(index + 1).trim();\n    if (value) {\n      var fv = value[0];\n      var lv = value[value.length - 1];\n      if (fv === lv) {\n        if (fv === '\"' || fv === '\\'' || fv === '`') {\n          value = value.slice(1, -1);\n          if (value && fv === '`') {\n            value = value.replace(RAW_CRLF_RE, replaceCrLf);\n          }\n        }\n      } else if (isSafeNumStr(value)) {\n        value = parseInt(value, 10);\n      }\n    }\n  } else {\n    name = line.trim();\n    value = '';\n  }\n  return {\n    name: name,\n    value: value\n  };\n}\n\nfunction isNum(n) {\n  return typeof n === 'number';\n}\n\nexports.parsePlainText = function (text, resolveKeys) {\n  var result;\n  text.split(LINE_END_RE).forEach(function(line) {\n    if (!(line = line.trim())) {\n      return;\n    }\n    var obj = parseLine(line);\n    var name = obj.name;\n    if (resolveKeys) {\n      name = parseKeys(obj.name);\n    }\n    var value = obj.value;\n    var isKey = !Array.isArray(name);\n    if (isKey || name.length <= 1) {\n      if (isKey) {\n        result = result || {};\n      } else {\n        name = name[0];\n        result = result || [];\n      }\n      result[name] = value;\n      return;\n    }\n    result = result || (isNum(name[0]) ? [] : {});\n    obj = result;\n    var lastIndex = name.length - 1;\n    name.forEach(function(key, i) {\n      if (i === lastIndex) {\n        obj[key] = value;\n        return;\n      }\n      var next = obj[key];\n      if (!next || typeof next !== 'object') {\n        next = isNum(name[i + 1]) ? [] : {};\n      }\n      obj[key] = next;\n      obj = next;\n    });\n  });\n  return result || {};\n};\n\nexports.isWhistleName = function(name) {\n  return /^[\\w.-]{1,30}$/.test(name);\n};\n\nvar HTTP_PORT_RE = /:80$/;\nvar HTTPS_PORT_RE = /:443$/;\n\nfunction removeDefaultPort(host, isHttps) {\n  return host && host.replace(isHttps ? HTTPS_PORT_RE : HTTP_PORT_RE, '');\n}\n\nfunction getFullUrl(req) {\n  var headers = req.headers;\n  var host = headers[REAL_HOST_HEADER];\n  if (hasProtocol(req.url)) {\n    var options = parseUrl(req.url);\n    if (\n      options.protocol === 'https:' ||\n      (req.isWs && options.protocol === 'wss:')\n    ) {\n      req.isHttps = true;\n    }\n    req.url = options.path;\n    if (options.host) {\n      headers.host = options.host;\n    }\n  } else {\n    req.url = req.url || '/';\n    if (req.url[0] !== '/') {\n      req.url = '/' + req.url;\n    }\n  }\n  if (host) {\n    delete headers[REAL_HOST_HEADER];\n  }\n  if (!isString(host)) {\n    host = headers.host;\n    if (typeof host !== 'string') {\n      host = headers.host = '';\n    }\n  } else if (headers.host !== host) {\n    if (isString(headers.host)) {\n      req._fwdHost = headers.host;\n    }\n    headers.host = host;\n  }\n  host = removeDefaultPort(host, req.isHttps);\n  return (req.isWs ? 'ws' : 'http') + (req.isHttps ? 's' : '') + '://' + host + req.url;\n}\n\nexports.getFullUrl = getFullUrl;\n\nfunction getReqOptions(req, port, host) {\n  var options = parseUrl(getFullUrl(req));\n  options.headers = req.headers;\n  options.method = req.method;\n  options.agent = false;\n  options.protocol = null;\n  options.host = host || LOCALHOST;\n  if (port > 0) {\n    options.port = port;\n  }\n  options.hostname = null;\n  return options;\n}\n\nexports.getReqOptions = getReqOptions;\n\nexports.forwardRequest = function(req, res, port, host) {\n  var options = getReqOptions(req, port, host);\n  var destroyed;\n  var abort = function () {\n    if (!destroyed) {\n      destroyed = true;\n      client.destroy();\n    }\n  };\n  var client = http.request(options, function (_res) {\n    res.writeHead(_res.statusCode, _res.headers);\n    _res.pipe(res);\n  });\n  req.on('error', abort);\n  res.on('error', abort);\n  res.once('close', abort);\n  client.on('error', function (err) {\n    abort();\n    res.emit('error', err);\n  });\n  req.pipe(client);\n  return client;\n};\n\nexports.setInternalOptions = function (options, config, isInternal) {\n  if ((options.url || options.uri) && !options.pluginName) {\n    options.headers = options.headers || {};\n    options.headers[config.PROXY_ID_HEADER] = isInternal ? 'internal' : 'internalx';\n    options.whistleConfig = {\n      host: config.host || LOCALHOST,\n      port: config.port\n    };\n  }\n  return options;\n};\n\nexports.createHash = function (str) {\n  var shasum = crypto.createHash('sha1');\n  shasum.update(str || '');\n  return shasum.digest('hex');\n};\n\nvar VER_LEN = 3;\n\nfunction compareVer(n1, n2, index) {\n  n1 = parseInt(n1, 10) || 0;\n  n2 = parseInt(n2, 10) || 0;\n  if (n1 === n2) {\n    return 0;\n  }\n  return n1 > n2 ? VER_LEN - index : index - VER_LEN;\n}\n\nexports.compareVersion = function(v1, v2) {\n  if (v1 === v2 || !v1 || typeof v1 !== 'string') {\n    return 0;\n  }\n  if (!v2 || typeof v2 !== 'string') {\n    return 3;\n  }\n  v1 = v1.split('.');\n  v2 = v2.split('.');\n\n  for (var i = 0; i < 3; i++) {\n    var flag = compareVer(v1[i], v2[i], i);\n    if (flag) {\n      return Math.max(flag, 0);\n    }\n  }\n  v1 = v1[2];\n  v2 = v2[2];\n  if (!v1 || !v2) {\n    return 0;\n  }\n  var i1 = v1.indexOf('-');\n  var i2 = v2.indexOf('-');\n  var test1 = i1 === -1 ? '' : v1.substring(i1);\n  var test2 = i2 === -1 ? '' : v2.substring(i2);\n  if (test1 === test2) {\n    return 0;\n  }\n  if (!test1 || !test2) {\n    return test1 ? 0 : 1;\n  }\n  return test1 > test2 ? 1 : 0;\n};\n\nexports.upperFirst = function(str) {\n  return isString(str) ? str[0].toUpperCase() + str.substring(1) : '';\n};\n\nexports.readWhistleRc = function(options) {\n  options = options || {};\n  if (options.rcPath === 'none') {\n    return;\n  }\n  var rcPath = options.rcPath || path.join(getHomedir(), '.whistlerc');\n  var rcText = (readFileTextSync(rcPath, true, UTF8_OPTIONS) || '').trim();\n  if (!rcText) {\n    return;\n  }\n  var storage = options.storage ? options.storage + '.' : '';\n  var wildcard = '*.';\n  var result;\n  rcText.split(LINE_END_RE).forEach(function(line) {\n    if (!(line = line.trim()) || line[0] === '#') {\n      return;\n    }\n    var index = line.indexOf(': ');\n    if (index === -1) {\n      index = line.indexOf('=');\n    }\n    if (index != -1) {\n      var name = line.substring(0, index).trim();\n      var value = name && line.substring(index + 1).trim();\n      if (value) {\n        if (name.indexOf(storage) !== 0) {\n          if (name.indexOf(wildcard) !== 0) {\n            return;\n          }\n          name = name.substring(wildcard.length);\n        } else {\n          name = name.substring(storage.length);\n        }\n        if (value[0] === '\"' && value[value.length - 1] === '\"') {\n          value = value.slice(1, -1);\n        }\n        result = result || {};\n        result[name] = value;\n      }\n    }\n  });\n  return result;\n};\n\nvar GZIP_RE = /\\bgzip\\b/i;\nvar canGzip = function (req) {\n  return GZIP_RE.test(req.headers['accept-encoding']);\n};\n\nfunction sendGzipData(res, headers, buffer) {\n  if (headers) {\n    headers['Content-Encoding'] = 'gzip';\n    headers['Content-Length'] = buffer.length;\n    res.writeHead(200, headers);\n    res.end(buffer);\n  } else {\n    res.setHeader('Content-Encoding', 'gzip');\n    res.setHeader('Content-Length', buffer.length);\n    res.send(buffer);\n  }\n}\n\nfunction sendRes(res, status, text) {\n  res.status(status);\n  if (text && text[0] === '<') {\n    res.send(text);\n  } else {\n    res.type('text').end(text);\n  }\n}\n\nexports.sendRes = sendRes;\n\nexports.sendGzip = function (req, res, data) {\n  if (!canGzip(req)) {\n    return res.json(data);\n  }\n  var str = JSON.stringify(data);\n  if (str.length < GZIP_THRESHOLD) {\n    return res.json(data);\n  }\n  gzip(str, function(err, result) {\n    if (err) {\n      try {\n        res.json(data);\n      } catch (e) {\n        sendRes(res, 500, 'Internal Server Error');\n      }\n      return;\n    }\n    sendGzipData(res, {\n      'Content-Type': 'application/json; charset=utf-8'\n    }, result);\n  });\n};\n\nexports.sendGzipText = function(req, res, headers, text, gzipText) {\n  if (!text || !canGzip(req) || (!gzipText && text.length < GZIP_THRESHOLD)) {\n    headers && res.writeHead(200, headers);\n    return headers ? res.end(text) : res.send(text);\n  }\n  if (gzipText) {\n    return sendGzipData(res, headers, gzipText);\n  }\n  gzip(text, function(err, result) {\n    if (err) {\n      headers && res.writeHead(200, headers);\n      return headers ? res.end(text) : res.send(text);\n    }\n    sendGzipData(res, headers, result);\n  });\n};\n\nexports.checkHistory = function(data) {\n  if (\n    typeof data.url === 'string' &&\n    typeof data.method === 'string' &&\n    typeof data.headers === 'string'\n  ) {\n    data.url = data.url.trim();\n    data.headers = data.headers.trim();\n    data.method = data.method.trim();\n    if (!data.body) {\n      data.body = '';\n      return true;\n    }\n    return typeof data.body === 'string';\n  }\n};\n\nvar TLSV2_CIPHERS = ['DHE-RSA-AES256-GCM-SHA384',\n                      'DHE-RSA-AES128-GCM-SHA256',\n                      'AES256-GCM-SHA384',\n                      'ECDHE-ECDSA-AES256-GCM-SHA384'].join(':');\nvar TLS_OPTIONS = { ciphers: TLSV2_CIPHERS };\n\nfunction getTlsOptions(rules) {\n  var cipher = rules && rules.cipher;\n  var opts = cipher && cipher.__tlsOptions;\n  opts = opts && opts._opts;\n  if (!opts) {\n    return TLS_OPTIONS;\n  }\n  if (!opts.ciphers) {\n    opts.ciphers = TLSV2_CIPHERS;\n  }\n  return opts;\n}\n\nexports.getTlsOptions = getTlsOptions;\n\nfunction toLowerCase(str) {\n  return typeof str == 'string' ? str.trim().toLowerCase() : str;\n}\n\nexports.toLowerCase = toLowerCase;\n\nfunction getContentEncoding(headers) {\n  var encoding = toLowerCase(\n    (headers && headers['content-encoding']) || headers\n  );\n  return encoding === 'gzip' || (supportsBr && encoding === 'br') ||\n    encoding === 'deflate' ? encoding : null;\n}\n\nexports.getContentEncoding = getContentEncoding;\n\n\nfunction getUnzipStream(headers) {\n  switch (getContentEncoding(headers)) {\n  case 'gzip':\n    return zlib.createGunzip();\n  case 'br':\n    return supportsBr && zlib.createBrotliDecompress();\n  case 'deflate':\n    return zlib.createInflate();\n  }\n}\n\nexports.getUnzipStream = getUnzipStream;\n\nvar G_NON_LATIN1_RE = /\\s|[^\\x00-\\xFF]/gu;\n\nfunction safeEncodeURIComponent(ch) {\n  try {\n    return encodeURIComponent(ch);\n  } catch (e) {}\n\n  return ch;\n}\n\nexports.safeEncodeURIComponent = safeEncodeURIComponent;\n\n/**\n * 解析一些字符时，encodeURIComponent可能会抛异常，对这种字符不做任何处理\n * see: http://stackoverflow.com/questions/16868415/encodeuricomponent-throws-an-exception\n * @param ch\n * @returns\n */\nexports.encodeNonLatin1Char = function (str) {\n  if (!isString(str)) {\n    return '';\n  }\n  return str.replace(G_NON_LATIN1_RE, safeEncodeURIComponent);\n};\n\nfunction toUpperCase(str) {\n  return typeof str == 'string' ? str.trim().toUpperCase() : str;\n}\n\nexports.toUpperCase = toUpperCase;\n\nvar CHARSET_RE = /charset=([\\w-]+)/i;\n\nfunction getCharset(str) {\n  var charset;\n  if (CHARSET_RE.test(str)) {\n    charset = RegExp.$1;\n    if (!iconv.encodingExists(charset)) {\n      return;\n    }\n  }\n\n  return charset;\n}\n\nexports.getCharset = getCharset;\n\nfunction toBuffer(buf, charset) {\n  if (buf == null || Buffer.isBuffer(buf)) {\n    return buf;\n  }\n  if (typeof buf === 'object') {\n    try {\n      buf = JSON.stringify(buf);\n    } catch (e) {}\n  } else {\n    buf = String(buf);\n  }\n  if (!buf) {\n    return;\n  }\n  if (charset && typeof charset === 'string' && !UTF8_RE.test(charset)) {\n    try {\n      charset = charset.toLowerCase();\n      if (charset === 'base64') {\n        return Buffer.from(buf, 'base64');\n      }\n      return iconv.encode(buf, charset);\n    } catch (e) {}\n  }\n  return Buffer.from(buf);\n}\n\nexports.toBuffer = toBuffer;\n\nfunction hasRequestBody(req) {\n  req = typeof req == 'string' ? req : req.method;\n  if (typeof req != 'string') {\n    return false;\n  }\n\n  req = req.toUpperCase();\n  return !(\n    req === 'GET' ||\n    req === 'HEAD' ||\n    req === 'OPTIONS' ||\n    req === 'CONNECT'\n  );\n}\n\nexports.hasRequestBody = hasRequestBody;\n\nfunction getMethod(method) {\n  if (typeof method !== 'string') {\n    return 'GET';\n  }\n  return method.trim().toUpperCase() || 'GET';\n}\n\nexports.getMethod = getMethod;\n\nfunction evalJson(str) {\n  try {\n    return json5.parse(str);\n  } catch (e) {}\n}\n\nexports.parseRawJson = evalJson;\n\nexports.isJson = function (str) {\n  str = str && typeof str === 'string' && str.trim();\n  if (!str) {\n    return false;\n  }\n  if (str === '{}') {\n    return true;\n  }\n  var first = str[0];\n  var last = str[str.length - 1];\n  if ((first !== '{' || last !== '}' || str.indexOf(':') === -1) && (first !== '[' || last !== ']')) {\n    return false;\n  }\n  return !!evalJson(str);\n};\n\nvar CRLF_RE = /\\r\\n|\\r|\\n/g;\n\nfunction parseHeaders(headers, rawNames) {\n  if (typeof headers == 'string') {\n    headers = headers.split(CRLF_RE);\n  }\n  var _headers = {};\n  headers.forEach(function (line) {\n    var index = line.indexOf(':');\n    var value;\n    if (index != -1) {\n      value = line.substring(index + 1).trim();\n      var rawName = line.substring(0, index).trim();\n      var name = rawName.toLowerCase();\n      var list = _headers[name];\n      if (rawNames) {\n        rawNames[name] = rawName;\n      }\n      if (list) {\n        if (!Array.isArray(list)) {\n          _headers[name] = list = [list];\n        }\n        list.push(value);\n      } else {\n        _headers[name] = value;\n      }\n    }\n  });\n\n  return lowerCaseify(_headers);\n}\n\nexports.parseHeaders = parseHeaders;\n\n\nfunction isCiphersError(e) {\n  var c = e.code;\n  return (\n    c === 'EPROTO' || c === 'ERR_SSL_BAD_ECPOINT' || c === 'ERR_SSL_VERSION_OR_CIPHER_MISMATCH' ||\n    String(e.message).indexOf('disconnected before secure TLS connection was established') !== -1\n  );\n}\n\nexports.isCiphersError = isCiphersError;\n\nexports.connect = function (options, callback) {\n  var socket, timer, done, retry;\n  var execCallback = function (err) {\n    clearTimeout(timer);\n    timer = null;\n    if (!done) {\n      done = true;\n      err ? callback(err) : callback(null, socket);\n    }\n  };\n  var handleConnect = function () {\n    execCallback();\n  };\n  var handleError = function (err) {\n    if (done) {\n      return;\n    }\n    socket.removeAllListeners();\n    socket.on('error', noop);\n    socket.destroy(err);\n    clearTimeout(timer);\n    if (retry) {\n      return execCallback(err);\n    }\n    retry = true;\n    timer = setTimeout(handleTimeout, 12000);\n    try {\n      if (options.ALPNProtocols && err && isCiphersError(err)) {\n        extend(options, getTlsOptions(options._rules));\n      }\n      socket = sockMgr.connect(options, handleConnect);\n    } catch (e) {\n      return execCallback(e);\n    }\n    socket.on('error', handleError);\n    socket.on('close', function (err) {\n      !done && execCallback(err || new Error('closed'));\n    });\n  };\n  var handleTimeout = function () {\n    handleError(TIMEOUT_ERR);\n  };\n  var sockMgr = options.ALPNProtocols ? tls : net;\n  timer = setTimeout(handleTimeout, 6000);\n  try {\n    socket = sockMgr.connect(options, handleConnect);\n  } catch (e) {\n    return execCallback(e);\n  }\n  socket.on('error', handleError);\n};\n\nfunction getRemoteAddr(req) {\n  try {\n    var socket = req.socket || req;\n    if (!socket._remoteAddr) {\n      var ip = removeIPV6Prefix(socket.remoteAddress) || LOCALHOST;\n      socket._remoteAddr = ip;\n    }\n    return socket._remoteAddr;\n  } catch (e) {}\n  return LOCALHOST;\n}\n\nexports.getRemoteAddr = getRemoteAddr;\n\nfunction getRemotePort(req) {\n  try {\n    var socket = req.socket || req;\n    if (socket._remotePort == null) {\n      var port = socket.remotePort;\n      socket._remotePort = port > 0 ? port : '0';\n    }\n    return socket._remotePort;\n  } catch (e) {}\n  return '0';\n}\n\nexports.getRemotePort = getRemotePort;\n\nexports.getClientPort = function (req, config) {\n  var headers = req.headers || {};\n  var port = headers[CLIENT_PORT_HEADER];\n  if (port > 0) {\n    return port;\n  }\n  return getRemotePort(req, config);\n};\n\nfunction isLocalIp(ip) {\n  if (!isString(ip)) {\n    return true;\n  }\n  return ip.length < 7 || ip === LOCALHOST;\n}\n\nexports.isLocalIp = isLocalIp;\n\nfunction setRawHeader(headers, name, value) {\n  var keys = Object.keys(headers);\n  for (var i = 0, len = keys.length; i < len; i++) {\n    var key = keys[i];\n    if (key.toLowerCase() === name) {\n      headers[key] = value;\n      return;\n    }\n  }\n  headers[name] = value;\n}\n\nexports.setRawHeader = setRawHeader;\n\nfunction getTunnelPath(headers) {\n  var host = headers.host || headers.Host;\n  if (host) {\n    return host;\n  }\n  var keys = Object.keys(headers);\n  for (var i = 0, len = keys.length; i < len; i++) {\n    var key = keys[i];\n    if (key.toLowerCase() === 'host') {\n      return headers[key];\n    }\n  }\n}\n\nexports.connectInner = function (options, cb, config) {\n  var headers = options.headers || {};\n  var proxyOptions = {\n    method: 'CONNECT',\n    agent: false,\n    proxyTunnelPath: options.proxyTunnelPath,\n    enableIntercept: options.enableIntercept,\n    path: getTunnelPath(headers),\n    host: options.proxyHost,\n    port: options.proxyPort,\n    rejectUnauthorized: config.rejectUnauthorized,\n    headers: headers\n  };\n  var httpModule = http;\n  if (options.proxyServername) {\n    proxyOptions.servername = options.proxyServername;\n    httpModule = https;\n  }\n  var auth = options.proxyAuth || options.auth;\n  if (auth) {\n    auth = Buffer.isBuffer(auth) ? auth : Buffer.from(auth + '');\n    auth = 'Basic ' + auth.toString('base64');\n    setRawHeader(headers, 'proxy-authorization', auth);\n  }\n  var timer = setTimeout(function () {\n    if (req) {\n      req.emit('error', TIMEOUT_ERR);\n      req.destroy();\n      req.socket && req.socket.destroy();\n    }\n  }, CONN_TIMEOUT);\n  var req = httpModule.request(proxyOptions);\n  req.on('error', noop);\n  req\n    .on('connect', function (res, socket) {\n      clearTimeout(timer);\n      socket.on('error', noop);\n      if (res.statusCode !== 200) {\n        var err = new Error(\n          'Tunneling socket could not be established, statusCode=' +\n            res.statusCode\n        );\n        err.statusCode = res.statusCode;\n        socket.destroy();\n        process.nextTick(function () {\n          req.emit('error', err);\n        });\n        return;\n      }\n      if (res.headers[ALLOW_ACK]) {\n        socket.write('1');\n      }\n      cb(socket, res);\n    })\n    .end();\n  return req;\n};\n\n\n// AES 加密\nexports.encryptAES = function (text, secretKey, salt) {\n  var key = crypto.scryptSync(secretKey, salt || '5b6af7b9884e1165', 32);\n  var iv = crypto.randomBytes(16);\n  var cipher = crypto.createCipheriv(ALGORITHM, key, iv);\n  var encrypted = cipher.update(text, 'utf8', 'hex');\n  return iv.toString('hex') + '\\n' + encrypted + cipher.final('hex');\n};\n\n// AES 解密\nexports.decryptAES = function (text, secretKey, salt) {\n  text = text.split('\\n');\n  if (text.length !== 2) {\n    throw new Error('Invalid encrypted text');\n  }\n  var key = crypto.scryptSync(secretKey, salt || '5b6af7b9884e1165', 32);\n  var iv = Buffer.from(text[0], 'hex');\n  var decipher = crypto.createDecipheriv(ALGORITHM, key, iv);\n  var decrypted = decipher.update(text[1], 'hex', 'utf8');\n  return decrypted + decipher.final('utf8');\n};\n\nfunction getStat(filepath, callback) {\n  fs.stat(filepath, function (err, stat) {\n    if (stat) {\n      return callback(err, stat);\n    }\n    if (err && err.code === 'ENOENT') {\n      return callback(err);\n    }\n    fs.stat(filepath, callback);\n  });\n}\nexports.getStat = getStat;\n\nexports.getStatSync = function (filepath) {\n  try {\n    return fs.statSync(filepath);\n  } catch (e) {\n    if (e.code === 'ENOENT') {\n      return;\n    }\n  }\n  try {\n    return fs.statSync(filepath);\n  } catch (e) {}\n};\n\nexports.walkInterfaces = function (callback) {\n  var interfaces = os.networkInterfaces();\n  Object.keys(interfaces).forEach(function (name) {\n    var list = interfaces[name];\n    Array.isArray(list) && list.forEach(function (iface) {\n      callback(iface, name);\n    });\n  });\n};\n\nvar DATA_HEADER = 'x-whistle-session-info-5b6a_f7b9-88xe_1165_';\nvar FLAGS = ['_existsCustomCert', '_enableCapture', '_isUpgrade', '_noDecompress',\n  '_isUIRequest', 'fromTunnel', 'fromComposer', 'isPluginReq'];\nvar RAW_KEYS = ['_sniType', 'clientIp', 'clientPort', '_remoteAddr', '_remotePort', 'reqId'];\nvar ENCODE_KEYS = ['sniRuleValue', 'hostIp', '_pipeValue', '_hostValue', '_proxyValue',\n  '_pacValue', 'customParser', 'globalValue', 'serverName', 'commonName', 'fullUrl',\n  'method', '_statusCode', '_relativeUrl', '_ruleUrl', '_finalUrl', '_pluginVarsValue',\n  '_globalPluginVarsValue', '_ruleValue', '_extraUrl', '_ruleProtocol', '_rawPattern', 'hasCertCache'];\nvar RULES_KEYS = ['host', 'proxy', 'pac'];\nvar ALL_KEYS = FLAGS.concat(RAW_KEYS).concat(ENCODE_KEYS);\nvar DECODE_KEYS = ENCODE_KEYS.concat(RULES_KEYS);\nvar LAST_FLAGS_INDEX = FLAGS.length;\nvar LAST_RAW_INDEX = LAST_FLAGS_INDEX + RAW_KEYS.length;\nvar LAST_ENCODE_INDEX = LAST_RAW_INDEX + ENCODE_KEYS.length;\n\nfunction getValue(rule) {\n  return rule && joinIpPort(getMatcherValue(rule) || '', rule.port);\n}\n\nexports.setSessionInfo = function(req, headers) {\n  var result = [];\n  FLAGS.forEach(function(flag, index) {\n    if (req[flag]) {\n      result[index] = 1;\n    }\n  });\n  RAW_KEYS.forEach(function(key, index) {\n    var value = req[key];\n    if (value != null) {\n      result[LAST_FLAGS_INDEX + index] = value;\n    }\n  });\n  ENCODE_KEYS.forEach(function(key, index) {\n    var value = req[key];\n    if (value) {\n      result[LAST_RAW_INDEX + index] = safeEncodeURIComponent(value);\n    }\n  });\n  var rules = req.rules || '';\n  RULES_KEYS.forEach(function(key, index) {\n    var value = getValue(rules[key]);\n    if (value) {\n      result[LAST_ENCODE_INDEX + index] = safeEncodeURIComponent(value);\n    }\n  });\n  result = result.join();\n  if (result) {\n    headers[DATA_HEADER] = result;\n  } else {\n    delete headers[DATA_HEADER];\n  }\n  return headers;\n};\n\nexports.getSessionInfo = function(headers) {\n  var data = headers[DATA_HEADER];\n  delete headers[DATA_HEADER];\n  if (!isString(data)) {\n    return;\n  }\n  var result = {};\n  var rawResult = {};\n  data = data.split(',');\n  ALL_KEYS.forEach(function(flag, index) {\n    result[flag] = data[index] || '';\n  });\n  DECODE_KEYS.forEach(function(key, index) {\n    var value = data[LAST_RAW_INDEX + index];\n    if (value) {\n      rawResult[key] = value;\n      try {\n        result[key] = decodeURIComponent(value);\n      } catch (e) {\n        result[key] = value;\n      }\n    }\n  });\n  result._ = rawResult;\n  return result;\n};\n"
  },
  {
    "path": "lib/util/data-server.js",
    "content": "var net = require('net');\nvar util = require('./index');\nvar logger = require('./logger');\nvar config = require('../config');\nvar socketMgr = require('../socket-mgr');\n\nvar version = config.version;\nvar nodeVersion = process.version.substring(1);\n\nif (!(config.reqCacheSize > 0) || config.reqCacheSize < 600) {\n  config.reqCacheSize = 600;\n}\n\nif (!(config.frameCacheSize > 0) || config.frameCacheSize < 720) {\n  config.frameCacheSize = 600;\n}\n\nvar CLEAR_INTERVAL = 6000;\nvar CACHE_TIME = 1000 * 60 * 2;\nvar MAX_CACHE_TIME = 1000 * 60 * 6;\nvar MAX_LENGTH = config.reqCacheSize;\nvar OVERFLOW_LENGTH = MAX_LENGTH * 3;\nvar MAX_CACHE_SIZE = MAX_LENGTH * 2;\nvar PRESERVE_LEN = 360;\nvar MAX_FRAMES_LENGTH = config.frameCacheSize;\nvar COUNT = 100;\nvar count = 0;\nvar ids = [];\nvar reqData = {};\nvar framesCache = [];\nvar framesMap = {};\nvar proxy, binded;\nvar clearCount = 0;\n\nfunction enable() {\n  if (binded) {\n    return;\n  }\n  binded = true;\n  proxy.on('request', handleRequest);\n  proxy.on('frame', handleFrame);\n  setInterval(clearCache, CLEAR_INTERVAL);\n}\n\n/**\n * 如果超过最大缓存数，清理如下请求数据：\n * 1. 已经请求结束且结束时间超过10秒\n * 2. 请求#1前面的未结束且未被ui读取过的请求\n */\n\nvar MAX_BUF_LEN1 = 1024 * 512;\nvar MAX_BUF_LEN2 = 1024 * 256;\nvar MAX_BUF_LEN3 = 1024 * 128;\nvar MAX_BUF_LEN4 = 1024 * 64;\nvar MIN1 = 1000 * 60;\nvar MIN2 = MIN1 * 2;\nvar MIN3 = MIN1 * 6;\nvar MIN4 = MIN1 * 12;\n\nfunction reduceFrameSize(frame, len, interval, now) {\n  var id = frame.frameId;\n  if (now - id.substring(0, id.indexOf('-')) < interval) {\n    return;\n  }\n  frame.len = len;\n  var bin = frame.bin;\n  var base64 = frame.base64;\n  if (base64) {\n    frame.base64 = null;\n    bin = Buffer.from(base64, 'base64');\n  }\n  if (bin) {\n    frame.bin = bin.slice(0, len);\n  }\n}\n\nvar clearFrames = function (frame, now) {\n  var len = frame.len || frame.length;\n  if (!len || len <= MAX_BUF_LEN4) {\n    return;\n  }\n  if (len > MAX_BUF_LEN1) {\n    return reduceFrameSize(frame, MAX_BUF_LEN1, MIN1, now);\n  }\n  if (len > MAX_BUF_LEN2) {\n    return reduceFrameSize(frame, MAX_BUF_LEN2, MIN2, now);\n  }\n\n  if (len > MAX_BUF_LEN3) {\n    return reduceFrameSize(frame, MAX_BUF_LEN3, MIN3, now);\n  }\n  reduceFrameSize(frame, MAX_BUF_LEN4, MIN4, now);\n};\n\nfunction clearCache() {\n  var overflow = framesCache.length - MAX_FRAMES_LENGTH;\n  var now = Date.now();\n  // 1 分钟触发一次\n  ++clearCount;\n  if (clearCount > 10) {\n    clearCount = 0;\n  }\n\n  if (overflow > 0) {\n    framesCache.splice(0, overflow + 60);\n    framesMap = {};\n    framesCache.forEach(function (frame) {\n      framesMap[frame.reqId] = frame;\n      !clearCount && clearFrames(frame, now);\n    });\n  } else if (!clearCount) {\n    framesCache.forEach(function (frame) {\n      clearFrames(frame, now);\n    });\n  }\n  var len = ids.length;\n  if (len <= MAX_LENGTH) {\n    return;\n  }\n\n  var _ids = [];\n  var preserveLen = len;\n  overflow = -1;\n  if (len >= OVERFLOW_LENGTH) {\n    overflow = len - MAX_CACHE_SIZE;\n    preserveLen = len - PRESERVE_LEN;\n  }\n  var isTimeout = function (curData, i) {\n    if (i < overflow) {\n      return true;\n    }\n    return (\n      curData.endTime &&\n      now - curData.endTime > (i >= preserveLen ? MAX_CACHE_TIME : CACHE_TIME)\n    );\n  };\n  for (var i = 0; i < len; i++) {\n    var id = ids[i];\n    var curData = reqData[id];\n    if (isTimeout(curData, i)) {\n      curData.abort && curData.abort(true);\n      delete reqData[id];\n    } else {\n      if (curData.abort && now - curData.startTime > MAX_CACHE_TIME) {\n        curData.abort(true);\n      }\n      _ids.push(id);\n    }\n  }\n  ids = _ids;\n}\n// 不存在startTime相等的id\nfunction getIndex(startTime, start, end) {\n  var midIndex = Math.floor((start + end) / 2);\n  if (midIndex == start) {\n    return end;\n  }\n  if (ids[midIndex] < startTime) {\n    return getIndex(startTime, midIndex, end);\n  }\n  return getIndex(startTime, start, midIndex);\n}\n\nfunction toBase64String(data) {\n  if (Buffer.isBuffer(data.body)) {\n    data.base64 = data.body.toString('base64');\n    data.body = '';\n  }\n}\n\nfunction getIds(startTime, count, lastRowId) {\n  startTime = startTime || lastRowId;\n  if (!startTime) {\n    return ids.slice(-count);\n  }\n  var index = 0;\n  if (startTime !== '0') {\n    index = ids.indexOf(startTime) + 1;\n    if (!index && startTime.length > 6) {\n      var startId = ids[0];\n      if (startId && startId < startTime) {\n        var end = ids.length - 1;\n        if (ids[end] < startTime) {\n          return [];\n        }\n        index = getIndex(startTime, 0, end);\n      }\n    }\n  }\n  return ids.slice(index, index + count);\n}\n\nfunction removeBase64(data) {\n  var result = {};\n  Object.keys(data).forEach(function(key) {\n    if (key !== 'headers' && key !== 'base64' && key !== 'rawHeaderNames') {\n      result[key] = data[key];\n    }\n  });\n  return result;\n}\n\nfunction getBase64Len(base64) {\n  if (!base64) {\n    return 0;\n  }\n  var len = base64.length;\n  if (base64[len - 1] === '=') {\n    len -= 2;\n    if (base64[len] === '=') {\n      --len;\n    }\n  }\n  return len;\n}\n\nfunction setBody(item, len) {\n  if (!(len > 0)) {\n    return item;\n  }\n  var base64 = item.base64;\n  var curLen = getBase64Len(base64);\n  item = removeBase64(item);\n  if (len < curLen) {\n    item.preLen = len;\n    item.base64 = base64.substring(len);\n  }\n  return item;\n}\n\nfunction getList(ids, status) {\n  if (!Array.isArray(ids)) {\n    return [];\n  }\n  return ids.map(function (id, i) {\n    var data = reqData[id];\n    var opts = status && status[i];\n    if (data) {\n      toBase64String(data.req);\n      toBase64String(data.res);\n    }\n    if (!data || typeof opts !== 'string') {\n      return data;\n    }\n    opts = opts.split('-');\n    var result = {};\n    Object.keys(data).forEach(function(key) {\n      if (key === 'url' || key === 'rulesHeaders' || (key === 'rules' && opts[1] != '0')) {\n        return;\n      }\n      var item = data[key];\n      if (key === 'req') {\n        if (!opts[0]) {\n          item = removeBase64(item);\n        } else {\n          item = setBody(item, +opts[0]);\n        }\n      } else if (key === 'res') {\n        item = setBody(item, +opts[1]);\n      }\n      result[key] = item;\n    });\n    return result;\n  });\n}\n\nfunction handleRequest(req, data) {\n  var id = (data.id = data.id || data.startTime + '-' + ++count);\n  var removeAbort = function () {\n    if (data.abort) {\n      delete data.abort;\n    }\n  };\n  req.on('end', removeAbort);\n  req.on('error', removeAbort);\n  req.on('abort', removeAbort);\n  data.version = version;\n  data.nodeVersion = nodeVersion;\n  reqData[id] = data;\n  ids.indexOf(id) === -1 && ids.push(id);\n}\n\nfunction decodeData(frame) {\n  if (frame.base64 == null) {\n    frame.base64 = frame.bin ? frame.bin.toString('base64') : '';\n    frame.bin = '';\n  }\n  return frame;\n}\nfunction handleFrame(data) {\n  framesCache.push(data);\n  framesMap[data.reqId] = data;\n}\n\nfunction getFrames(curReqId, lastFrameId) {\n  if (!curReqId) {\n    return;\n  }\n  var result = [];\n  var lastFrame = framesMap[curReqId];\n  if (lastFrame && (!lastFrameId || lastFrame.frameId > lastFrameId)) {\n    var count = 16;\n    for (var i = 0, len = framesCache.length; i < len; i++) {\n      var frame = framesCache[i];\n      if (\n        frame.reqId === curReqId &&\n        (!lastFrameId || frame.frameId > lastFrameId)\n      ) {\n        result.push(decodeData(frame));\n        if (--count <= 0) {\n          return result;\n        }\n      }\n    }\n  }\n  return result;\n}\n\nfunction getLastFrame(curReqId) {\n  var frame = framesMap[curReqId];\n  if (frame && frame.reqId === curReqId) {\n    return decodeData(frame);\n  }\n}\n\nfunction getDomain(item) {\n  var url = item && item.url;\n  if (!url) {\n    return '';\n  }\n  var index = url.indexOf('://');\n  if (index === -1) {\n    return '';\n  }\n  index += 3;\n  url = url.substring(index);\n  index = url.indexOf('/');\n  if (index !== -1) {\n    url = url.substring(0, index);\n  }\n  index = url.indexOf(':');\n  if (index !== -1) {\n    url = url.substring(0, index);\n  }\n  return url;\n}\n\n\nmodule.exports = function init(_proxy) {\n  proxy = _proxy;\n  enable();\n  /**\n   * options: {\n   * \t\tstartTime: timestamp || timestamp + '-' + count\n   * \t\tcount: 获取新数据的数量\n   * \t\tids: 请未结束的id列表\n   * }\n   *\n   * @param options\n   */\n  function formatFilter(filter, clientIp, clientId) {\n    if (!filter.url && !filter.name && !filter.value && !filter.ip) {\n      return;\n    }\n    var url = util.trimStr(filter.url).toLowerCase();\n    var ip = util.trimStr(filter.ip);\n    var list = [];\n    var cid;\n    var result;\n    if (ip === 'self') {\n      ip = clientIp;\n      cid = clientId;\n    } else if (ip === 'clientIp') {\n      ip = clientIp;\n    }\n    if (ip === 'clientId') {\n      if (clientId) {\n        cid = clientId;\n      } else {\n        result = { clientIp: clientIp };\n      }\n      ip = null;\n    } else if (ip && !net.isIP(ip)) {\n      ip.split(',').forEach(function (item) {\n        item = item.trim();\n        if (item === 'clientId') {\n          cid = clientId;\n        } else {\n          if (item === 'self') {\n            cid = clientId;\n            item = clientIp;\n          } else if (item === 'clientIp') {\n            item = clientIp;\n          }\n          if (list.indexOf(item) === -1) {\n            list.push(item);\n          }\n        }\n      });\n      ip = null;\n    }\n    if (url) {\n      result = result || {};\n      result.url = url;\n    }\n    var headers;\n    for (var i = 0; i < 6; i++) {\n      var key = 'name' + (i || '');\n      var name = util.trimStr(filter[key]).toLowerCase();\n      if (name) {\n        result = result || {};\n        var value = util.trimStr(filter['value' + (i || '')]).toLowerCase();\n        if (i) {\n          headers = headers || [];\n          result.headers = headers;\n          headers.push({\n            name: name,\n            value: value\n          });\n        } else {\n          result.name = name;\n          result.value = value;\n        }\n      }\n    }\n    if (ip) {\n      result = result || {};\n      result.ip = ip;\n    }\n    if (cid) {\n      result = result || {};\n      result.clientId = cid;\n    }\n    if (list.length) {\n      result = result || {};\n      result.ipList = result.idList = list.slice(0, 16);\n    }\n    if (result && (result.name || headers) && filter.mtype == 1) {\n      result.exact = 1;\n    }\n    return result;\n  }\n  function checkClientIp(item, filter) {\n    var clientIp = item.req.ip;\n    if (filter.clientIp) {\n      return clientIp === filter.clientIp;\n    }\n    if (filter.ip && clientIp === filter.ip) {\n      return true;\n    }\n    // 有 clientId 过滤条件时，必须匹配 clientId\n    var clientId = getClientId(item);\n    var ipList = filter.ipList;\n    if (filter.clientId) {\n      if (clientId === filter.clientId) {\n        return true;\n      }\n      if (!ipList) {\n        return false;\n      }\n    } else if (!ipList) {\n      return true;\n    }\n    var len = ipList.length;\n    if (len < 3) {\n      if (ipList[0] === clientIp || ipList[0] === clientId) {\n        return true;\n      }\n      if (ipList[1] && (ipList[1] === clientIp || ipList[1] === clientId)) {\n        return true;\n      }\n    } else {\n      for (var i = 0; i < len; i++) {\n        var ip = ipList[i];\n        if (ip === clientIp || ip === clientId) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  function checkHeader(text, keyword, exact) {\n    if (!keyword) {\n      return text != null;\n    }\n    if (!text || typeof text !== 'string') {\n      if (!Array.isArray(text)) {\n        return false;\n      }\n      text = text.join('\\n');\n    }\n    text = text.toLowerCase();\n    if (exact) {\n      return (\n        text === keyword ||\n        text === util.encodeURIComponent(keyword).toLowerCase()\n      );\n    }\n    return (\n      text.indexOf(keyword) !== -1 ||\n      text.indexOf(util.encodeURIComponent(keyword).toLowerCase()) !== -1\n    );\n  }\n\n  function getClientId(item) {\n    return (\n      item._clientId ||\n      item.req.headers[config.CLIENT_ID_HEADER] ||\n      item.clientId\n    );\n  }\n\n  function checkItem(item, filter) {\n    if (!item || !checkClientIp(item, filter)) {\n      return false;\n    }\n    var h = item.req.headers;\n    if (filter.filterKey && h[filter.filterKey] != filter.filterValue) {\n      return false;\n    }\n    if (filter.filterClientId && getClientId(item) != filter.filterClientId) {\n      return false;\n    }\n    if (\n      filter.name &&\n      !checkHeader(h[filter.name], filter.value, filter.exact)\n    ) {\n      return false;\n    }\n    if (\n      filter.url &&\n      !checkHeader((item.isHttps ? 'tunnel://' : '') + item.url, filter.url)\n    ) {\n      return false;\n    }\n    var headers = filter.headers;\n    if (headers) {\n      for (var i = 0, len = headers.length; i < len; i++) {\n        var header = headers[i];\n        if (!checkHeader(h[header.name], header.value, filter.exact)) {\n          return false;\n        }\n      }\n    }\n    return true;\n  }\n  proxy.getLastDataId = function () {\n    return ids[ids.length - 1];\n  };\n\n  proxy.getCookiesByDomain = function(domain) {\n    var result = [];\n    if (!domain || typeof domain !== 'string') {\n      return result;\n    }\n    for (var i = ids.length - 1; i >= 0; i--) {\n      var item = reqData[ids[i]];\n      if (getDomain(item) === domain) {\n        var cookie = item.req.headers.cookie;\n        if (cookie && result.indexOf(cookie) === -1) {\n          result.push(cookie);\n          if (result.length >= 30) {\n            return result;\n          }\n        }\n      }\n    }\n    return result;\n  };\n\n  proxy.getItem = function (id) {\n    var item = reqData[id];\n    if (item) {\n      toBase64String(item.req);\n      toBase64String(item.res);\n    }\n    return item;\n  };\n  proxy.abortRequest = function (id) {\n    var item = id && reqData[id];\n    item && item.abort && item.abort();\n  };\n  proxy.getFrames = function (options) {\n    return getFrames(options.curReqId, options.lastFrameId);\n  };\n\n  function getNetworkTime(id) {\n    var item = id && reqData[id];\n    return item && {\n      startTime: item.startTime,\n      dnsTime: item.dnsTime,\n      requestTime: item.requestTime,\n      responseTime: item.responseTime,\n      endTime: item.endTime\n    };\n  }\n\n  proxy.getData = function (\n    options,\n    clientIp,\n    key,\n    value,\n    filterClientId,\n    clientId\n  ) {\n    options = options || {};\n    var filter = formatFilter(options, clientIp, clientId);\n    var data = {};\n    var count = options.count;\n    var startTime = options.startTime;\n    var clearNetwork =\n      !(options.dumpCount > 0) && (count == 0 || startTime == -2);\n    if (!clearNetwork) {\n      count = count > 0 && count < COUNT ? +count : COUNT;\n      if (options.dumpCount > 0) {\n        var len = ids.length;\n        startTime = ids[len > options.dumpCount ? len - options.dumpCount : 0];\n      }\n    }\n    if (key && value) {\n      filter = filter || {};\n      filter.filterKey = key;\n      filter.filterValue = value;\n    }\n    if (filterClientId) {\n      filter = filter || {};\n      filter.filterClientId = filterClientId;\n    }\n    var newIds =\n      clearNetwork || startTime == -1\n        ? []\n        : getIds(startTime, count, options.lastRowId);\n    var setData = function (item) {\n      if (item) {\n        if (config.secureFilter) {\n          try {\n            item = config.secureFilter(item, clientIp, filter) || item;\n          } catch (e) {\n            if (config.debugMode) {\n              /* eslint-disable no-console */\n              console.log(e);\n            }\n            logger.error(e);\n          }\n        }\n        data[item.id] = item;\n      }\n    };\n    if (newIds.length > 0) {\n      if (filter) {\n        var id = newIds[0];\n        var index = ids.indexOf(newIds[0]);\n        newIds = [];\n        while (id && count > 0) {\n          var item = reqData[id];\n          if (checkItem(item, filter)) {\n            toBase64String(item.req);\n            toBase64String(item.res);\n            setData(item);\n            newIds.push(id);\n            --count;\n          }\n          id = ids[++index];\n        }\n      } else if (!filter) {\n        getList(newIds).forEach(setData);\n      }\n    }\n    getList(options.ids, options.status).forEach(setData);\n    var endId = ids[ids.length - 1];\n    var lastFrameId, frames;\n    if (options.lastFrameId == -3) {\n      var lastFrame = getLastFrame(options.curReqId);\n      if (lastFrame) {\n        if (lastFrame.closed || lastFrame.err) {\n          frames = [lastFrame];\n        } else {\n          lastFrameId = lastFrame.frameId;\n        }\n      }\n    } else {\n      frames = getFrames(options.curReqId, options.lastFrameId);\n    }\n    var lastNewId = newIds[newIds.length - 1];\n    var lastNewFrameId = frames && frames[frames.length - 1];\n    var hasNew =\n      (lastNewId && endId !== lastNewId) ||\n      (lastNewFrameId && lastNewFrameId !== lastFrameId);\n    var tunnelIds = options.tunnelIds;\n    var tunnelIps = {};\n    if (Array.isArray(tunnelIds) && tunnelIds.length > 0) {\n      tunnelIds.forEach(function (id) {\n        tunnelIps[id] = proxy.getTunnelIp(id);\n      });\n    }\n\n    return {\n      ids: options.ids || [],\n      composerTime: getNetworkTime(options.composerReqId),\n      tunnelIps: tunnelIps,\n      newIds: newIds,\n      data: data,\n      hasNew: hasNew,\n      lastId: clearNetwork ? endId : lastNewId,\n      endId: endId,\n      frames: frames,\n      lastFrameId: lastFrameId,\n      socketStatus: socketMgr.getStatus(options.curReqId)\n    };\n  };\n};\n"
  },
  {
    "path": "lib/util/drain.js",
    "content": "var createTransform = require('./common').createTransform;\n\nvar noop = function () {};\n\nmodule.exports = function (stream, endHandler) {\n  if (stream._hasAlreadyDrain || (!stream.noReqBody && stream.useH2)) {\n    return typeof endHandler == 'function' && endHandler();\n  }\n  stream._hasAlreadyDrain = true;\n  var emitEndStream = createTransform();\n  emitEndStream.on('data', noop).on('error', noop);\n  emitEndStream.on('end', endHandler);\n  stream.pipe(emitEndStream);\n};\n"
  },
  {
    "path": "lib/util/file-mgr.js",
    "content": "var fs = require('fs');\nvar iconv = require('iconv-lite');\nvar common = require('./common');\nvar isUtf8 = require('./is-utf8');\n\nvar isWin32 = process.platform === 'win32';\nvar MAX_SIZE = 1024 * 1024 * 64;\nvar CRLF = Buffer.from('\\r\\n');\nvar noop = function (_) {\n  return _;\n};\n\nfunction convertSlash(filePath) {\n  filePath = common.getHomePath(filePath);\n  return isWin32 ? filePath : common.formatPathSep(filePath);\n}\n\nfunction decode(buf) {\n  if (!Buffer.isBuffer(buf)) {\n    return buf ? String(buf) : '';\n  }\n  if (!isUtf8(buf)) {\n    try {\n      return iconv.decode(buf, 'GB18030');\n    } catch (e) {}\n  }\n  return String(buf);\n}\n\nfunction isString(path) {\n  return path && typeof path === 'string';\n}\n\nfunction readSingleFile(path, callback) {\n  if (!isString(path)) {\n    return callback();\n  }\n  var stream = fs.createReadStream(convertSlash(path));\n  var done, buf;\n  var execCallback = function (err) {\n    if (done) {\n      return;\n    }\n    done = true;\n    stream.close();\n    callback(err ? null : buf);\n  };\n  stream.on('data', function (data) {\n    if (done) {\n      return;\n    }\n    buf = buf ? Buffer.concat([buf, data]) : data;\n    if (buf.length > MAX_SIZE) {\n      execCallback();\n    }\n  });\n  stream.on('error', execCallback);\n  stream.on('end', execCallback);\n}\n\nfunction getFileMap(list) {\n  if (Array.isArray(list)) {\n    list = list.join('|');\n  }\n  if (!isString(list)) {\n    return '';\n  }\n  var fileMap = {};\n  list = list.split('|');\n  list.forEach(function (file) {\n    fileMap[file || ''] = 1;\n  });\n  return fileMap;\n}\n\nfunction readFileMap(list, callback, isText) {\n  var fileMap = getFileMap(list);\n  if (!fileMap) {\n    return callback('');\n  }\n  var files = Object.keys(fileMap);\n  var len = files.length;\n  files.forEach(function (file) {\n    readSingleFile(file, function (data) {\n      fileMap[file || ''] = isText ? decode(data) : data;\n      if (--len <= 0) {\n        callback(fileMap);\n      }\n    });\n  });\n}\n\nfunction joinData(list, isText, charset) {\n  if (!list || !list.length) {\n    return '';\n  }\n  if (isText) {\n    return list.filter(noop).join('\\r\\n');\n  }\n  var result = [];\n  list.forEach(function (buf) {\n    if (buf) {\n      buf = common.toBuffer(buf, charset);\n      result.push(buf, CRLF);\n    }\n  });\n  result.pop();\n  return result.length ? Buffer.concat(result) : '';\n}\n\nfunction readFileFromMap(path, fileMap, isText) {\n  if (!isString(path)) {\n    return '';\n  }\n  path = path.split('|');\n  return joinData(\n    path.map(function (file) {\n      return fileMap[file || ''];\n    }),\n    isText\n  );\n}\n\nfunction readFileList(list, callback, isText) {\n  readFileMap(\n    list,\n    function (fileMap) {\n      if (!fileMap) {\n        return callback('');\n      }\n      var result = [];\n      list.forEach(function (file) {\n        result.push(readFileFromMap(file, fileMap, isText));\n      });\n      callback(result);\n    },\n    isText\n  );\n}\n\nfunction readFile(path, callback) {\n  if (!isString(path)) {\n    return callback();\n  }\n  readFileList(path.split('|'), function (result) {\n    callback(joinData(result));\n  });\n}\n\nfunction readFilesText(list, callback) {\n  readFileList(list, callback, true);\n}\n\nfunction readFileText(path, callback) {\n  if (!isString(path)) {\n    return callback();\n  }\n  readFilesText(\n    path.split('|'),\n    function (result) {\n      callback(joinData(result, true));\n    },\n    true\n  );\n}\n\nexports.joinData = joinData;\nexports.decode = decode;\nexports.readFile = readFile;\nexports.readFileList = readFileList;\nexports.readFileText = readFileText;\nexports.readFilesText = readFilesText;\nexports.convertSlash = convertSlash;\n"
  },
  {
    "path": "lib/util/file-writer-transform.js",
    "content": "var Transform = require('pipestream').Transform;\nvar util = require('util');\nvar config = require('../config');\nvar STATUS_CODES = require('http').STATUS_CODES || {};\n\nfunction FileWriterTransform(writer, source, isRaw, req, isReq) {\n  var self = this;\n  Transform.call(self);\n  self._writer = writer;\n  source.on('error', function () {\n    writer.end();\n  });\n  isRaw && writer.write(getRawData(source, req, isReq));\n}\n\nfunction getRawData(source, req, isReq) {\n  var firstLine;\n  if (req) {\n    var message = source.statusMessage || STATUS_CODES[source.statusCode] || '';\n    firstLine = [\n      'HTTP/' + (req.httpVersion || '1.1'),\n      source.statusCode,\n      message\n    ].join(' ');\n  } else {\n    firstLine = [\n      source.method,\n      source.url,\n      'HTTP/' + (source.httpVersion || '1.1')\n    ].join(' ');\n  }\n\n  var headers = [];\n  var rawHeaderNames = source.rawHeaderNames || {};\n  Object.keys(source.headers).forEach(function (key) {\n    var value = source.headers[key];\n    if (!isReq || (key !== config.HTTPS_FIELD && key !== 'content-encoding')) {\n      key = rawHeaderNames[key] || key;\n      headers.push(\n        Array.isArray(value)\n          ? value\n              .map(function (val) {\n                return key + ': ' + val;\n              })\n              .join('\\r\\n')\n          : key + ': ' + value\n      );\n    }\n  });\n  if (headers = headers.join('\\r\\n')) {\n    headers = '\\r\\n' + headers;\n  }\n  return firstLine + headers + '\\r\\n\\r\\n';\n}\n\nutil.inherits(FileWriterTransform, Transform);\n\nFileWriterTransform.prototype._transform = function (\n  chunk,\n  encoding,\n  callback\n) {\n  if (chunk) {\n    this._writer.write(chunk);\n  } else {\n    this._writer.end();\n  }\n\n  callback(null, chunk);\n};\n\nmodule.exports = FileWriterTransform;\n"
  },
  {
    "path": "lib/util/http-mgr.js",
    "content": "var http = require('http');\nvar https = require('https');\nvar fs = require('fs');\nvar extend = require('extend');\nvar fileMgr = require('./file-mgr');\nvar logger = require('./logger');\nvar parseUrl = require('./parse-url');\nvar zlib = require('./zlib');\nvar common = require('./common');\n\nvar cache = {};\nvar listeners = [];\nvar newUrls;\nvar TIMEOUT = 16000;\nvar MAX_RULES_LEN = 1024 * 72;\nvar MAX_FILE_LEN = 1024 * 256;\nvar MAX_INTERVAL = 1000 * 30;\nvar MIN_INTERVAL = 1000 * 10;\nvar TIMEOUT_ERR = new Error('Timeout');\nvar EXCEED = 'EXCEED';\nvar OPTIONS = { encoding: 'utf8' };\nvar queue = [];\nvar queueTimer;\nvar FILE_RE = /^(?:[a-z]:[\\\\/]|[~～]?\\/)/i;\nvar GZIP_RE = /gzip/i;\nvar pendingList = process.whistleStarted ? null : [];\nvar pluginMgr;\n\nprocess.once('whistleStarted', function () {\n  if (pendingList) {\n    pendingList.forEach(function (item) {\n      add(item[0], item[1], item[2]);\n    });\n    pendingList = null;\n  }\n});\n\nfunction getInterval(time, isLocal) {\n  var len = Object.keys(cache).length || 1;\n  var interval = isLocal\n    ? 5000\n    : Math.max(MIN_INTERVAL, Math.ceil(MAX_INTERVAL / len));\n  var minTime = interval - (time > 0 ? time : 0);\n  return Math.max(minTime, 1000);\n}\n\nfunction triggerChange(data, body) {\n  if (data) {\n    body = (body && body.trim()) || '';\n    if (data.body === body) {\n      return;\n    }\n    data.body = body;\n  }\n  if (newUrls) {\n    return;\n  }\n  newUrls = {};\n  listeners.forEach(function (l) {\n    l();\n  });\n  Object.keys(newUrls).forEach(function (url) {\n    newUrls[url] = cache[url];\n  });\n  cache = newUrls;\n  newUrls = null;\n}\n\nfunction parseOptions(options) {\n  if (typeof options === 'string') {\n    options = parseUrl(options);\n  } else {\n    var fullUrl = options.url || options.uri;\n    if (fullUrl && typeof fullUrl === 'string') {\n      options = extend(options, parseUrl(fullUrl));\n    }\n  }\n  var maxLength = options.maxLength;\n  if (!(maxLength > 0)) {\n    options.maxLength = 0;\n  }\n  options.agent = false;\n  if (options.rejectUnauthorized !== true) {\n    options.rejectUnauthorized = false;\n  }\n  if (options.headers && options.headers.trailer) {\n    delete options.headers.trailer;\n  }\n  return options;\n}\n\nfunction toString(obj) {\n  if (obj == null) {\n    return;\n  }\n  if (Buffer.isBuffer(obj)) {\n    return obj;\n  }\n  if (typeof obj === 'object') {\n    return JSON.stringify(obj) || '';\n  }\n  return obj + '';\n}\n\nvar NOT_PLUGIN_ERR = new Error('Error: not found');\nvar NOT_UI_SERVER_ERR = new Error('Error: not implemented uiServer');\nNOT_PLUGIN_ERR.code = 404;\nNOT_UI_SERVER_ERR.code = 501;\n\nfunction loadPlugin(options, callback) {\n  var name = options.pluginName;\n  if (!name) {\n    return callback();\n  }\n  pluginMgr.loadPluginByName(name, function (err, ports) {\n    if (err || !ports || !ports.uiPort) {\n      return callback(err || (ports ? NOT_UI_SERVER_ERR : NOT_PLUGIN_ERR));\n    }\n    options.url =\n      'http://127.0.0.1:' + ports.uiPort + options.url.substring(name.length);\n    callback();\n  });\n}\n\nfunction sendReq(options, callback) {\n  var conf = options.whistleConfig;\n  var httpModule = http;\n  var isHttps = options.protocol === 'https:';\n  if (conf && options.headers) {\n    var headers = options.headers;\n    if (isHttps) {\n      headers['x-whistle-https-request'] = 1;\n    }\n    headers.host = headers.host || options.host;\n    options.protocol = null;\n    options.hostname = null;\n    options.agent = false;\n    options.host = conf.host;\n    options.port = conf.port;\n  } else if (isHttps) {\n    httpModule = https;\n  }\n\n  var timer, client;\n  var handleCallback = function (err, res) {\n    clearTimeout(timer);\n    if (httpModule) {\n      httpModule = null;\n      callback(err, res);\n    }\n    err && client && client.destroy();\n  };\n  timer = setTimeout(function () {\n    handleCallback(TIMEOUT_ERR);\n  }, TIMEOUT);\n  try {\n    client = httpModule.request(options, function (res) {\n      res.on('error', handleCallback);\n      handleCallback(null, res);\n    });\n    client.on('error', handleCallback);\n    var body = options.body;\n    if (body && typeof body.pipe === 'function') {\n      body.once('error', function () {\n        client.destroy();\n      });\n      body.pipe(client);\n    } else {\n      client.end(toString(body));\n    }\n    return client;\n  } catch (e) {\n    handleCallback(e);\n  }\n}\n\nfunction gunzip(err, res, body, callback) {\n  if (!err && body && res && GZIP_RE.test(res.headers['content-encoding'])) {\n    zlib.gunzip(body, callback);\n  } else {\n    callback(err, body);\n  }\n}\n\nfunction request(options, callback) {\n  loadPlugin(options, function (err) {\n    if (err) {\n      return callback(err, '', '');\n    }\n    options = parseOptions(options);\n    var done, client, timer;\n    var type = options.responseType;\n    var strictMode = options.strictMode;\n    var handleCallback = function (err, res, body) {\n      if (!done) {\n        done = true;\n        gunzip(err, res, body, function(e, data) {\n          data = e ? '' : (options.needRawData || type === 'buffer' ? data : data + '');\n          if (type === 'json') {\n            if (!data) {\n              e = e || new Error('Invalid JSON');\n            } else {\n              try {\n                data = JSON.parse(data);\n              } catch (err) {\n                data = null;\n                e = err;\n              }\n            }\n          }\n          callback(e, data, res || '');\n        });\n      }\n      clearTimeout(timer);\n      err && client && client.destroy();\n    };\n    var addTimeout = function () {\n      clearTimeout(timer);\n      timer = setTimeout(function () {\n        handleCallback(TIMEOUT_ERR);\n      }, TIMEOUT);\n    };\n    var maxLength = options.maxLength;\n    var handleResponse = function(res) {\n      if (strictMode && res && res.statusCode != 200) {\n        return handleCallback(new Error('Response code ' + res.statusCode), res);\n      }\n      if (type === 'stream') {\n        clearTimeout(timer);\n        return callback(null, res, res);\n      }\n      addTimeout();\n      var body = '';\n      res.on('data', function (data) {\n        body = body ? Buffer.concat([body, data]) : data;\n        addTimeout();\n        if (maxLength && body.length > maxLength) {\n          var err;\n          if (!options.ignoreExceedError) {\n            err = new Error('The response body exceeded length limit');\n            err.code = EXCEED;\n          }\n          handleCallback(err, res, body);\n        }\n      });\n      res.on('end', function() {\n        handleCallback(null, res, body);\n      });\n    };\n    client = sendReq(options, function (err, res) {\n      if (err) {\n        handleCallback(err);\n      } else {\n        handleResponse(res);\n      }\n    });\n  });\n}\n\nexports.request = request;\n\nfunction readFile(url, callback) {\n  var data;\n  var now = Date.now();\n  var execCallback = function () {\n    callback(url, Date.now() - now);\n  };\n  var filePath = fileMgr.convertSlash(url);\n  common.getStat(filePath, function (err, stat) {\n    data = cache[url];\n    if (!data) {\n      return execCallback();\n    }\n    if (err) {\n      if (err.code === 'ENOENT') {\n        err = null;\n      } else {\n        logger.error(url, err.message);\n      }\n      triggerChange(data);\n      data.mtime = null;\n      return execCallback();\n    }\n    if (!stat.isFile()) {\n      triggerChange(data);\n      data.mtime = null;\n      return execCallback();\n    }\n    var time = stat.mtime.getTime();\n    if (time === data.mtime) {\n      return execCallback();\n    }\n\n    var stream = fs.createReadStream(filePath, OPTIONS);\n    var done;\n    var body = '';\n    var listener = function (err) {\n      if (done) {\n        return;\n      }\n      execCallback();\n      if (err && err.code !== 'ENOENT') {\n        return;\n      }\n      done = true;\n      data.mtime = time;\n      stream.close();\n      triggerChange(data, body);\n    };\n    stream.on('data', function (text) {\n      if (done) {\n        return;\n      }\n      body += text;\n      if (body.length > MAX_FILE_LEN) {\n        listener();\n      }\n    });\n    stream.on('error', listener);\n    stream.on('end', listener);\n  });\n}\n\nfunction addQueue(url, consumeTime) {\n  if (cache[url] && queue.indexOf(url) === -1) {\n    queue.push(url);\n  }\n  var data;\n  while (!queueTimer && !data) {\n    url = queue.shift();\n    if (!url) {\n      return;\n    }\n    data = cache[url];\n    if (data) {\n      queueTimer = setTimeout(function () {\n        queueTimer = null;\n        updateBody(url, addQueue);\n      }, getInterval(consumeTime, data.isLocalUrl || data.isLocalPath));\n      return;\n    }\n  }\n}\n\nfunction updateBody(url, callback, init) {\n  var data = cache[url];\n  if (!data) {\n    return callback && callback();\n  }\n  if (data.isLocalPath) {\n    return readFile(url, addQueue);\n  }\n  var now = Date.now();\n  var options = {\n    url: url,\n    pluginName: data.pluginName,\n    maxLength: MAX_RULES_LEN,\n    ignoreExceedError: true\n  };\n  if (data.headers) {\n    options.headers = data.headers;\n  }\n  request(options, function (err, body, res) {\n    data = cache[url];\n    callback && callback(url, Date.now() - now);\n    if (!data) {\n      return;\n    }\n    var code = res.statusCode;\n    var isRedirect = code == 301 || code == 302 || code == 303 || code == 307 || code == 308;\n    var notFound = err\n      ? err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED'\n      : code != 200 && code != 204;\n    err && logger.error('[Load Rules]', url, err.message || err);\n    if (notFound) {\n      data._retry = data._retry || 0;\n      if (isRedirect || data._retry > 2) {\n        if (!err) {\n          var msg = code;\n          if (isRedirect) {\n            var loc = res.headers.location;\n            msg += loc ? ' redirect to ' + loc : '';\n          }\n          logger.warn('[Load Rules]', url, 'status', msg);\n        }\n        data._retry = -6;\n        err = body = '';\n        notFound = false;\n      }\n      ++data._retry;\n    } else {\n      data._retry = 0;\n    }\n    if (notFound || err) {\n      if (init) {\n        updateBody(url);\n        return;\n      }\n    }\n    addQueue(url);\n    if (notFound || err) {\n      return;\n    }\n    triggerChange(data, body);\n  });\n  return true;\n}\n\nexports.addChangeListener = function (l) {\n  listeners.push(l);\n};\n\nfunction add(url, headers, pluginName) {\n  var data = cache[url];\n  if (!data) {\n    cache[url] = data = {\n      body: '',\n      pluginName: pluginName,\n      isLocalUrl: pluginName || url.indexOf('http://127.0.0.1:') === 0,\n      isLocalPath: FILE_RE.test(url),\n      headers: headers\n    };\n    updateBody(url, null, true);\n  }\n  if (newUrls) {\n    newUrls[url] = 1;\n  }\n  return data.body;\n}\n\nexports.add = function (url, headers, pluginName) {\n  if (pendingList && headers && headers['x-whistle-internal-id']) {\n    pendingList.push([url, headers, pluginName]);\n    return '';\n  }\n  return add(url, headers, pluginName);\n};\n\nexports.forceUpdate = function (root) {\n  Object.keys(cache).forEach(function (url) {\n    if (url.indexOf(root) === 0) {\n      updateBody(url);\n    }\n  });\n};\n\nexports.triggerChange = triggerChange;\n\nexports.setPluginMgr = function (mgr) {\n  pluginMgr = mgr;\n};\n"
  },
  {
    "path": "lib/util/index.js",
    "content": "var http = require('http');\nvar path = require('path');\nvar os = require('os');\nvar fs = require('fs');\nvar vm = require('vm');\nvar net = require('net');\nvar crypto = require('crypto');\nvar fse = require('fs-extra2');\nvar qs = require('querystring');\nvar extend = require('extend');\nvar LRU = require('lru-cache');\nvar iconv = require('iconv-lite');\nvar zlib = require('zlib');\nvar dns = require('dns');\nvar mime = require('mime');\nvar PipeStream = require('pipestream');\nvar protoMgr = require('../rules/protocols');\nvar protocols = protoMgr.protocols;\nvar logger = require('./logger');\nvar config = require('../config');\nvar isUtf8 = require('./is-utf8');\nvar fileMgr = require('./file-mgr');\nvar httpMgr = require('./http-mgr');\nvar ReplacePatternTransform = require('./replace-pattern-transform');\nvar parseQuery = require('./parse-query');\nvar common = require('./common');\nvar proc = require('./process');\nvar parseUrl = require('./parse-url');\nvar h2Consts = config.enableH2 ? require('http2').constants : {};\n\nvar uid = config.uid;\nvar supportsBr = common.supportsBr;\nvar isLocalIp = common.isLocalIp;\nvar getStat = common.getStat;\nvar toBuffer = common.toBuffer;\nvar pendingFiles = {};\nvar localIpCache = new LRU({ max: 120 });\nvar LOCALHOST = '127.0.0.1';\nvar aliasProtocols = protoMgr.aliasProtocols;\nvar CONTEXT = vm.createContext();\nvar END_SLASH_RE = /[/\\\\]$/;\nvar GEN_URL_RE = /^\\s*(?:https?:)?\\/\\/\\w[^\\s]*\\s*$/i;\nvar CORS_KEY_RE = /^(?:enable|use-credentials|useCredentials|credentials)$/i;\nvar NON_LATIN1_RE = /[^\\x00-\\xFF]/;\nvar SCRIPT_START = toBuffer('<script');\nvar SCRIPT_END = toBuffer('</script>');\nvar STYLE_START = toBuffer('<style>');\nvar STYLE_END = toBuffer('</style>');\nvar PROXY_RE = /^x?(?:socks|https?-proxy|proxy|internal(?:-https)?-proxy)$/;\nvar SEP_RE = /[|&]/;\nvar ctxTimer;\nvar END_RE = /[/\\\\]$/;\nvar EXPIRED_SEC = -123456;\nvar EXP_COOKIE = { maxAge: EXPIRED_SEC, path: '/' };\nvar EXP_SECURE_COOKIE = { maxAge: EXPIRED_SEC, path: '/', secure: true };\nvar EXP_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/' };\nvar EXP_SECURE_COOKIE_D = { maxAge: EXPIRED_SEC, path: '/', secure: true };\nvar resetContext = function () {\n  ctxTimer = null;\n  CONTEXT = vm.createContext();\n};\nvar TIMEOUT_ERR = common.TIMEOUT_ERR;\nvar SUB_MATCH_RE = /\\$[&\\d]/;\nvar PROTO_NAME_RE = /^([\\w.-]+):\\/\\//;\nvar replacePattern = ReplacePatternTransform.replacePattern;\nvar cryptoConsts = crypto.constants || {};\nvar EMPTY_BUFFER = toBuffer('');\nvar encodeHtml = common.encodeHtml;\nvar lowerCaseify = common.lowerCaseify;\nvar removeIPV6Prefix = common.removeIPV6Prefix;\nvar hasBody = common.hasBody;\nvar hasProtocol = common.hasProtocol;\nvar removeProtocol = common.removeProtocol;\nvar getTlsOptions = common.getTlsOptions;\nvar isUrl = common.isUrl;\nvar joinIpPort = common.joinIpPort;\nvar getContentEncoding = common.getContentEncoding;\nvar getUnzipStream = common.getUnzipStream;\nvar toLowerCase = common.toLowerCase;\nvar safeEncodeURIComponent = common.safeEncodeURIComponent;\nvar encodeNonLatin1Char = common.encodeNonLatin1Char;\nvar toUpperCase = common.toUpperCase;\nvar getCharset = common.getCharset;\nvar hasRequestBody = common.hasRequestBody;\nvar parseRawJson = common.parseRawJson;\nvar getMatcher = common.getMatcher;\nvar getMatcherValue = common.getMatcherValue;\nvar getRemoteAddr = common.getRemoteAddr;\nvar getRemotePort = common.getRemotePort;\nvar workerIndex = process.env && process.env.workerIndex;\nvar INTERNAL_ID = Date.now().toString(16) +  Math.floor(Math.random() * 10000000).toString(16);\nvar pluginMgr;\nvar RESOLVE_KEY_RE = /^re[qs]Merge:\\/\\//;\nvar CONTROL_RE =\n  /[\\u001e\\u001f\\u200e\\u200f\\u200d\\u200c\\u202a\\u202d\\u202e\\u202c\\u206e\\u206f\\u206b\\u206a\\u206d\\u206c]+/g;\nvar MULTI_LINE_VALUE_RE =\n  /^[^\\n\\r\\S]*(```+)[^\\n\\r\\S]*(\\S+)[^\\n\\r\\S]*[\\r\\n](?:([\\s\\S]*?)[\\r\\n])??[^\\n\\r\\S]*\\1\\s*$/gm;\nvar SPACE_RE = /\\s/;\nvar PATH_RE = /^[\\w./-]+$/;\nvar SLASH_RE = /[\\\\/]/;\nvar ROOT_STYLE = ':root{background-color:#121212;color:#f0f0f0;}';\nvar THEME_STYLE = '<style>@media (prefers-color-scheme: dark) {:not([data-theme=\"light\"])'\n  + ROOT_STYLE + '}\\n' + '[data-theme=\"dark\"]' + ROOT_STYLE + '</style>\\n';\n\nvar SNI_PLUGIN_HEADER = 'x-whistle-sni-plugin-' + uid;\nvar INTERNAL_ID_HEADER = 'x-whistle-internal-id';\nvar TEMP_TUNNEL_DATA_HEADER = 'x-whistle-tunnel-data-' + uid;\nvar TUNNEL_DATA_HEADER = 'x-whistle-tunnel-data';\nvar FWD_HOST_HEADER = 'x-forwarded-host';\nvar FWD_PROPS_HEADER = 'x-whistle-forwarded-props';\nvar REAL_HOST_HEADER = common.REAL_HOST_HEADER;\nvar HTTPS_PROTO_HEADER = 'x-forwarded-proto';\nvar clientIpKey = common.CLIENT_IP_HEADER;\nvar clientInfoKey = config.CLIENT_INFO_HEADER;\n\nexports.SNI_PLUGIN_HEADER = SNI_PLUGIN_HEADER;\nexports.INTERNAL_ID_HEADER = INTERNAL_ID_HEADER;\nexports.TEMP_TUNNEL_DATA_HEADER = TEMP_TUNNEL_DATA_HEADER;\nexports.TUNNEL_DATA_HEADER = TUNNEL_DATA_HEADER;\nexports.REAL_HOST_HEADER = REAL_HOST_HEADER;\nexports.HTTPS_PROTO_HEADER = HTTPS_PROTO_HEADER;\nexports.ADDITIONAL_HEAD = 'x-whistle-additional-headers';\n\nworkerIndex = workerIndex >= 0 ? common.padLeft(config.workerIndex, 3) : '';\n\n\nexports.sendRes = common.sendRes;\nexports.THEME_STYLE = THEME_STYLE;\nexports.encodeNonLatin1Char = encodeNonLatin1Char;\nexports.isJson = common.isJson;\nexports.encodeURIComponent = safeEncodeURIComponent;\nexports.getStat = getStat;\nexports.toLowerCase = toLowerCase;\nexports.toUpperCase = toUpperCase;\nexports.getCharset = getCharset;\nexports.getContentEncoding = getContentEncoding;\nexports.getUnzipStream = getUnzipStream;\nexports.hasRequestBody = hasRequestBody;\nexports.TIMEOUT_ERR = TIMEOUT_ERR;\nexports.getTlsOptions = getTlsOptions;\nexports.encodeHtml = encodeHtml;\nexports.hasProtocol = hasProtocol;\nexports.removeProtocol = removeProtocol;\nexports.parseRawJson = parseRawJson;\nexports.getMatcherValue = getMatcherValue;\nexports.setProtocol = common.setProtocol;\nexports.getProtocol = common.getProtocol;\nexports.replaceProtocol = common.replaceProtocol;\nexports.getMethod = common.getMethod;\nexports.sendGzip = common.sendGzip;\nexports.sendGzipText = common.sendGzipText;\nexports.getPureUrl = common.getPureUrl;\nexports.isTunnelHost = common.isTunnelHost;\nexports.joinIpPort = joinIpPort;\nexports.isWebSocket = common.isWebSocket;\nexports.wrapRuleValue = common.wrapRuleValue;\nexports.createTransform = common.createTransform;\nexports.readFileSync = common.readFileTextSync;\nexports.parseHeaders = common.parseHeaders;\nexports.connect = common.connect;\nexports.isUrl = isUrl;\nexports.workerIndex = workerIndex;\nexports.proc = proc;\nexports.INTERNAL_ID = INTERNAL_ID;\n// 避免属性被 stringify ，减少冗余数据传给前端\nexports.PLUGIN_VALUES = Symbol('values');\nexports.PLUGIN_MENU_CONFIG = Symbol('menuConfig');\nexports.PLUGIN_INSPECTOR_CONFIG = Symbol('inspectorConfig');\nexports.drain = require('./drain');\nexports.isWin = process.platform === 'win32';\nexports.isUtf8 = isUtf8;\nexports.WhistleTransform = require('./whistle-transform');\nexports.ReplacePatternTransform = ReplacePatternTransform;\nexports.replacePattern = replacePattern;\nexports.ReplaceStringTransform = require('./replace-string-transform');\nexports.SpeedTransform = require('./speed-transform');\nexports.FileWriterTransform = require('./file-writer-transform');\nexports.getServer = require('hagent').getServer;\nexports.parseUrl = parseUrl;\nexports.parseQuery = parseQuery;\nexports.localIpCache = localIpCache;\nexports.getRemoteAddr = getRemoteAddr;\nexports.getRemotePort = getRemotePort;\nexports.listenerCount = require('./patch').listenerCount;\nexports.EMPTY_BUFFER = EMPTY_BUFFER;\n\nvar NOT_SUPPORTED_ERR = new Error('Unsupported');\nNOT_SUPPORTED_ERR.code = 502;\n\nfunction request(options, callback) {\n  if (options && options.isInternalReq) {\n    return callback && callback(NOT_SUPPORTED_ERR, '', '');\n  }\n  return httpMgr.request(common.setInternalOptions(options, config, true), callback);\n}\n\nexports.request = request;\n\nfunction getInlineKey(key, file) {\n  return file ? key + '\\n\\r' + file : key;\n}\n\nexports.getInlineKey = getInlineKey;\n\nfunction resolveInlineValues(str, inlineValues, file) {\n  str = str && str.replace(CONTROL_RE, '').trim();\n  if (!str || str.indexOf('```') === -1) {\n    return str;\n  }\n  return str.replace(MULTI_LINE_VALUE_RE, function (_, __, key, value) {\n    key = getInlineKey(key, file);\n    if (inlineValues && inlineValues[key] == null) {\n      inlineValues[key] = value || '';\n    }\n    return '';\n  });\n}\n\nexports.resolveInlineValues = resolveInlineValues;\n\nexports.getPluginFile = function(name) {\n  return 'Plugin: ' + name;\n};\n\nexports.toPrivateValues = function(vals, file) {\n  if (!vals) {\n    return vals;\n  }\n  var keys = Object.keys(vals);\n  if (!keys.length) {\n    return vals;\n  }\n  var result = {};\n  keys.forEach(function(key) {\n    result[getInlineKey(key, file)] = vals[key];\n  });\n  return result;\n};\n\nfunction setSecureOptions(options) {\n  var secureOptions = cryptoConsts.SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;\n  if (secureOptions) {\n    options.secureOptions = secureOptions;\n  }\n  return options;\n}\n\nexports.setSecureOptions = setSecureOptions;\n\nfunction noop(_) {\n  return _;\n}\n\nexports.noop = noop;\n\nfunction isCiphersError(e) {\n  var c = e.code;\n  return (\n    c === 'EPROTO' || c === 'ERR_SSL_BAD_ECPOINT' || c === 'ERR_SSL_VERSION_OR_CIPHER_MISMATCH' ||\n    String(e.message).indexOf('disconnected before secure TLS connection was established') !== -1\n  );\n}\n\nexports.isCiphersError = isCiphersError;\n\nexports.setFramesMode = function(headers, enable) {\n  if (enable) {\n    headers['x-whistle-frames-mode'] = '1';\n  } else {\n    delete headers['x-whistle-frames-mode'];\n  }\n};\n\nfunction getScriptProps(props) {\n  var result = '';\n  if (props['use-credentials'] || props.useCredentials){\n    result += ' crossorigin=\"use-credentials\"';\n  } else if (props.anonymous) {\n    result += ' crossorigin=\"anonymous\"';\n  } else if (props.crossorigin) {\n    result += ' crossorigin';\n  }\n  if (props.defer) {\n    result += ' defer';\n  }\n  if (props.async) {\n    result += ' async';\n  }\n  if (props.nomodule) {\n    result += ' nomodule';\n  }\n  if (props.module) {\n    result += ' type=\"module\"';\n  } else if (props.importmap) {\n    result += ' type=\"importmap\"';\n  } else if (props.speculationrules) {\n    result += ' type=\"speculationrules\"';\n  }\n  return result;\n}\n\nfunction wrapJs(js, charset, isUrl, props) {\n  if (!js) {\n    return '';\n  }\n  if (isUrl) {\n    return toBuffer('<script' + getScriptProps(props) + ' src=\"' + js + '\"></script>', charset);\n  }\n  return Buffer.concat([SCRIPT_START, toBuffer(getScriptProps(props) + '>'), toBuffer(js, charset), SCRIPT_END]);\n}\n\nfunction wrapCss(css, charset, isUrl) {\n  if (!css) {\n    return '';\n  }\n  if (isUrl) {\n    return toBuffer('<link rel=\"stylesheet\" href=\"' + css + '\" />', charset);\n  }\n  return Buffer.concat([STYLE_START, toBuffer(css, charset), STYLE_END]);\n}\n\nvar MAX_LEN = 1024 * 1024 * 5;\n\nexports.getLatestVersion =  function(registry, cb, field) {\n  if (registry && typeof registry !== 'string') {\n    var name = registry.moduleName;\n    registry = registry.registry  ||\n      (registry.installRegistry && registry.installRegistry[0]) ||\n      'https://registry.npmjs.org';\n    registry = registry.replace(/\\/$/, '') + '/' + name;\n  }\n  if (!registry) {\n    return cb();\n  }\n  request(\n    {\n      url: registry,\n      maxLength: MAX_LEN,\n      responseType: 'json'\n    },\n    function (_, body) {\n      if (isString(field)) {\n        body = body && body[field];\n      } else {\n        body = body && body['dist-tags'];\n        body = body && body['latest'];\n      }\n      cb(body);\n    }\n  );\n};\n\nexports.getRegistry = function(pkg) {\n  var registry = pkg.whistleConfig && pkg.whistleConfig.registry;\n  return common.getRegistry(registry);\n};\nexports.isEmptyObject = common.isEmptyObject;\nexports.isGroup = common.isGroup;\nexports.addTrailerNames = common.addTrailerNames;\nexports.removeIllegalTrailers = common.removeIllegalTrailers;\nexports.isHead = common.isHead;\nexports.hasBody = hasBody;\n\nvar ESTABLISHED_CTN =\n  'HTTP/1.1 200 Connection Established\\r\\nProxy-Agent: ' +\n  config.appName +\n  '\\r\\n\\r\\n';\nexports.setEstablished = function (socket) {\n  socket.write(ESTABLISHED_CTN);\n};\n\nvar PORT_RE = /:\\d*$/;\n\nexports.changePort = function(url, port) {\n  var protocol = '';\n  var index = url.indexOf('://');\n  if (index !== -1) {\n    index += 3;\n    protocol = url.substring(0, index);\n    url = url.substring(index);\n  }\n  index = url.indexOf('/');\n  if (index != -1) {\n    var host = url.substring(0, index);\n    if (net.isIPv6(host)) {\n      host = '[' + host + ']';\n    } else {\n      host = host.replace(PORT_RE, '');\n    }\n    url = host + ':' + port + url.substring(index);\n  }\n  return protocol + url;\n};\n\nfunction handleStatusCode(statusCode, headers) {\n  if (statusCode == 401) {\n    headers['www-authenticate'] = 'Basic realm=User Login';\n  } else if (statusCode == 407) {\n    headers['proxy-authenticate'] = 'Basic realm=User Login';\n  }\n  return headers;\n}\n\nexports.handleStatusCode = handleStatusCode;\n\nfunction getStatusCode(statusCode) {\n  statusCode |= 0;\n  return statusCode < 100 || statusCode > 999 ? 0 : statusCode;\n}\n\nexports.getStatusCode = getStatusCode;\n\nfunction compare(v1, v2) {\n  return v1 == v2 ? 0 : v1 > v2 ? -1 : 1;\n}\n\nexports.compare = compare;\n\nvar scriptCache = {};\nvar VM_OPTIONS = {\n  displayErrors: false,\n  timeout: 60\n};\nvar MAX_SCRIPT_SIZE = 1024 * 256;\nvar MAX_SCRIPT_CACHE_COUNT = 64;\nvar MIN_SCRIPT_CACHE_COUNT = 32;\n\nfunction getScript(content) {\n  content = content.trim();\n  var len = content.length;\n  if (!len || len > MAX_SCRIPT_SIZE) {\n    return;\n  }\n\n  var script = scriptCache[content];\n  delete scriptCache[content];\n\n  var list = Object.keys(scriptCache);\n  if (list.length > MAX_SCRIPT_CACHE_COUNT) {\n    list = list\n      .map(function (content) {\n        var script = scriptCache[content];\n        script.content = content;\n        return script;\n      })\n      .sort(function (a, b) {\n        return compare(a.time, b.time);\n      })\n      .splice(0, MIN_SCRIPT_CACHE_COUNT);\n\n    scriptCache = {};\n    list.forEach(function (script) {\n      scriptCache[script.content] = {\n        script: script.script,\n        time: script.time\n      };\n    });\n  }\n\n  script = scriptCache[content] = script || {\n    script: new vm.Script('(function(){\\n' + content + '\\n})()')\n  };\n  script.time = Date.now();\n\n  return script.script;\n}\n\nfunction clearContext() {\n  Object.keys(CONTEXT).forEach(function (key) {\n    delete CONTEXT[key];\n  });\n  if (!ctxTimer) {\n    ctxTimer = setTimeout(resetContext, 30000);\n  }\n}\n\nexports.execScriptSync = function(script, context) {\n  try {\n    if ((script = getScript(script))) {\n      CONTEXT.console = {};\n      ['fatal', 'error', 'warn', 'info', 'log', 'debug'].forEach(function (\n        level\n      ) {\n        CONTEXT.console[level] = logger[level];\n      });\n      Object.keys(context).forEach(function (key) {\n        CONTEXT[key] = context[key];\n      });\n      script.runInContext(CONTEXT, VM_OPTIONS);\n    }\n    return true;\n  } catch (e) {\n    logger.error(e);\n  } finally {\n    clearContext();\n  }\n};\n\nfunction checkWriterFile(file, callback, force) {\n  if (force) {\n    return callback(true);\n  }\n  getStat(file, function (err) {\n    if (!err || err.code === 'ENOTDIR') {\n      return callback();\n    }\n    if (err.code === 'ENOENT') {\n      return callback(true);\n    }\n    callback(err);\n  });\n}\n\nfunction getFileWriter(file, callback, force) {\n  if (!file) {\n    return callback();\n  }\n  if (END_RE.test(file)) {\n    file = path.join(file, 'index.html');\n  }\n  if (!force && pendingFiles[file]) {\n    return callback();\n  }\n  var execCb = function (writer) {\n    delete pendingFiles[file];\n    callback(writer);\n  };\n  pendingFiles[file] = 1;\n  checkWriterFile(file, function (notExists) {\n    if (!notExists) {\n      return execCb();\n    }\n    fse.ensureFile(file, function (err) {\n      if (err) {\n        logger.error(err);\n        return execCb();\n      }\n      execCb(fs.createWriteStream(file).on('error', logger.error));\n    });\n  },\n    force\n  );\n}\n\nfunction handleCallback(list, fn, callback) {\n  if (!Array.isArray(list)) {\n    list = [list];\n  }\n  var len = list.length;\n  if (!len) {\n    return callback();\n  }\n  var result = [];\n  list.forEach(function(item, i) {\n    fn(item, function(value) {\n      result[i] = value;\n      if (--len === 0) {\n        callback.apply(null, result);\n      }\n    });\n  });\n}\n\nexports.getFileWriters = function (files, callback, force) {\n  handleCallback(files, function(file, cb) {\n    getFileWriter(file, cb, force);\n  }, callback);\n};\nexports.toBuffer = toBuffer;\n\nfunction getErrorStack(err) {\n  if (!err) {\n    return '';\n  }\n\n  var stack;\n  try {\n    stack = err.stack;\n  } catch (e) {}\n  stack = stack || err.message || err;\n  var result = [\n    'From: ' + config.appName + '@' + config.version,\n    'Node: ' + process.version,\n    'Host: ' + hostname,\n    'Date: ' + formatDate(),\n    stack\n  ];\n  return result.join('\\r\\n');\n}\n\nexports.getErrorStack = getErrorStack;\n\nfunction formatDate(now) {\n  now = now || new Date();\n  return now.toLocaleString();\n}\n\nexports.formatDate = formatDate;\n\nvar REG_EXP_RE = /^\\/(.+)\\/(i?u?|ui)$/;\n\nfunction isRegExp(regExp) {\n  return REG_EXP_RE.test(regExp);\n}\n\nexports.isRegExp = isRegExp;\n\nvar ORIG_REG_EXP = /^\\/(.+)\\/([igmu]{0,4})$/;\nvar FLAGS_RE = /[igmu]{2}/;\n\nfunction isOriginalRegExp(regExp) {\n  if (!ORIG_REG_EXP.test(regExp) || FLAGS_RE.test(regExp.$2)) {\n    return false;\n  }\n\n  return true;\n}\nexports.isOriginalRegExp = isOriginalRegExp;\n\nfunction toOriginalRegExp(regExp) {\n  regExp = ORIG_REG_EXP.test(regExp);\n  try {\n    regExp = regExp && new RegExp(RegExp.$1, RegExp.$2);\n  } catch (e) {\n    regExp = null;\n  }\n  return regExp;\n}\nexports.toOriginalRegExp = toOriginalRegExp;\n\nexports.emitError = function (obj, err) {\n  if (obj) {\n    obj.once('error', noop);\n    obj.emit('error', err || new Error('Unknown'));\n  }\n};\n\nexports.startWithList = function (buf, subBuf, start) {\n  var len = subBuf.length;\n  if (!len) {\n    return false;\n  }\n\n  start = start || 0;\n  for (var i = 0; i < len; i++) {\n    if (buf[i + start] != subBuf[i]) {\n      return false;\n    }\n  }\n\n  return true;\n};\n\nexports.endWithList = function (buf, subBuf, end) {\n  var subLen = subBuf.length;\n  if (!subLen) {\n    return false;\n  }\n  if (!(end >= 0)) {\n    end = buf.length - 1;\n  }\n\n  for (var i = 0; i < subLen; i++) {\n    if (subBuf[subLen - i - 1] != buf[end - i]) {\n      return false;\n    }\n  }\n\n  return true;\n};\n\nfunction isEnable(req, name) {\n  return req.enable[name] && !req.disable[name];\n}\n\nexports.isEnable = isEnable;\n\nexports.isDisable = function(req, name) {\n  return req.disable[name] && !req.enable[name];\n};\n\nexports.formatUrl = common.formatUrl;\n\nexports.isKeepClientId = function(req, proxyUrl) {\n  if (isEnable(req, 'keepClientId')) {\n    return true;\n  }\n  var disable = req.disable;\n  if (disable.clientId || disable.clientID) {\n    return false;\n  }\n  var enable = req.enable;\n  return enable.clientId || enable.clientID || proxyUrl;\n};\n\nexports.getInternalHost = function (req, host) {\n  if (isEnable(req, 'useLocalHost')) {\n    return 'local.wproxy.org';\n  }\n  if (host && isEnable(req, 'useSafePort')) {\n    var index = host.indexOf(':');\n    if (index !== -1) {\n      host = host.substring(0, index);\n    }\n    host += ':8899';\n  }\n  return host;\n};\n\nfunction isAuthCapture(req) {\n  var e = req.enable || '';\n  var d = req.disable || '';\n  return (\n    (e.authCapture || e.authIntercept) && !d.authCapture && !d.authIntercept\n  );\n}\n\nexports.isAuthCapture = isAuthCapture;\n\nexports.toRegExp = function toRegExp(regExp, ignoreCase) {\n  regExp = REG_EXP_RE.test(regExp);\n  try {\n    regExp = regExp && new RegExp(RegExp.$1, ignoreCase ? 'i' : RegExp.$2);\n  } catch (e) {\n    regExp = null;\n  }\n  return regExp;\n};\n\nvar isString = common.isString;\nvar getFullUrl = common.getFullUrl;\nexports.isString = isString;\nexports.getFullUrl = getFullUrl;\n\nfunction disableCSP(headers) {\n  delete headers['content-security-policy'];\n  delete headers['content-security-policy-report-only'];\n  delete headers['x-content-security-policy'];\n  delete headers['x-content-security-policy-report-only'];\n  delete headers['x-webkit-csp'];\n}\n\nexports.disableCSP = disableCSP;\n\nvar interfaces = os.networkInterfaces();\nvar hostname = os.hostname();\nvar simpleHostname = '';\nvar cpus = os.cpus();\nvar addressList = [];\nvar usiTimer;\n\nfunction updateSystyemInfo() {\n  clearTimeout(usiTimer);\n  interfaces = os.networkInterfaces();\n  hostname = os.hostname();\n  addressList = [];\n  common.walkInterfaces(function (info) {\n    addressList.push(info.address.toLowerCase());\n  });\n  usiTimer = setTimeout(updateSystyemInfo, 36000);\n}\n\nupdateSystyemInfo();\nprocess.on('w2NetworkInterfacesChange', updateSystyemInfo);\n\nif (isString(hostname)) {\n  simpleHostname = hostname.replace(/[^\\w.-]+/g, '').substring(0, 20);\n  simpleHostname = simpleHostname ? simpleHostname + '.' : '';\n}\n\nfunction createHash(str) {\n  return crypto.createHash('sha256').update(str).digest('hex');\n}\n\nexports.createHash = createHash;\n\nvar clientId = [\n  hostname,\n  os.platform(),\n  os.release(),\n  os.arch(),\n  cpus.length,\n  cpus[0] && cpus[0].model,\n  config.clientId\n];\nclientId = config.clientId =\n  simpleHostname +\n  crypto\n    .createHmac('sha256', config.CLIENT_ID_HEADER)\n    .update(clientId.join('\\r\\n'))\n    .digest('base64');\nconfig.runtimeId =\n  simpleHostname +\n  crypto\n    .createHmac('sha256', config.CLIENT_ID_HEADER)\n    .update(clientId + '\\r\\n' + Math.random() + '\\r\\n' + Date.now())\n    .digest('base64') +\n  '/' +\n  config.port;\nconfig.runtimeHeaders = { 'x-whistle-runtime-id': config.runtimeId };\nconfig.pluginHeaders = { 'x-whistle-runtime-id': config.runtimeId };\nconfig.pluginHeaders[INTERNAL_ID_HEADER] = INTERNAL_ID;\nconfig.pluginHeaders[config.PLUGIN_HOOK_NAME_HEADER] = config.PLUGIN_HOOKS.UI;\n\nexports.setClientId = function (\n  headers,\n  enable,\n  disable,\n  clientIp,\n  isInternalProxy\n) {\n  if (disable && (disable.clientId || disable.clientID)) {\n    return;\n  }\n  enable = enable || '';\n  if (\n    enable.clientId ||\n    enable.clientID ||\n    isInternalProxy\n  ) {\n    var id = getClientId(headers);\n    if (\n      (enable.multiClient || isInternalProxy) &&\n      !enable.singleClient &&\n      !disable.multiClient\n    ) {\n      if (headers[config.CLIENT_ID_HEADER]) {\n        return;\n      }\n      if (!isLocalAddress(clientIp)) {\n        id += '/' + clientIp;\n      }\n    }\n    headers[config.CLIENT_ID_HEADER] = id;\n  }\n};\n\nfunction getClientId(headers) {\n  var id = headers[config.CLIENT_ID_HEADER];\n  var idKey = config.cidKey;\n  if (!idKey || (id && !config.overCidKey)) {\n    return id || clientId;\n  }\n  return headers[idKey] || id || clientId;\n}\n\nexports.getClientId = getClientId;\n\nexports.getUpdateUrl = common.getUpdateUrl;\n\nexports.getTunnelKey = function (conf) {\n  var tunnelKey = conf.tunnelKey || conf.tunnelKeys;\n  if (isString(tunnelKey)) {\n    tunnelKey = tunnelKey.toLowerCase().split(/[:,|]/);\n    tunnelKey = tunnelKey.map(trim).filter(noop);\n    return tunnelKey.slice(0, 10);\n  }\n};\n\nfunction getComposerClientId(headers) {\n  var clientId = headers[config.COMPOSER_CLIENT_ID_HEADER];\n  if (clientId) {\n    delete headers[config.COMPOSER_CLIENT_ID_HEADER];\n    return clientId;\n  }\n}\n\nexports.getComposerClientId = getComposerClientId;\n\nexports.removeClientId = function (headers) {\n  delete headers[config.CLIENT_ID_HEADER];\n};\n\nfunction networkInterfaces() {\n  return interfaces;\n}\n\nfunction getHostname() {\n  return hostname;\n}\n\nexports.networkInterfaces = networkInterfaces;\nexports.hostname = getHostname;\n\nfunction getProxyTunnelPath(req, isHttps) {\n  var host = req._phost && req._proxyTunnel && req.headers.host;\n  if (isString(host)) {\n    return host.indexOf(':') !== -1 ? host : host + ':' + (isHttps ? 443 : 80);\n  }\n}\nexports.getProxyTunnelPath = getProxyTunnelPath;\n\nfunction isLocalAddress(address) {\n  if (isLocalIp(address)) {\n    return true;\n  }\n  address = address.toLowerCase();\n  if (address[0] === '[') {\n    address = address.slice(1, -1);\n  }\n  if (address == '0:0:0:0:0:0:0:1') {\n    return true;\n  }\n  return localIpCache.get(address) || addressList.indexOf(address) !== -1;\n}\n\nexports.isLocalAddress = isLocalAddress;\n\nfunction isLocalHost(host) {\n  return host === 'localhost' || isLocalAddress(host);\n}\n\nexports.isLocalHost = isLocalHost;\n\nfunction parseHost(host) {\n  var index;\n  if (host[0] === '[') {\n    index = host.indexOf(']');\n    return [host.substring(1, index), host.substring(index + 2)];\n  }\n  index = host.indexOf(':');\n  if (index === -1 || host.indexOf(':', index + 1) !== -1) {\n    return [host, ''];\n  }\n  return [host.substring(0, index), host.substring(index + 1)];\n}\n\nexports.parseHost = parseHost;\n\nfunction compareUrl(url, fullUrl) {\n  url = common.getAbsUrl(url, fullUrl);\n  if (url === fullUrl) {\n    return true;\n  }\n  try {\n    return url === decodeURIComponent(fullUrl);\n  } catch (e) {}\n}\n\nexports.compareUrl = compareUrl;\n\nfunction getPath(url, noProtocol) {\n  if (url) {\n    url = common.getPureUrl(url);\n    var index = noProtocol ? -1 : url.indexOf('://');\n    url = index > -1 ? url.substring(index + 3) : url;\n  }\n\n  return url;\n}\n\nexports.getPath = getPath;\n\nfunction getFilename(url) {\n  if (typeof url == 'string' && (url = getPath(url).trim())) {\n    var index = url.lastIndexOf('/');\n    if (index != -1) {\n      url = url.substring(index + 1);\n    } else {\n      url = null;\n    }\n  } else {\n    url = null;\n  }\n\n  return url || 'index.html';\n}\n\nexports.getFilename = getFilename;\n\nfunction disableReqCache(headers) {\n  delete headers['if-modified-since'];\n  delete headers['if-none-match'];\n  delete headers['last-modified'];\n  delete headers.etag;\n\n  headers['pragma'] = 'no-cache';\n  headers['cache-control'] = 'no-cache';\n}\n\nexports.disableReqCache = disableReqCache;\n\nfunction disableResStore(headers) {\n  headers['cache-control'] = 'no-store';\n  headers['expires'] = new Date(Date.now() - 60000000).toGMTString();\n  headers['pragma'] = 'no-cache';\n  delete headers.tag;\n}\n\nexports.disableResStore = disableResStore;\n\nfunction notNull(value) {\n  return value != null;\n}\n\nvar HTTP_PROTO_RE = /^(?:ws|http)s?:/;\n\nfunction parsePathReplace(urlPath, params, delPaths) {\n  if ((!params && !delPaths) || !HTTP_PROTO_RE.test(urlPath)) {\n    return;\n  }\n  var index = urlPath.indexOf('://');\n  if (index == -1) {\n    return;\n  }\n  index = urlPath.indexOf('/', index + 3) + 1;\n  if (!index) {\n    return;\n  }\n  var curPath = urlPath.substring(index);\n  params && Object.keys(params).forEach(function (pattern) {\n    var value = params[pattern];\n    value = value == null ? '' : value + '';\n    if (isOriginalRegExp(pattern) && (pattern = toOriginalRegExp(pattern))) {\n      curPath = curPath.replace(pattern, value);\n    } else if (pattern) {\n      curPath = curPath.split(pattern).join(value);\n    }\n  });\n  if (curPath && delPaths) {\n    var query = '';\n    var qIdx = curPath.indexOf('?');\n    if (qIdx !== -1) {\n      query = curPath.substring(qIdx);\n      curPath = curPath.substring(0, qIdx);\n    }\n    if (curPath) {\n      if (delPaths.all) {\n        delete delPaths.all;\n        curPath = query;\n      } else {\n        curPath = curPath.split('/');\n        var len = curPath.length;\n        var last = delPaths.last;\n        if (last) {\n          delete delPaths.last;\n          delPaths[len - 1] = 1;\n        }\n        Object.keys(delPaths).forEach(function (key) {\n          key = +key;\n          key = key < 0 ? len + key : key;\n          if (key >= 0 && key < len) {\n            curPath[key] = null;\n          }\n        });\n\n        curPath = curPath.filter(notNull);\n        if (last && curPath[curPath.length - 1]) {\n          curPath.push('');\n        }\n        curPath = curPath.join('/');\n      }\n    }\n    curPath += query;\n  }\n  var newUrl = urlPath.substring(0, index) + curPath;\n  return newUrl === urlPath ? null : newUrl;\n}\n\nexports.parsePathReplace = parsePathReplace;\n\nfunction wrapResponse(res, realUrl) {\n  var newRes = common.createTransform();\n  newRes.statusCode = res.statusCode;\n  newRes.rawHeaderNames = res.rawHeaderNames;\n  newRes.headers = lowerCaseify(res.headers);\n  newRes.headers['x-server'] = config.appName;\n  res.body != null &&\n    newRes.push(Buffer.isBuffer(res.body) ? res.body : String(res.body));\n  newRes.push(null);\n  newRes.isCustomRes = true;\n  newRes.realUrl = realUrl;\n  return newRes;\n}\n\nexports.wrapResponse = wrapResponse;\n\nfunction wrapGatewayError(body) {\n  return wrapResponse({\n    statusCode: 502,\n    headers: {\n      'content-type': 'text/html; charset=utf8'\n    },\n    body: body\n      ? '<pre>\\n' +\n        encodeHtml(body) +\n        '\\n\\n\\n<a href=\"javascript:;\" onclick=\"location.reload()\"' +\n        '>Reload this page</a>\\n</pre>'\n      : ''\n  });\n}\n\nexports.wrapGatewayError = wrapGatewayError;\n\nfunction sendStatusCodeError(cltRes, svrRes) {\n  delete svrRes.headers['content-length'];\n  cltRes.writeHead(502, svrRes.headers);\n  cltRes.src(wrapGatewayError('Invalid status code: ' + svrRes.statusCode));\n}\nexports.sendStatusCodeError = sendStatusCodeError;\nexports.getQueryValue = function (value) {\n  if (value && typeof value === 'object') {\n    try {\n      return JSON.stringify(value) || '';\n    } catch (e) {}\n  }\n  return value || '';\n};\n\nfunction parseInlineJSON(text, isValue) {\n  if (!isValue || SPACE_RE.test(text)) {\n    return;\n  }\n  return parseQuery(text, null, null, true);\n}\n\nfunction _parseJSON(data, resolveKeys) {\n  if (typeof data === 'object') {\n    return data;\n  }\n  if (!isString(data) || !(data = data.trim())) {\n    return null;\n  }\n  return parsePureJSON(data, true) || common.parsePlainText(data, resolveKeys === true);\n}\n\nfunction parseJSON(data) {\n  return _parseJSON(data);\n}\n\nfunction parsePureJSON(data, isValue) {\n  return parseRawJson(data) || parseInlineJSON(data, isValue);\n}\n\nexports.parseJSON = parseJSON;\n\nfunction trim(text) {\n  return text && text.trim();\n}\n\nexports.trim = trim;\nexports.lowerCaseify = lowerCaseify;\n\nvar QUERY_PARAM_RE = /^[^\\\\/]+=/;\n\n\nfunction tryParseMatcher(text, rule) {\n  var matcher = !text && removeProtocol(getMatcher(rule), true);\n  if (!matcher || matcher.indexOf('=') === -1) {\n    return;\n  }\n  return parseQuery(matcher, null, null, true);\n}\n\nexports.parseRuleJson = function(rules, callback, req) {\n  handleCallback(rules, function(rule, cb) {\n    readRuleList(rule, cb, true, null, null, req);\n  }, callback);\n\n};\n\nfunction getTempFilePath(filePath, rule) {\n  var root = rule.root;\n  if (!root && common.TEMP_PATH_RE.test(filePath)) {\n    rule._suffix = RegExp.$2;\n    return path.join(config.TEMP_FILES_PATH, RegExp.$1);\n  }\n  return joinPath(root, decodePath(filePath));\n}\n\nfunction readRuleValue(rule, callback, checkUrl, needRawData, req) {\n  if (!rule) {\n    return callback();\n  }\n  if (rule.value) {\n    return callback(removeProtocol(rule.value, true));\n  }\n  var filePath = getMatcherValue(rule);\n  if (checkUrl && GEN_URL_RE.test(filePath)) {\n    return callback(filePath);\n  }\n  var opts = pluginMgr.resolveKey(filePath, rule, req);\n  var readFile;\n  if (opts) {\n    readFile = pluginMgr[needRawData ? 'requestBin' : 'requestText'];\n    return readFile(opts, callback);\n  }\n\n  filePath = getTempFilePath(filePath, rule);\n  if (!filePath) {\n    return callback();\n  }\n  readFile = fileMgr[needRawData ? 'readFile' : 'readFileText'];\n  readFile(filePath, callback);\n}\n\nfunction isGenUrl(value) {\n  return typeof value === 'string' && GEN_URL_RE.test(value);\n}\n\nvar CORS_RE = /^re[qs]Cors:\\/\\//;\n\nfunction isDeep(result) {\n  for (var i = 0, len = result.length; i < len; i++) {\n    if (result[i] === true) {\n      return true;\n    }\n  }\n}\n\nfunction indexOfQuote(matcher) {\n  return matcher[0] === '\"' ? matcher.indexOf('\"=') : -1;\n}\n\nfunction getMatcherJson(rule, value) {\n  var matcher = rule.rawMatcher;\n  var eq = matcher.indexOf('=');\n  if (eq === -1) {\n    return;\n  }\n  var and = matcher.indexOf('&');\n  if (and !== -1 && and > eq) {\n    return;\n  }\n  matcher = removeProtocol(matcher, true);\n  var len = matcher.length - 1;\n  var first = matcher[0];\n  var last = matcher[len];\n  if (first === '{' && last === '}') {\n    return;\n  }\n  var index;\n  if (first === '(' && last === ')') {\n    matcher = matcher.substring(1, len);\n  } else {\n    if (!QUERY_PARAM_RE.test(matcher)) {\n      index = indexOfQuote(matcher);\n      if (index === -1) {\n        return;\n      }\n    } else if (first === '<' && last === '>') {\n      matcher = matcher.substring(1, len);\n    }\n  }\n  if (index == null) {\n    index = indexOfQuote(matcher);\n  }\n  var hasQuote;\n  if (index === -1) {\n    index =  matcher.indexOf('=');\n  } else {\n    index += 1;\n    hasQuote = true;\n  }\n  var key = matcher.substring(0, index);\n  value = value || removeProtocol(getMatcher(rule), true);\n  if (value.indexOf(key + '=')) {\n    return;\n  }\n  var result = {};\n  result[hasQuote ? key.substring(1, index - 1) : key] = value.substring(index + 1);\n  return result;\n}\n\nfunction readRuleList(rule, callback, isJson, charset, isHtml, req) {\n  if (!rule) {\n    return callback();\n  }\n  var len = rule.list && rule.list.length;\n  var isBin = protoMgr.isBinProtocol(rule.name);\n  var needRawData = isBin && !isJson;\n  if (!len) {\n    if (isJson) {\n      var val = removeProtocol(getMatcher(rule), true);\n      val = val && val.trim();\n      var json = getMatcherJson(rule, val) || parsePureJSON(val, QUERY_PARAM_RE.test(val));\n      if (json) {\n        return callback(json);\n      }\n    }\n    return readRuleValue(\n      rule,\n      isJson\n        ? function (value) {\n          callback(tryParseMatcher(value, rule) || _parseJSON(value, RESOLVE_KEY_RE.test(rule.matcher)));\n        }\n        : callback,\n        false,\n        needRawData,\n        req\n    );\n  }\n  var result = [];\n  var isJsHtml = isHtml && isBin === 2;\n  var isCssHtml = isHtml && isBin === 3;\n  var isRawList = rule.isRawList;\n  var execCallback = function () {\n    if (--len > 0) {\n      return;\n    }\n    if (isJson) {\n      var deepMerge = isDeep(result);\n      result = result.map(function(text, i) {\n        var item = rule.list[i];\n        return tryParseMatcher(text, item) || _parseJSON(text, item && RESOLVE_KEY_RE.test(item.matcher));\n      }).filter(noop);\n      if (result.length > 1) {\n        result.reverse();\n        if (typeof result[0] !== 'object') {\n          result[0] = {};\n        }\n        deepMerge && result.unshift(true);\n        callback(extend.apply(null, result));\n      } else {\n        callback(result[0]);\n      }\n    } else if (isRawList) {\n      callback(result);\n    } else if (isHtml) {\n      result = result.filter(noop);\n      callback(result.length ? result : '');\n    } else {\n      callback(fileMgr.joinData(result, !isBin, charset));\n    }\n  };\n  var isCors = CORS_RE.test(rule.matcher);\n  var checkUrl = isJsHtml || isCssHtml;\n  rule.list.forEach(function (r, i) {\n    if (isJson) {\n      var json = r.jsonObject;\n      var value;\n      if (json) {\n        r.jsonObject = undefined;\n      } else {\n        value = removeProtocol(getMatcher(r), true);\n        json = getMatcherJson(r, value);\n      }\n      if (json) {\n        result[i] = json;\n        return execCallback();\n      }\n      value = value && value.trim();\n      if (value) {\n        if (isCors) {\n          if (GEN_URL_RE.test(value)) {\n            json = { origin: value };\n          } else if (value === '*') {\n            json = { '*': '' };\n          } else if (CORS_KEY_RE.test(value)) {\n            json = { enable: true };\n          }\n        }\n        json = json || parsePureJSON(value, QUERY_PARAM_RE.test(value));\n        if (json) {\n          result[i] = json;\n          return execCallback();\n        }\n      }\n    }\n    readRuleValue(\n      r,\n      function (value) {\n        if (isHtml && value) {\n          var props = r.lineProps;\n          if (checkUrl) {\n            var isUrl = isGenUrl(value);\n            var wrap = isJsHtml ? wrapJs : wrapCss;\n            value = wrap(isUrl ? value.trim() : value, charset, isUrl, props);\n          } else {\n            value = toBuffer(value, charset);\n          }\n          var strictHtml = props.strictHtml;\n          var safeHtml = props.safeHtml;\n          if (strictHtml || safeHtml) {\n            value._strictHtml = strictHtml;\n            value._safeHtml = safeHtml;\n            r.lineStrict = strictHtml;\n            r.lineSafe = safeHtml;\n          }\n        }\n        result[i] = value;\n        execCallback();\n      },\n      checkUrl,\n      needRawData,\n      req\n    );\n  });\n}\n\nexports.getRuleValue = function(rules, callback, noBody, charset, isHtml, req) {\n  if (noBody || !rules) {\n    return callback();\n  }\n  handleCallback(rules, function(rule, cb) {\n    readRuleList(rule, cb, false, charset, isHtml, req);\n  }, callback);\n};\n\nfunction decodePath(path) {\n  path = getPath(path, true);\n  try {\n    return decodeURIComponent(path);\n  } catch (e) {\n    logger.error(e);\n  }\n\n  try {\n    return qs.unescape(path);\n  } catch (e) {\n    logger.error(e);\n  }\n\n  return path;\n}\n\nexports.getRuleFiles = function(rule, req) {\n  if (rule.key) {\n    return [];\n  }\n  var files = rule.files || [getPath(getUrl(rule))];\n  var rawFiles = rule.rawFiles || files;\n  var result = [];\n  files.forEach(function (file, i) {\n    var opts = pluginMgr.resolveKey(rawFiles[i], rule, req);\n    if (opts) {\n      result.push(opts);\n    } else if (file = getTempFilePath(file, rule)) {\n      file = fileMgr.convertSlash(file);\n      if (END_SLASH_RE.test(file)) {\n        result.push(file.slice(0, -1));\n        result.push(file + 'index.html');\n      } else {\n        result.push(file);\n      }\n    } else {\n      result.push(file);\n    }\n  });\n  return result;\n};\n\nexports.getWriteFilePath = function(rule) {\n  var filePath = getPath(getUrl(rule));\n  return filePath && joinPath(rule.root, decodePath(filePath));\n};\n\nfunction getUrl(rule) {\n  return common.trimUrl(rule && (common.getRuleValue(rule) || rule.url));\n}\n\nexports.rule = {\n  getMatcher: getMatcher,\n  getUrl: getUrl\n};\n\nexports.getUrlValue = function(rule) {\n  rule = getUrl(rule);\n  return rule && removeProtocol(rule, true);\n};\n\nfunction _getRawType(type) {\n  return typeof type === 'string' ? type.split(';')[0].toLowerCase() : '';\n}\n\nfunction getRawType(data) {\n  return _getRawType(data.headers && data.headers['content-type']);\n}\n\nexports.getRawType = getRawType;\n\nfunction getContentType(contentType) {\n  if (contentType && typeof contentType != 'string') {\n    contentType = contentType['content-type'] || contentType.contentType;\n  }\n  contentType = _getRawType(contentType);\n  if (!contentType) {\n    return;\n  }\n  if (contentType.indexOf('javascript') != -1) {\n    return 'JS';\n  }\n\n  if (contentType.indexOf('css') != -1) {\n    return 'CSS';\n  }\n\n  if (contentType.indexOf('html') != -1) {\n    return 'HTML';\n  }\n\n  if (contentType.indexOf('json') != -1) {\n    return 'JSON';\n  }\n\n  if (contentType.indexOf('xml') != -1) {\n    return 'XML';\n  }\n\n  if (contentType.indexOf('text/') != -1) {\n    return 'TEXT';\n  }\n\n  if (contentType.indexOf('image/') != -1) {\n    return 'IMG';\n  }\n}\n\nexports.getContentType = getContentType;\n\nfunction isText(contentType) {\n  contentType = contentType && getContentType(contentType);\n  return contentType && contentType !== 'IMG';\n}\n\nexports.isText = isText;\n\nfunction supportHtmlTransform(res, req) {\n  var headers = res.headers;\n  if (getContentType(headers) != 'HTML' || !hasBody(res, req)) {\n    return false;\n  }\n\n  var contentEncoding = getContentEncoding(headers);\n  //chrome新增了sdch压缩算法，对此类响应无法解码，deflate无法区分deflate还是deflateRaw\n  return !contentEncoding || contentEncoding == 'gzip' || contentEncoding === 'br';\n}\n\nexports.supportHtmlTransform = supportHtmlTransform;\n\nexports.getEnableEncoding = function(enable) {\n  if (!enable) {\n    return;\n  }\n  if (enable.br) {\n    return supportsBr ? 'br' : null;\n  }\n  if (enable.gzip) {\n    return 'gzip';\n  }\n  if (enable.deflate) {\n    return 'deflate';\n  }\n};\n\nfunction removeUnsupportsHeaders(headers, supportsDeflate) {\n  //只保留支持的zip格式：gzip、deflate\n  if (!headers || !headers['accept-encoding']) {\n    return;\n  }\n  if (config.noGzip) {\n    delete headers['accept-encoding'];\n    return;\n  }\n  var list = headers['accept-encoding'].trim().split(/\\s*,\\s*/g);\n  var acceptEncoding = [];\n  for (var i = 0, len = list.length; i < len; i++) {\n    var ae = list[i].toLowerCase();\n    if (ae && ((supportsDeflate && ae == 'deflate') || ae == 'gzip' || (supportsBr && ae === 'br'))) {\n      acceptEncoding.push(ae);\n    }\n  }\n\n  if ((acceptEncoding = acceptEncoding.join(', '))) {\n    headers['accept-encoding'] = acceptEncoding;\n  }\n}\n\nexports.removeUnsupportsHeaders = removeUnsupportsHeaders;\n\n\nfunction getZipStream(headers) {\n  switch (getContentEncoding(headers)) {\n  case 'gzip':\n    return zlib.createGzip();\n  case 'br':\n    return supportsBr && zlib.createBrotliCompress();\n  case 'deflate':\n    return zlib.createDeflate();\n  }\n}\n\nexports.isZip = function(encoding, chunk) {\n  if (encoding === 'gzip') {\n    return chunk[0] === 31 && (chunk[1] == null || chunk[1] === 139);\n  }\n  if (encoding === 'br') {\n    return supportsBr;\n  }\n  return encoding === 'deflate';\n};\n\n\n\nexports.getZipStream = getZipStream;\n\nexports.isWhistleTransformData = function (obj) {\n  if (!obj) {\n    return false;\n  }\n  return obj.speed > 0 || obj.delay > 0 || obj.top || obj.body || obj.bottom;\n};\n\nfunction getPipeIconvStream(headers) {\n  var pipeStream = new PipeStream();\n  var charset = getCharset(headers['content-type']);\n\n  if (charset) {\n    pipeStream.addHead(iconv.decodeStream(charset));\n    pipeStream.addTail(iconv.encodeStream(charset));\n  } else {\n    pipeStream.addHead(function (res, next) {\n      var buffer, iconvDecoder;\n\n      res.on('data', function (chunk) {\n        buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;\n        resolveCharset(buffer);\n      });\n      res.on('end', resolveCharset);\n\n      function resolveCharset(chunk) {\n        if (!charset) {\n          if (chunk && buffer.length < 25600) {\n            return;\n          }\n          charset = !buffer || isUtf8(buffer) ? 'utf8' : 'GB18030';\n        }\n        if (!iconvDecoder) {\n          iconvDecoder = iconv.decodeStream(charset);\n          next(iconvDecoder);\n        }\n        if (buffer) {\n          iconvDecoder.write(buffer);\n          buffer = null;\n        }\n        !chunk && iconvDecoder.end();\n      }\n    });\n\n    pipeStream.addTail(function (src, next) {\n      next(src.pipe(iconv.encodeStream(charset)));\n    });\n  }\n\n  return pipeStream;\n}\n\nexports.getPipeIconvStream = getPipeIconvStream;\n\nfunction getClientIpFH(headers, name) {\n  var val = headers[name];\n  if (!isString(val)) {\n    return '';\n  }\n  var index = val.indexOf(',');\n  if (index !== -1) {\n    val = val.substring(0, index);\n  }\n  val = removeIPV6Prefix(val.trim());\n  return net.isIP(val) && !isLocalAddress(val) ? val : '';\n}\n\nfunction getForwardedFor(headers) {\n  var ip = getClientIpFH(headers, clientIpKey);\n  var cipKey = config.cipKey;\n  if (cipKey && (!ip || config.overCipKey)) {\n    ip = getClientIpFH(headers, cipKey) || ip;\n  }\n  return ip;\n}\nexports.getForwardedFor = getForwardedFor;\n\nfunction getClientIp(req, ip) {\n  ip = ip || getForwardedFor(req.headers || {}) || getRemoteAddr(req);\n  return isLocalIp(ip) ? LOCALHOST : ip;\n}\n\nexports.getClientIp = getClientIp;\n\n\nfunction getClientPort(req) {\n  return common.getClientPort(req, config);\n}\n\nexports.getClientPort = getClientPort;\n\nexports.removeIPV6Prefix = removeIPV6Prefix;\n\nexports.isUrlEncoded = common.isUrlEncoded;\n\nfunction isJSONContent(req) {\n  if (!hasRequestBody(req)) {\n    return false;\n  }\n  return getContentType(req.headers) === 'JSON';\n}\n\nexports.isJSONContent = isJSONContent;\n\nfunction isProxyPort(proxyPort) {\n  return (\n    proxyPort == config.port ||\n    proxyPort == config.httpsPort ||\n    proxyPort == config.httpPort ||\n    proxyPort == config.socksPort ||\n    proxyPort == config.realPort\n  );\n}\n\nexports.isProxyPort = isProxyPort;\n\nexports.isLocalPHost = function(req, isHttps) {\n  var phost = req._phost;\n  var hostname = phost && phost.hostname;\n  if (!hostname || !isProxyPort(phost.port || (isHttps ? 443 : 80))) {\n    return false;\n  }\n  return isLocalHost(hostname);\n};\n\nvar MULTIPART_RE = /multipart/i;\nfunction isMultipart(req) {\n  return MULTIPART_RE.test(req.headers['content-type']);\n}\n\nexports.isMultipart = isMultipart;\n\nfunction getQueryString(url) {\n  var index = url.indexOf('?');\n  return index == -1 ? '' : url.substring(index + 1);\n}\n\nexports.getQueryString = getQueryString;\n\nfunction replaceQueryString(query, replaceQuery, delProps) {\n  if (replaceQuery && typeof replaceQuery != 'string') {\n    replaceQuery = qs.stringify(replaceQuery);\n  }\n  if (delProps ? (!query && !replaceQuery) : (!query || !replaceQuery)) {\n    return replaceQuery || query;\n  }\n\n  var queryList = [];\n  var params = {};\n  var curParams = {};\n  var name, value;\n  var parseKey = function (item) {\n    var index = item.indexOf('=');\n    if (index == -1) {\n      name = item;\n      value = '';\n    } else {\n      name = item.substring(0, index);\n      value = item.substring(index + 1);\n    }\n  };\n  var addValue = function(obj) {\n    var curVal = obj[name];\n    if (Array.isArray(curVal)) {\n      curVal.push(value);\n    } else if (curVal != null) {\n      curVal = [curVal, value];\n    }\n    obj[name] = curVal || value;\n  };\n  if (replaceQuery) {\n    replaceQuery = replaceQuery.split('&').map(function(item) {\n      parseKey(item);\n      if (!delProps || !delProps[name]) {\n        addValue(params);\n      }\n    });\n  }\n  if (query) {\n    query.split('&').map(function(item) {\n      parseKey(item);\n      if ((!delProps || !delProps[name]) && params[name] == null) {\n        addValue(curParams);\n      }\n    });\n  }\n  extend(curParams, params);\n\n  Object.keys(curParams).forEach(function(key) {\n    var val = curParams[key];\n    if (Array.isArray(val)) {\n      val.forEach(function(v) {\n        queryList.push(key + '=' + v);\n      });\n    } else {\n      queryList.push(key + '=' + val);\n    }\n  });\n\n  return queryList.join('&');\n}\n\nexports.replaceQueryString = replaceQueryString;\n\nexports.replaceUrlQueryString = function(url, queryString) {\n  if (!queryString) {\n    return url;\n  }\n  url = url || '';\n  var hashIndex = url.indexOf('#');\n  var hashString = '';\n  if (hashIndex != -1) {\n    hashString = url.substring(hashIndex);\n    url = url.substring(0, hashIndex);\n  }\n  queryString = replaceQueryString(getQueryString(url), queryString);\n\n  return (\n    url.replace(/\\?.*$/, '') +\n    (queryString ? '?' + queryString : '') +\n    hashString\n  );\n};\n\nexports.decodeBuffer = fileMgr.decode;\n\nfunction setHeaders(data, obj) {\n  if (!data.headers) {\n    data.headers = {};\n  }\n  Object.keys(obj).forEach(function (key) {\n    data.headers[key] = obj[key];\n  });\n  return data;\n}\n\nexports.setHeaders = setHeaders;\n\nfunction setHeader(data, name, value) {\n  if (!data.headers) {\n    data.headers = {};\n  }\n  data.headers[name] = value;\n  return data;\n}\n\nexports.setHeader = setHeader;\n\nfunction joinPath(root, dir) {\n  if (common.existsUpPath(dir)) {\n    return;\n  }\n  if (!root) {\n    return dir;\n  }\n  var fullPath = path.resolve(root, dir);\n  var slash = END_SLASH_RE.exec(dir || root);\n  return slash && !END_SLASH_RE.test(fullPath) ? fullPath + slash[0] : fullPath;\n}\n\nexports.joinPath = joinPath;\n\nfunction resolveProperties(list, result) {\n  result = result || {};\n  if (list) {\n    list\n      .map(getMatcherValue)\n      .join('|')\n      .split(SEP_RE)\n      .forEach(function (action) {\n        if (action) {\n          result[action] = true;\n        }\n      });\n  }\n  return result;\n}\n\nexports.resolveProperties = resolveProperties;\n\nexports.parseLineProps = function (str) {\n  str = str && removeProtocol(str, true);\n  if (!str) {\n    return;\n  }\n  var result = {};\n  str.split(SEP_RE).forEach(function (action) {\n    if (action) {\n      result[action] = true;\n    }\n  });\n  return result;\n};\n\nfunction resolveIgnore(ignore) {\n  var keys = Object.keys(ignore);\n  var exclude = {};\n  var ignoreAll, disableIgnoreAll;\n  ignore = {};\n  keys.forEach(function (name) {\n    if (name.indexOf('ignore.') === 0 || name.indexOf('ignore:') === 0) {\n      exclude[name.substring(7)] = 1;\n      return;\n    }\n    if (name.indexOf('-') === 0 || name.indexOf('!') === 0) {\n      name = name.substring(1);\n      if (name === '*') {\n        disableIgnoreAll = true;\n      } else {\n        exclude[name] = 1;\n      }\n      return;\n    }\n    name = name.replace('ignore|', '');\n    if (name === 'filter' || name === 'ignore') {\n      return;\n    }\n    if (\n      name === 'allRules' ||\n      name === 'allProtocols' ||\n      name === 'All' ||\n      name === '*'\n    ) {\n      ignoreAll = true;\n      return;\n    }\n    ignore[aliasProtocols[name] || name] = 1;\n  });\n  if (ignoreAll && !disableIgnoreAll) {\n    protocols.forEach(function (name) {\n      ignore[name] = 1;\n    });\n    keys = protocols;\n  } else {\n    keys = Object.keys(ignore);\n  }\n  keys.forEach(function (name) {\n    if (exclude[name]) {\n      delete ignore[name];\n    }\n  });\n  return {\n    ignoreAll: ignoreAll,\n    exclude: exclude,\n    ignore: ignore\n  };\n}\n\nfunction resolveFilter(ignore, filter) {\n  filter = filter || {};\n  var result = resolveIgnore(ignore);\n  ignore = result.ignore;\n  Object.keys(ignore).forEach(function (name) {\n    if (protocols.indexOf(name) === -1) {\n      filter['ignore|' + name] = true;\n    } else {\n      filter[name] = true;\n    }\n  });\n  Object.keys(result.exclude).forEach(function (name) {\n    filter['ignore:' + name] = 1;\n  });\n  if (result.ignoreAll) {\n    filter.allRules = 1;\n  }\n  return filter;\n}\n\nexports.resolveFilter = resolveFilter;\n\nexports.isIgnored = function (filter, name) {\n  return (\n    !filter['ignore:' + name] && (filter[name] || filter['ignore|' + name])\n  );\n};\n\nfunction exactIgnore(filter, rule) {\n  if (filter['ignore|' + 'pattern=' + rule.rawPattern]) {\n    return true;\n  }\n  if (filter['ignore|' + 'matcher=' + rule.matcher]) {\n    return true;\n  }\n  return filter['ignore|' + 'matcher=' + rule.rawMatcher];\n}\n\nexports.exactIgnore = exactIgnore;\n\nfunction notSkip(props, name) {\n  return props['-' + name] || props['!' + name];\n}\n\nexports.checkSkip = function(skip, rule, curUrl) {\n  if (skip['*'] && !notSkip(skip, '*')) {\n    return true;\n  }\n  var proto = getProtocolName(rule.url || rule.matcher) || getProtocolName(curUrl);\n  var name = rule.name;\n  if ((skip[name] || skip[proto]) && !notSkip(skip, name) && !notSkip(skip, proto)) {\n    return true;\n  }\n  return false;\n};\n\nfunction resolveRuleProps(rule, result) {\n  result = result || {};\n  if (rule) {\n    rule.list.forEach(function (rule) {\n      getMatcherValue(rule)\n        .split(SEP_RE)\n        .forEach(function (action) {\n          result[action] = true;\n        });\n    });\n  }\n  return result;\n}\n\nvar PLUGIN_RE = /^(?:plugin|whistle)\\.[a-z\\d_\\-]+$/;\nvar enableRules = ['https', 'intercept', 'capture', 'hide'];\n\nfunction ignorePlugins(rules, name, exclude) {\n  var isPlugin = name === 'plugin';\n  if (!isPlugin && !PLUGIN_RE.test(name)) {\n    return;\n  }\n  if (rules.plugin) {\n    var list = rules.plugin.list;\n    for (var i = list.length - 1; i >= 0; i--) {\n      var pName = getProtocolName(list[i].matcher);\n      if ((isPlugin || name === pName) && !exclude[pName]) {\n        list.splice(i, 1);\n      }\n    }\n    if (!list.length) {\n      delete rules.plugin;\n    }\n  }\n  return true;\n}\n\nfunction getProtocolName(url) {\n  return PROTO_NAME_RE.test(url) ? RegExp.$1 : '';\n}\n\nfunction ignoreForwardRule(rules, name, exclude) {\n  var isRule = name === 'rule';\n  if (!isRule && rules[name]) {\n    return;\n  }\n  if (rules.rule) {\n    var pName = getProtocolName(rules.rule.url);\n    if ((isRule || name === pName) && !exclude[pName]) {\n      delete rules.rule;\n    }\n  }\n  return true;\n}\n\nfunction ignoreProxy(rules, name, exclude) {\n  if (!rules.proxy) {\n    return;\n  }\n  if (name === 'proxy') {\n    delete rules.proxy;\n    return true;\n  }\n  if (!PROXY_RE.test(name)) {\n    return;\n  }\n  var pName = getProtocolName(rules.proxy.url);\n  var realName = aliasProtocols[name] || name;\n  var realPName = aliasProtocols[pName] || pName;\n  if (realName === realPName && !exclude[pName] && !exclude[realPName]) {\n    delete rules.proxy;\n  }\n  return true;\n}\n\nvar EXACT_IGNORE_RE = /^(?:pattern|matcher)=./;\n\nfunction ignoreRules(rules, ignore, isResRules) {\n  var result = resolveIgnore(ignore);\n  var ignoreAll = result.ignoreAll;\n  var exclude = result.exclude;\n  ignore = result.ignore;\n  var keys = Object.keys(ignoreAll ? rules : ignore);\n  var filter;\n  keys.forEach(function (name) {\n    if (EXACT_IGNORE_RE.test(name)) {\n      filter = filter || {};\n      filter['ignore|' + name] = true;\n    }\n    if (name === 'filter' || name === 'ignore' || exclude[name]) {\n      return;\n    }\n    if (!isResRules || protoMgr.resProtocols.indexOf(name) !== -1) {\n      if (\n        ignorePlugins(rules, name, exclude) ||\n        ignoreProxy(rules, name, exclude) ||\n        ignoreForwardRule(rules, name, exclude)\n      ) {\n        return;\n      }\n      delete rules[name];\n    }\n  });\n  if (filter) {\n    Object.keys(rules).forEach(function(name) {\n      var rule = rules[name];\n      var list = rule.list;\n      var matched = exactIgnore(filter, rule);\n      if (list) {\n        list = list.filter(function(r) {\n          return !exactIgnore(filter, r);\n        });\n        if (!list.length) {\n          delete rules[name];\n        } else {\n          if (matched) {\n            rule = extend({}, list[0]);\n            rules[name] = rule;\n          }\n          rule.list = list;\n        }\n      } else if (matched) {\n        delete rules[name];\n      }\n    });\n  }\n}\n\nexports.ignoreRules = ignoreRules;\n\nfunction filterRepeatPlugin(rule) {\n  if (rule.name !== 'plugin') {\n    return;\n  }\n  var exists = {};\n  rule.list = rule.list.filter(function (p) {\n    var protocol = p.matcher.substring(\n      p.matcher.indexOf('.'),\n      p.matcher.indexOf(':')\n    );\n    if (!exists[protocol]) {\n      exists[protocol] = 1;\n      return true;\n    }\n    return false;\n  });\n}\n\nexports.filterRepeatPlugin = filterRepeatPlugin;\n\nfunction isImportant(rule) {\n  return rule && rule.lineProps.important;\n}\n\nexports.isImportant = isImportant;\n\nfunction mergeRule(curRule, newRule) {\n  if (!curRule|| !newRule) {\n    return curRule || newRule;\n  }\n  if (!curRule.list) {\n    return isImportant(curRule) && !isImportant(newRule) ? curRule : newRule;\n  }\n  var list = newRule.list.concat(curRule.list);\n  if (isImportant(curRule)) {\n    var importants = [];\n    var normals = [];\n    for (var i = 0, len = list.length; i < len; i++) {\n      var rule = list[i];\n      if (isImportant(rule)) {\n        importants.push(rule);\n      } else {\n        normals.push(rule);\n      }\n    }\n    list = importants.concat(normals);\n  }\n  curRule.list = list;\n  filterRepeatPlugin(curRule);\n  return curRule;\n}\n\nfunction mergeRules(req, add, isResRules) {\n  var origin = req.rules;\n  var origAdd = add;\n  add = add || {};\n  var merge = function (protocol) {\n    var rule = mergeRule(origin[protocol], add[protocol]);\n    if (rule) {\n      origin[protocol] = rule;\n    }\n  };\n  if (origAdd) {\n    if (isResRules) {\n      protoMgr.resProtocols.forEach(merge);\n    } else {\n      Object.keys(origAdd).forEach(merge);\n    }\n  }\n\n  req['delete'] = resolveRuleProps(origin['delete'], req['delete']);\n  req._filters = resolveRuleProps(origin.filter, req._filters);\n  req.disable = resolveRuleProps(origin.disable, req.disable);\n  req.ignore = resolveRuleProps(origin.ignore, req.ignore);\n  req.enable = resolveRuleProps(origin.enable, req.enable);\n  enableRules.forEach(function (rule) {\n    if (req.enable[rule]) {\n      req._filters[rule] = true;\n    }\n  });\n  ignoreRules(origin, extend(req.ignore, req._filters), isResRules);\n  return add;\n}\n\nexports.mergeRules = mergeRules;\n\nfunction parseHeaderReplace(rule) {\n  var list = rule && rule.list;\n  if (!list) {\n    return '';\n  }\n  var result = '';\n  list.forEach(function (item) {\n    var obj = parseJSON(getMatcherValue(item));\n    var prop, name;\n    obj &&\n      Object.keys(obj).forEach(function (key) {\n        var value = obj[key];\n        if (!key.indexOf('req.') || !key.indexOf('reqH.')) {\n          prop = 'req';\n          name = null;\n        } else if (!key.indexOf('res.') || !key.indexOf('resH.')) {\n          prop = 'res';\n          name = null;\n        } else if (!key.indexOf('trailer.')) {\n          prop = 'trailer';\n          name = null;\n        } else if (!prop) {\n          return;\n        }\n        result = result || {};\n        var index = key.indexOf(':');\n        name = name || key.substring(key.indexOf('.') + 1, index).trim();\n        if (!name) {\n          return;\n        }\n        key = key.substring(index + 1);\n        var pattern = toOriginalRegExp(key);\n        var opList = result[prop];\n        var op = {\n          regExp: pattern,\n          name: name.toLowerCase(),\n          key: key,\n          value: value\n        };\n        if (opList) {\n          opList.push(op);\n        } else {\n          result[prop] = opList = [op];\n        }\n      });\n  });\n  return result;\n}\n\nexports.parseHeaderReplace = parseHeaderReplace;\n\nfunction replaceHeader(str, regExp, key, value) {\n  if (str == null || str === '') {\n    return str;\n  }\n  str = String(str);\n  if (!regExp || !SUB_MATCH_RE.test(value)) {\n    if (regExp) {\n      return str.replace(regExp, value);\n    }\n    return key ? str.split(key).join(value) : str;\n  }\n  return str.replace(regExp, function () {\n    return replacePattern(value, arguments);\n  });\n}\n\nfunction handleHeaderReplace(headers, opList) {\n  opList &&\n    opList.forEach(function (item) {\n      var header = headers[item.name];\n      if (header == null || header === '') {\n        return;\n      }\n      var regExp = item.regExp;\n      var key = item.key;\n      var value = item.value;\n      if (Array.isArray(header)) {\n        headers[item.name] = header.map(function (str) {\n          return replaceHeader(str, regExp, key, value);\n        });\n      } else {\n        headers[item.name] = replaceHeader(header, regExp, key, value);\n      }\n    });\n}\n\nexports.handleHeaderReplace = handleHeaderReplace;\n\nvar HTML_RE = /html/i;\n\nfunction addClientInfo(req) {\n  var headers = req.headers;\n  headers[clientIpKey] = req.clientIp || getClientIp(req);\n  headers[common.CLIENT_PORT_HEADER] = req.clientPort || getClientPort(req);\n  headers['x-whistle-remote-address'] = req._remoteAddr || getRemoteAddr(req);\n  headers['x-whistle-remote-port'] = req._remotePort || getRemotePort(req);\n}\n\nexports.addClientInfo = addClientInfo;\n\nexports.transformReq = function(req, res, port, host, html) {\n  var options = common.getReqOptions(req, port, host);\n  var headers = req.headers;\n  addClientInfo(req);\n  options.hostname = null;\n  html && removeUnsupportsHeaders(headers);\n  var destroyed;\n  var abort = function () {\n    if (!destroyed) {\n      destroyed = true;\n      client.destroy();\n    }\n  };\n  var client = http.request(options, function (_res) {\n    var resHeaders = _res.headers;\n    var origin =\n      !resHeaders['access-control-allow-origin'] && req.headers.origin;\n    if (origin) {\n      resHeaders['access-control-allow-origin'] = origin;\n      resHeaders['access-control-allow-credentials'] = true;\n    }\n    if (getStatusCode(_res.statusCode)) {\n      var type = resHeaders['content-type'];\n      if (html && (!type || HTML_RE.test(type)) && hasBody(_res)) {\n        var unzip = getUnzipStream(resHeaders);\n        var zip = getZipStream(resHeaders);\n        delete resHeaders['content-length'];\n        res.writeHead(_res.statusCode, resHeaders);\n        if (unzip && zip) {\n          unzip.on('error', abort);\n          zip.on('error', abort);\n          zip.write(html);\n          _res.pipe(unzip).pipe(zip).pipe(res);\n        } else {\n          res.write(html);\n          _res.pipe(res);\n        }\n      } else {\n        res.writeHead(_res.statusCode, resHeaders);\n        _res.pipe(res);\n      }\n    } else {\n      sendStatusCodeError(res, _res);\n    }\n  });\n  req.on('error', abort);\n  res.on('error', abort);\n  res.once('close', abort);\n  client.on('error', function (err) {\n    abort();\n    res.emit('error', err);\n  });\n  req.pipe(client);\n  return client;\n};\n\nfunction trimStr(str) {\n  if (typeof str !== 'string') {\n    return '';\n  }\n  return str.trim();\n}\n\nexports.trimStr = trimStr;\n\nfunction hasHeaderRules(headers) {\n  return (\n    headers['x-whistle-rule-key'] ||\n    headers['x-whistle-rule-value'] ||\n    headers['x-whistle-rule-host']\n  );\n}\n\nfunction checkIfAddInterceptPolicy(proxyHeaders, headers) {\n  if (hasHeaderRules(headers)) {\n    proxyHeaders['x-whistle-policy'] = 'intercept';\n    return true;\n  }\n}\n\nexports.checkIfAddInterceptPolicy = checkIfAddInterceptPolicy;\n\nfunction getCgiUrl(url) {\n  if (!isString(url) || !(url = url.trim())) {\n    return;\n  }\n  if (url[0] === '.' && url[1] === '/') {\n    url = url.substring(2);\n  } else {\n    url = url[0] === '/' ? url.substring(1) : url;\n  }\n  return url.substring(0, 10240);\n}\nexports.getCgiUrl = getCgiUrl;\n\nexports.getInstallRegistry = function(registry) {\n  if (Array.isArray(registry)) {\n    var result;\n    registry.slice(0, 3).forEach(function (reg) {\n      reg = common.getRegistry(reg);\n      if (reg) {\n        result = result || [];\n        result.push(reg);\n      }\n    });\n    return result;\n  }\n  registry = common.getRegistry(registry);\n  return registry && [registry];\n};\n\nexports.getCustomTab = function (tab, pluginName) {\n  if (!tab || !isString(tab.name)) {\n    return;\n  }\n  var name = tab.name.trim();\n  var page = getPage(tab.page || tab.action);\n  if (!name || !page || page.indexOf('#') !== -1) {\n    return;\n  }\n  return {\n    action: 'plugin.' + pluginName + '/' + page,\n    name: name.substring(0, 32),\n    icon: getCgiUrl(tab.icon || tab.iconUrl)\n  };\n};\n\nfunction checkStyle(style) {\n  if (!style || style > 0) {\n    return style;\n  }\n  if (typeof style !== 'string') {\n    return;\n  }\n  return style.substring(0, 32);\n}\n\nexports.getPluginModal = function(conf) {\n  var modal = conf.openInModal || conf.openInDialog;\n  if (!modal) {\n    return;\n  }\n  var result;\n  var width = checkStyle(modal.width);\n  var height = checkStyle(modal.height);\n  if (width) {\n    result = { width: width };\n  }\n  if (height) {\n    result = result || {};\n    result.height = height;\n  }\n  return result || 1;\n};\n\nfunction getString(str) {\n  if (!isString(str)) {\n    return;\n  }\n  return str.trim();\n}\n\nexports.getString = getString;\n\nvar HTML_PAGE_RE = /\\.html?$/i;\n\nfunction getPage(page) {\n  page = getCgiUrl(page);\n  return !page || page.length > 128 || !HTML_PAGE_RE.test(page) ? null : page;\n}\n\nexports.getPluginMenu = function (menus, pluginName) {\n  if (!Array.isArray(menus)) {\n    return;\n  }\n  var len = menus.length;\n  var count = 5;\n  var map = {};\n  var result, menu, name, page;\n  for (var i = 0; i < len; i++) {\n    if (\n      (menu = menus[i]) &&\n      (name = getString(menu.name)) &&\n      !map[name] &&\n      (page = getPage(menu.page || menu.action)) &&\n      page.indexOf('#') === -1\n    ) {\n      result = result || [];\n      map[name] = 1;\n      var pattern = menu.urlPattern;\n      result.push({\n        name: name.substring(0, 20),\n        action: 'plugin.' + pluginName + '/' + page,\n        required: menu.required ? true : undefined,\n        requiredTreeNode: menu.requiredTreeNode ? true : undefined,\n        urlPattern: isRegExp(pattern) && pattern.length < 256 ? pattern : undefined\n      });\n      if (--count === 0) {\n        return result;\n      }\n    }\n  }\n  return result;\n};\n\nexports.getNetworkColumn = function(conf) {\n  var column = conf.networkColumn;\n  var title = column && (column.title || column.name);\n  var key = title && column.key;\n  if (!isString(title) || !isString(key)) {\n    return;\n  }\n  return {\n    title: title.substring(0, 16),\n    key: key.substring(0, 72),\n    iconKey: isString(column.iconKey) ? column.iconKey.substring(0, 72) : undefined,\n    showTitle: (column.showTips || column.showTitle) ? 1 : undefined,\n    width: Math.min(360, Math.max(parseInt(column.width) || 120, 70))\n  };\n};\n\nexports.getWebWorker = function(conf) {\n  var webWorker = conf.webWorker;\n  if (!isString(webWorker)) {\n    return;\n  }\n  webWorker = toRelPath(webWorker);\n  return webWorker && webWorker.length <= 120 ? webWorker : undefined;\n};\n\nvar MAX_HINT_LEN = 512;\nvar MAX_VAR_LEN = 100;\n\nfunction getHintList(conf, isVar) {\n  var hintList = conf.hintList;\n  if (!Array.isArray(hintList) || !hintList.length) {\n    return;\n  }\n  var result;\n  var maxLen = isVar ? MAX_VAR_LEN : MAX_HINT_LEN;\n  hintList.forEach(function (hint) {\n    if (typeof hint === 'string') {\n      if (hint.length <= maxLen) {\n        result = result || [];\n        result.push(hint);\n      }\n    } else if (hint) {\n      var text = hint.text || hint.value;\n      text = typeof text === 'string' ? text.trim() : '';\n      if (!text) {\n        return;\n      }\n      var help = hint.help;\n      var isKey = hint.isKey ? 1 : undefined;\n      var displayText = hint.display || hint.displayText || hint.label;\n      if (typeof help !== 'string') {\n        help = '';\n      }\n      if (typeof displayText !== 'string') {\n        displayText = '';\n      }\n      result = result || [];\n      if (!isKey && !help && !displayText) {\n        result.push(text);\n      } else {\n        result.push({\n          isKey: isKey,\n          text: text,\n          help: help.trim(),\n          displayText: displayText\n        });\n      }\n    }\n  });\n  return result;\n}\n\nexports.getHintList = getHintList;\n\nvar HINT_SUFFIX_RE = /^[.=][^\\s]*$/;\n\nexports.getPluginVarsConf = function (conf) {\n  var pluginVars = conf.pluginVars;\n  if (!pluginVars) {\n    return;\n  }\n  var varHintList = getHintList(pluginVars, true);\n  var hintSuffix = pluginVars.hintSuffix;\n  var varHintUrl = varHintList ? undefined : getCgiUrl(pluginVars.hintUrl);\n  if (Array.isArray(hintSuffix)) {\n    hintSuffix = hintSuffix.filter(function(suffix) {\n      return HINT_SUFFIX_RE.test(suffix);\n    });\n    var len = hintSuffix.length;\n    if (!len) {\n      hintSuffix = undefined;\n    } else if (len > 10) {\n      hintSuffix = hintSuffix.slice(0, 10);\n    }\n  } else {\n    hintSuffix = undefined;\n  }\n  if (varHintList || varHintUrl || hintSuffix) {\n    return {\n      hintSuffix: hintSuffix,\n      hintUrl: varHintUrl,\n      hintList: varHintList\n    };\n  }\n  return true;\n};\n\nfunction toRelPath(str) {\n  return str.replace(/^\\/+/, '');\n}\n\nexports.getStaticDir = function (conf) {\n  var staticDir = conf.staticDir;\n  if (\n    !staticDir ||\n    typeof staticDir !== 'string' ||\n    !PATH_RE.test(staticDir) ||\n    staticDir.length > 100\n  ) {\n    return;\n  }\n  return toRelPath(staticDir.replace(/^\\/+/, ''));\n};\n\nfunction toString(str) {\n  if (str != null) {\n    if (typeof str === 'string') {\n      return str;\n    }\n    try {\n      return JSON.stringify(str) || '';\n    } catch (e) {}\n  }\n  return '';\n}\nexports.toString = toString;\n\nvar index = 0;\n\nexports.getReqId = function (now) {\n  if (index > 999) {\n    index = 0;\n  }\n  return (now || Date.now()) + '-' + common.padLeft(index++, 3) + workerIndex;\n};\n\nexports.onSocketEnd = common.onSocketEnd;\nexports.onResEnd = common.onResEnd;\n\nexports.getEmptyRes = function getRes() {\n  var res = common.createTransform();\n  res._transform = noop;\n  res.on('data', noop);\n  res.destroy = noop;\n  return res;\n};\n\nvar REQ_HEADER_RE = /^req\\.?H(?:eaders?)?\\.(.+)$/i;\nvar RES_HEADER_RE = /^res\\.?H(?:eaders?)?\\.(.+)$/i;\nvar TRAILER_RE = /trailer\\.(.+)$/;\nvar HEADER_RE = /^headers\\.(.+)$/;\nvar REQ_COOKIE_RE = /^req\\.?C(?:ookies?)?\\.(.+)$/i;\nvar RES_COOKIE_RE = /^res\\.?C(?:ookies?)?\\.(.+)$/i;\nvar REQ_BODY_RE = /^req\\.?B(?:ody?)?\\.(.+)$/i;\nvar RES_BODY_RE = /^res\\.?B(?:ody?)?\\.(.+)$/i;\nvar COOKIE_RE = /^cookies?\\.(.+)$/i;\nvar QUERY_RE = /^(?:query|params|url\\.?Params?)\\.(.+)$/i;\nvar QUERY_STRING_RE = /^(?:query|params|url\\.?Params?)$/i;\nvar PATH_INDEX_RE = /^pathname(?:\\.?(-?\\d+|first|last))?$/i;\n\nexports.parseDelQuery = function(req, delType) {\n  var query;\n  var paths;\n  var deleteRule = req['delete'];\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if (QUERY_RE.test(prop)) {\n        query = query || {};\n        query[RegExp.$1] = 1;\n      } else if (QUERY_STRING_RE.test(prop)) {\n        req._delQueryString = true;\n      } else if (PATH_INDEX_RE.test(prop)) {\n        paths = paths || {};\n        prop = RegExp.$1;\n        paths[prop === 'first' ? '0' : prop || 'all'] = 1;\n      } else if (delType) {\n        if (prop === 'reqType' || prop === 'req.type') {\n          delType.reqType = true;\n        } else if (prop === 'reqCharset' || prop === 'req.charset') {\n          delType.reqCharset = true;\n        }\n      }\n    });\n  }\n  return { query: query, paths: paths };\n};\n\nexports.deleteQuery = function(url, props, clear) {\n  if (!props && !clear) {\n    return url;\n  }\n  var index = url.indexOf('?');\n  if (index === -1) {\n    return url;\n  }\n  if (clear) {\n    return url.substring(0, index);\n  }\n  var query = url.substring(index + 1);\n  if (!query) {\n    return url;\n  }\n  query = query.split('&').filter(function(item) {\n    var i = item.indexOf('=');\n    return !props[i === -1 ? item : item.substring(0, i)];\n  }).join('&');\n  return url.substring(0, query ? index + 1 : index) + query;\n};\n\nfunction parseDeleteRule(req, p1, p2) {\n  var result;\n  var deleteRule = req['delete'];\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if ((p1 && p1.test(prop)) || (p2 && p2.test(prop))) {\n        result = result || {};\n        result[RegExp.$1] = 1;\n      }\n    });\n  }\n  return result;\n}\n\nfunction parseDelReqCookies(req) {\n  return parseDeleteRule(req, REQ_COOKIE_RE, COOKIE_RE);\n}\n\nexports.parseDelReqBody = function(req) {\n  return parseDeleteRule(req, REQ_BODY_RE);\n};\n\nexports.parseDelResBody = function(req) {\n  return parseDeleteRule(req, RES_BODY_RE);\n};\n\nexports.deleteProps = function(obj, keys) {\n  keys = obj && keys && Object.keys(keys);\n  if (!keys || !keys.length) {\n    return;\n  }\n  keys.forEach(function(key) {\n    common.deleteProps(obj, key);\n  });\n};\n\nfunction getDomain(req) {\n  var host = req._w2hostname;\n  if (typeof host !== 'string') {\n    return;\n  }\n  host = host.split('.');\n  var len = host.length;\n  if (len < 3) {\n    return;\n  }\n  if (len === 3) {\n    host[0] = '';\n  } else {\n    host.shift();\n  }\n  return host.join('.');\n}\n\nfunction parseDelResCookies(req) {\n  var resCookies;\n  var deleteRule = req['delete'];\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if (RES_COOKIE_RE.test(prop) || COOKIE_RE.test(prop)) {\n        resCookies = resCookies || {};\n        var list = [EXP_COOKIE, EXP_SECURE_COOKIE];\n        resCookies[RegExp.$1] = list;\n        var domain = getDomain(req);\n        if (domain) {\n          EXP_COOKIE_D.domain = domain;\n          EXP_SECURE_COOKIE_D.domain = domain;\n          list.push(EXP_COOKIE_D, EXP_SECURE_COOKIE_D);\n        }\n      }\n    });\n  }\n  return resCookies;\n}\n\nexports.parseDelProps = function(req) {\n  var deleteRule = req['delete'];\n  var resHeaders;\n  var trailers;\n  var resType;\n  var resCharset;\n\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if (RES_HEADER_RE.test(prop)) {\n        resHeaders = resHeaders || {};\n        resHeaders[RegExp.$1.toLowerCase()] = 1;\n      } else if (HEADER_RE.test(prop)) {\n        resHeaders = resHeaders || {};\n        resHeaders[RegExp.$1.toLowerCase()] = 1;\n      } else if (TRAILER_RE.test(prop)) {\n        trailers = trailers || {};\n        trailers[RegExp.$1.toLowerCase()] = 1;\n      } else if (prop === 'resType' || prop === 'res.type') {\n        resType = true;\n      } else if (prop === 'resCharset' || prop === 'res.charset') {\n        resCharset = true;\n      }\n    });\n  }\n  return {\n    resHeaders: resHeaders,\n    trailers: trailers,\n    resType: resType,\n    resCharset: resCharset\n  };\n};\n\nfunction parseDelResHeaders(req) {\n  var deleteRule = req['delete'];\n  var result;\n\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if (RES_HEADER_RE.test(prop)) {\n        result = result || {};\n        result[RegExp.$1.toLowerCase()] = 1;\n      } else if (HEADER_RE.test(prop)) {\n        result = result || {};\n        result[RegExp.$1.toLowerCase()] = 1;\n      }\n    });\n  }\n  return result;\n}\n\nexports.deleteResHeaders = function(req, headers) {\n  var props = parseDelResHeaders(req);\n  if (props) {\n    Object.keys(props).forEach(function (name) {\n      delete headers[name];\n    });\n  }\n};\n\nfunction parseDelReqHeaders(req) {\n  var result;\n  var deleteRule = req['delete'];\n  if (deleteRule) {\n    Object.keys(deleteRule).forEach(function (prop) {\n      if (REQ_HEADER_RE.test(prop)) {\n        result = result || {};\n        result[RegExp.$1.toLowerCase()] = 1;\n      } else if (HEADER_RE.test(prop)) {\n        result = result || {};\n        result[RegExp.$1.toLowerCase()] = 1;\n      }\n    });\n  }\n  return result;\n}\n\nexports.deleteReqHeaders = function (req) {\n  var props = parseDelReqHeaders(req);\n  if (props) {\n    var headers = req.headers;\n    Object.keys(props).forEach(function (name) {\n      delete headers[name];\n    });\n  }\n};\n\n\nfunction parseOrigin(origin) {\n  if (!isString(origin)) {\n    return;\n  }\n  var index = origin.indexOf('//');\n  if (index !== -1) {\n    index = origin.indexOf('/', index + 2);\n    if (index != -1) {\n      origin = origin.substring(0, index);\n    }\n  }\n  return origin;\n}\n\nexports.setReqCors = function (data, cors) {\n  if (!cors) {\n    return;\n  }\n  cors = lowerCaseify(cors);\n  var origin;\n  if (cors.origin === '*') {\n    origin = cors.origin;\n  } else if (isUrl(cors.origin)) {\n    origin = parseOrigin(cors.origin);\n  }\n  if (origin !== undefined) {\n    setHeader(data, 'origin', origin);\n  } else if (cors['*'] === '') {\n    setHeader(data, 'origin', '*');\n  }\n  if (cors.method !== undefined) {\n    setHeader(data, 'access-control-request-method', cors.method);\n  }\n  if (cors.headers !== undefined) {\n    setHeader(data, 'access-control-request-headers', cors.headers);\n  }\n};\n\nexports.setResCors = function (data, cors, req) {\n  if (!cors) {\n    return;\n  }\n  cors = lowerCaseify(cors);\n  var cusOrigin;\n  if (cors.origin === '*') {\n    cusOrigin = cors.origin;\n  } else if (isUrl(cors.origin)) {\n    cusOrigin = parseOrigin(cors.origin);\n  }\n  var isEnable = cors.enable;\n  var isOptions = req.method === 'OPTIONS';\n  var isStar = cors['*'] === '';\n  if (cusOrigin || isEnable) {\n    var origin = cusOrigin || req.headers.origin;\n    if (origin) {\n      setHeaders(data, {\n        'access-control-allow-credentials': true,\n        'access-control-allow-origin': origin\n      });\n    }\n  } else if (isStar) {\n    setHeader(data, 'access-control-allow-origin', '*');\n  }\n\n  if (cors.methods !== undefined) {\n    setHeader(data, 'access-control-allow-methods', cors.methods);\n  }\n  var autoComp = isOptions && (isStar || isEnable);\n  if (cors.headers !== undefined) {\n    var operate = isOptions ? 'allow' : 'expose';\n    setHeader(data, 'access-control-' + operate + '-headers', cors.headers);\n  } else if (autoComp) {\n    var headers = req.headers['access-control-request-headers'];\n    if (headers) {\n      setHeader(data, 'access-control-allow-headers', headers);\n    }\n  }\n\n  if (cors.credentials !== undefined) {\n    setHeader(data, 'access-control-allow-credentials', cors.credentials);\n  } else if (autoComp) {\n    var method = req.headers['access-control-request-method'];\n    if (method) {\n      setHeader(data, 'access-control-allow-method', method);\n    }\n  }\n\n  if (cors.maxage !== undefined) {\n    setHeader(data, 'access-control-max-age', cors.maxage);\n  }\n};\n\nexports.disableReqProps = function (req) {\n  var disable = req.disable;\n  var headers = req.headers;\n\n  if (disable.ua) {\n    delete headers['user-agent'];\n  }\n\n  if (disable.gzip || isEnable(req, 'captureStream')) {\n    delete headers['accept-encoding'];\n  }\n\n  if (\n    disable.cookie ||\n    disable.cookies ||\n    disable.reqCookie ||\n    disable.reqCookies\n  ) {\n    delete headers.cookie;\n  }\n\n  if (disable.referer || disable.referrer) {\n    delete headers.referer;\n  }\n\n  if (disable.ajax) {\n    delete headers['x-requested-with'];\n  }\n\n  if (disable.cache) {\n    disableReqCache(headers);\n  }\n};\n\nexports.disableResProps = function (req, headers) {\n  var disable = req.disable;\n  if (\n    disable.cookie ||\n    disable.cookies ||\n    disable.resCookie ||\n    disable.resCookies\n  ) {\n    delete headers['set-cookie'];\n  }\n  if (disable.cache) {\n    headers['cache-control'] = 'no-cache';\n    headers.expires = new Date(Date.now() - 60000000).toGMTString();\n    headers.pragma = 'no-cache';\n  }\n  disable.csp && disableCSP(headers);\n};\n\nvar G_INVALID_NAME_CHAR_RE = /[^\\x00-\\xFF]|[\\r\\n;=%]/gu;\nvar INVALID_NAME_CHAR_RE = /[\\r\\n;=]/;\nfunction escapeName(name) {\n  if (\n    !name ||\n    (!NON_LATIN1_RE.test(name) && !INVALID_NAME_CHAR_RE.test(name))\n  ) {\n    return name;\n  }\n  return name.replace(G_INVALID_NAME_CHAR_RE, safeEncodeURIComponent);\n}\n\nvar G_INVALID_VALUE_CHAR_RE = /[^\\x00-\\xFF]|[\\r\\n;%]/gu;\nvar INVALID_VALUE_CHAR_RE = /[\\r\\n;]/;\nfunction escapeValue(value) {\n  if (!isString(value)) {\n    return (value = value == null ? '' : String(value));\n  }\n  if (!NON_LATIN1_RE.test(value) && !INVALID_VALUE_CHAR_RE.test(value)) {\n    return value;\n  }\n  return value.replace(G_INVALID_VALUE_CHAR_RE, safeEncodeURIComponent);\n}\n\nexports.setReqCookies = function (data, cookies, curCookies, req) {\n  var list = cookies && Object.keys(cookies);\n  var notEmpty = list && list.length;\n  var delKeys = parseDelReqCookies(req);\n  if (!notEmpty && (!delKeys || !curCookies)) {\n    return;\n  }\n  var result = {};\n  if (isString(curCookies)) {\n    curCookies.split(/;\\s*/g).forEach(function (cookie) {\n      var index = cookie.indexOf('=');\n      var key = cookie;\n      var value = null;\n      if (index !== -1) {\n        key = cookie.substring(0, index);\n        value = cookie.substring(index + 1);\n      }\n      if (!delKeys || !delKeys[key]) {\n        result[key] = value;\n      }\n    });\n  }\n\n  notEmpty && list.forEach(function (name) {\n    var escName = escapeName(name);\n    if (!delKeys || (!delKeys[name] && !delKeys[escName])) {\n      var value = cookies[name];\n      value = value && typeof value == 'object' ? value.value : value;\n      result[escName] = value ? escapeValue(value) : value;\n    }\n  });\n\n  cookies = Object.keys(result)\n    .map(function (name) {\n      var value = result[name];\n      return name + '=' + (value == null ? '' : value);\n    })\n    .join('; ');\n  setHeader(data, 'cookie', cookies);\n};\n\nfunction getCookieItem(name, cookie) {\n  if (!cookie || typeof cookie != 'object') {\n    return name + '=' + (cookie == null ? '' : cookie);\n  }\n  var attrs = [name + '=' + escapeValue(cookie.value)];\n  var maxAge = cookie.maxAge ||  cookie.maxage ||\n      cookie.MaxAge || cookie['Max-Age'] || cookie['max-age'];\n  maxAge = parseInt(cookie.maxAge, 10);\n  if (!Number.isNaN(maxAge)) {\n    attrs.push('Expires=' + new Date(Date.now() + maxAge * 1000).toGMTString());\n    attrs.push('Max-Age=' + (maxAge === EXPIRED_SEC ? 0 : maxAge));\n  }\n\n  (cookie.secure || cookie.Secure) && attrs.push('Secure');\n  (cookie.httpOnly || cookie.HttpOnly || cookie.httponly) && attrs.push('HttpOnly');\n  (cookie.partitioned || cookie.Partitioned) && attrs.push('Partitioned');\n  var path = cookie.path || cookie.Path;\n  var domain = cookie.domain || cookie.Domain;\n  var sameSite = cookie.sameSite || cookie.samesite || cookie.SameSite;\n  path && attrs.push('Path=' + path);\n  domain && attrs.push('Domain=' + domain);\n  sameSite && attrs.push('SameSite=' + sameSite);\n  return attrs.join('; ');\n}\n\nfunction addMapArr(obj, key, value) {\n  var arr = obj[key] || [];\n  arr.push(value);\n  obj[key] = arr;\n}\n\nexports.setResCookies = function (data, cookies, req) {\n  var delKeys = parseDelResCookies(req);\n  if (delKeys) {\n    cookies = cookies ? extend(cookies, delKeys) : delKeys;\n  }\n  var list = cookies && Object.keys(cookies);\n  var notEmpty = list && list.length;\n  if (!notEmpty) {\n    return;\n  }\n  var curCookies = data.headers && data.headers['set-cookie'];\n  if (!Array.isArray(curCookies)) {\n    curCookies = curCookies ? [curCookies + ''] : [];\n  }\n\n  var curData = {};\n  curCookies.forEach(function (cookie) {\n    var index = cookie.indexOf('=');\n    var key = cookie;\n    if (index !== -1) {\n      key = cookie.substring(0, index);\n    }\n    addMapArr(curData, key, cookie);\n  });\n  var result = {};\n  list.forEach(function (name) {\n    var cookie = cookies[name];\n    name = escapeName(name);\n    if (Array.isArray(cookie)) {\n      cookie.forEach(function(value) {\n        addMapArr(result, name, getCookieItem(name, value));\n      });\n    } else {\n      addMapArr(result, name, getCookieItem(name, cookie));\n    }\n  });\n  extend(curData, result);\n  result = [];\n  Object.keys(curData).forEach(function (name) {\n    curData[name].forEach(function(value) {\n      result.push(value);\n    });\n  });\n  setHeader(data, 'set-cookie', result);\n};\n\nexports.escapeRegExp = common.escapeRegExp;\n\nexports.checkTlsError = function (err) {\n  if (!err) {\n    return false;\n  }\n  if (err.code === 'EPROTO') {\n    return true;\n  }\n  var stack = err.stack || err.message;\n  if (!isString(stack)) {\n    return false;\n  }\n  if (\n    stack.indexOf('TLSSocket.onHangUp') !== -1 ||\n    stack.indexOf('statusCode=502') !== -1\n  ) {\n    return true;\n  }\n  return stack.toLowerCase().indexOf('openssl') !== -1;\n};\nexports.checkAuto2Http = function (req, ip, proxyUrl) {\n  return (\n    !req.disable.auto2http &&\n    (req.enable.auto2http ||\n      req.rules.host ||\n      (proxyUrl ? req._phost : isLocalAddress(ip)))\n  );\n};\n\nexports.setProxyHost = function (req, options, reserve) {\n  var phost = req._phost || options;\n  var opts = reserve ? options : extend({}, options);\n  opts.host = phost.hostname;\n  if (phost.port > 0) {\n    opts.port = phost.port;\n  }\n  opts.headers = opts.headers || {};\n  common.setRawHeader(opts.headers, 'host', joinIpPort(opts.host, opts.port));\n  return opts;\n};\n\nvar RESPONSE_FOR_NAME = /^name=(.+)$/;\nexports.setResponseFor = function (rules, headers, req, serverIp, phost) {\n  var responseFor = getMatcherValue(rules.responseFor);\n  phost = phost && phost.host;\n  if (!responseFor) {\n    if (req.isPluginReq) {\n      var isLocal1 = isLocalAddress(serverIp);\n      var isLocal2 = !phost || isLocalAddress(phost);\n      if (!isLocal1 || !isLocal2) {\n        responseFor = trimStr(headers['x-whistle-response-for']);\n        responseFor = responseFor\n          ? responseFor.split(',').map(trim).filter(noop)\n          : [];\n        if (!isLocal2 && responseFor.indexOf(phost) === -1) {\n          responseFor.push(phost);\n        }\n        if (!isLocal1 && responseFor.indexOf(serverIp) === -1) {\n          responseFor.push(serverIp);\n        }\n        headers['x-whistle-response-for'] = responseFor.join(', ');\n      }\n    }\n    return;\n  }\n  var reqHeaders = req.headers;\n  if (RESPONSE_FOR_NAME.test(responseFor)) {\n    var result = RegExp.$1.toLowerCase().split(',');\n    var reqResult = [];\n    result = result\n      .map(function (name) {\n        if (name.indexOf('req.') === 0) {\n          name = reqHeaders[name.substring(4)];\n          name && reqResult.push(name);\n          return;\n        }\n        return headers[name];\n      })\n      .filter(noop);\n    if (phost && result.indexOf(phost) === -1) {\n      result.push(phost);\n    }\n    serverIp = serverIp || '127.0.0.1';\n    if (result.indexOf(serverIp) === -1) {\n      result.push(serverIp);\n    }\n    responseFor = result.concat(reqResult).join(', ');\n  }\n  headers['x-whistle-response-for'] = responseFor;\n};\n\nvar CONFIG_VAR_RE = /\\${(port|version)}/gi;\nvar PLUGIN_RULES_URL_RE = /^whistle\\.([a-z\\d_-]+)(?:$|\\/)/i;\nvar PLUGIN_KEY_RE =/^\\$(?:whistle\\.)?([a-z\\d_-]+)[/:]([\\S\\s]+)$/;\nvar REL_PATH_RE = /^[\\w.-]+(?:[\\\\/]|$)/;\n\nfunction setConfigVarFn(_, name) {\n  return config[name.toLowerCase()];\n}\n\nfunction getRemoteRules(apo, rulesUrl, root) {\n  var headers = config.runtimeHeaders;\n  var pluginName;\n  if (PLUGIN_RULES_URL_RE.test(rulesUrl)) {\n    pluginName = RegExp.$1;\n    rulesUrl = pluginName + '/' + rulesUrl.substring(RegExp['$&'].length);\n    headers = config.pluginHeaders;\n  } else if (PLUGIN_KEY_RE.test(rulesUrl)) {\n    pluginName = RegExp.$1;\n    rulesUrl = pluginName + '/api/key/value?key=' + safeEncodeURIComponent(RegExp.$2);\n    headers = config.pluginHeaders;\n  }\n  if (apo) {\n    rulesUrl = rulesUrl.replace(CONFIG_VAR_RE, setConfigVarFn);\n  }\n  if (root && !pluginName && REL_PATH_RE.test(rulesUrl) && !(rulesUrl = joinPath(root, rulesUrl))) {\n    return '';\n  }\n  return httpMgr.add(rulesUrl, headers, pluginName);\n}\n\nexports.getRemoteRules = getRemoteRules;\n\nvar MAX_REMOTE_RULES_COUNT = 20;\nvar REMOTE_RULES_RE = /^\\s*@(`?)(whistle\\.[a-z\\d_\\-]+(?:\\/[^\\s#]*)?|(?:https?:\\/\\/|[a-z]:[\\\\/]|~?\\/)[^\\s#]+|\\$(?:whistle\\.)?[a-z\\d_-]+[/:][^\\s#]+)\\s*?\\1(?:#.*)?$/gim;\n\nexports.getRemoteRulesResolver = function(inlineValues) {\n  var index = 0;\n  return function(text, file) {\n    return text.replace(REMOTE_RULES_RE, function (_, apo, rulesUrl) {\n      if (index >= MAX_REMOTE_RULES_COUNT) {\n        return '';\n      }\n      ++index;\n      return resolveInlineValues(getRemoteRules(apo, rulesUrl), inlineValues, file);\n    });\n  };\n};\n\nfunction isCustomParser(req) {\n  var enable = req.enable;\n  var disable = req.disable;\n  return enable && (enable.customParser || enable.customFrames) && (!disable || (!disable.customParser && !disable.customFrames));\n}\nexports.isCustomParser = isCustomParser;\n\nexports.getParserStatus = function (req) {\n  if (!isCustomParser(req)) {\n    return;\n  }\n  var enable = req.enable;\n  var customParser = ['custom'];\n  if (enable.pauseSend) {\n    customParser.push('pauseSend');\n  } else if (enable.ignoreSend) {\n    customParser.push('ignoreSend');\n  }\n  if (enable.pauseReceive) {\n    customParser.push('pauseReceive');\n  } else if (enable.ignoreReceive) {\n    customParser.push('ignoreReceive');\n  }\n  return customParser.join();\n};\n\nexports.isInspect = function (enable) {\n  return (\n    enable.inspect ||\n    enable.pauseReceive ||\n    enable.pauseSend ||\n    enable.ignoreReceive ||\n    enable.ignoreSend\n  );\n};\n\nvar BYTES_RANGE_RE = /^\\s*bytes=/i;\n\nexports.parseRange = function (req, size) {\n  var range = size && req.headers.range;\n  if (!range || !BYTES_RANGE_RE.test(range)) {\n    return;\n  }\n  range = range.substring(range.indexOf('=') + 1).trim();\n  if (!range) {\n    return;\n  }\n  var start = size;\n  var end = -1;\n  range = range.split(',').forEach(function (item) {\n    item = item.split('-');\n    var s = parseInt(item[0], 10);\n    var e = parseInt(item[1], 10);\n    if (isNaN(s)) {\n      if (isNaN(e)) {\n        return;\n      }\n      s = size - e;\n    } else if (isNaN(e)) {\n      e = size - 1;\n    }\n    start = Math.min(s, start);\n    end = Math.max(end, e);\n  });\n  if (start < 0 || end < 0 || start > end || end >= size) {\n    return;\n  }\n  return {\n    start: start,\n    end: end\n  };\n};\n\nexports.parseClientInfo = function (req) {\n  if (req.headers[config.FROM_COM_HEADER] === '1') {\n    req.fromComposer = true;\n    req._disabledProxyRules = !!req.headers[config.DISABLE_RULES_HEADER];\n    delete req.headers[config.FROM_COM_HEADER];\n    delete req.headers[config.DISABLE_RULES_HEADER];\n  }\n  var socket = req.socket || '';\n  if (socket.fromTunnel) {\n    req.fromTunnel = true;\n  }\n  var clientInfo = req.headers[clientInfoKey];\n  if (clientInfo) {\n    delete req.headers[clientInfoKey];\n    clientInfo = String(clientInfo).split(',');\n    if (!net.isIP(clientInfo[0]) || !(clientInfo[1] > 0)) {\n      return '';\n    }\n    req.fromTunnel = true;\n    socket.fromTunnel = true;\n  }\n  return clientInfo || '';\n};\n\nexports.checkPluginReqOnce = function (req, raw) {\n  var isPluginReq = req.headers[config.PROXY_ID_HEADER];\n  if (raw ? isPluginReq : isPluginReq == 1) {\n    delete req.headers[config.PROXY_ID_HEADER];\n  }\n  req._isPureInternalReq = isPluginReq == 'internal';\n  if (req._isPureInternalReq || isPluginReq == 'internalx') {\n    req._isInternalReq = true;\n    delete req.headers[config.PROXY_ID_HEADER];\n  }\n  return isPluginReq;\n};\n\nexports.showPluginReq = function(req) {\n  return !req.isPluginReq || config.showPluginReq;\n};\n\nexports.checkPort = function (port, host, cb) {\n  if (typeof host !== 'string') {\n    cb = host;\n    host = '127.0.0.1';\n  }\n  if (!port) {\n    return cb();\n  }\n  var server = http.createServer();\n  server.listen(port, host, function () {\n    server.close(cb);\n  });\n};\n\nvar boundIpDeferMap = {};\nexports.getBoundIp = function (host, cb) {\n  if (typeof host === 'function') {\n    cb = host;\n    host = null;\n  }\n  host = host || config.defaultHost;\n  if (!host || net.isIP(host)) {\n    return cb(host);\n  }\n  var boundIpDefer = boundIpDeferMap[host];\n  if (boundIpDefer) {\n    return boundIpDefer.then(cb);\n  }\n  boundIpDeferMap[host] = boundIpDefer = new Promise(function(resolve) {\n    dns.lookup(host, function (err, ip) {\n      if (err) {\n        throw err;\n      }\n      resolve(ip);\n    });\n  });\n  boundIpDefer.then(cb);\n};\n\nfunction getPluginConfig(conf, name) {\n  var result;\n  if (conf != null) {\n    try {\n      result = JSON.stringify(conf);\n    } catch (e) {}\n  }\n  return (\n    '<script>window.' +\n    (name || 'whistleMenuConfig') +\n    ' = ' +\n    (result || '{}') +\n    ';</script>'\n  );\n}\n\nexports.getPluginMenuConfig = function (conf) {\n  return getPluginConfig(conf.menuConfig);\n};\n\nexports.getPluginInspectorConfig = function (conf) {\n  return getPluginConfig(conf.inspectorConfig, 'whistleInspectorConfig');\n};\n\nfunction isIllegalcHeader(name, value) {\n  switch (name) {\n  case h2Consts.HTTP2_HEADER_CONNECTION:\n  case h2Consts.HTTP2_HEADER_UPGRADE:\n  case h2Consts.HTTP2_HEADER_HOST:\n  case h2Consts.HTTP2_HEADER_HTTP2_SETTINGS:\n  case h2Consts.HTTP2_HEADER_KEEP_ALIVE:\n  case h2Consts.HTTP2_HEADER_PROXY_CONNECTION:\n  case h2Consts.HTTP2_HEADER_TRANSFER_ENCODING:\n    return true;\n  case h2Consts.HTTP2_HEADER_TE:\n    return value !== 'trailers';\n  default:\n    return false;\n  }\n}\n\nexports.formatH2Headers = function (headers) {\n  var newHeaders = {};\n  Object.keys(headers).forEach(function (name) {\n    var value = headers[name];\n    if (!isIllegalcHeader(name, value)) {\n      newHeaders[name] = value;\n    }\n  });\n  return newHeaders;\n};\n\nfunction getProp(obj, key, def) {\n  key = key.split('.');\n  for (var i = 0; i < key.length; i++) {\n    obj = obj ? obj[key[i]] : undefined;\n  }\n  return obj == null ? def : obj;\n}\n\nvar PLUGIN_VAR_RE =\n  /\\{\\{(?:whistlePluginName|whistlePluginPackage\\.([^}\\s]+))\\}\\}/g;\n\nexports.renderPluginRules = function (rules, pkg, simpleName) {\n  return (\n    rules &&\n    rules.replace(PLUGIN_VAR_RE, function (_, key) {\n      return key ? getProp(pkg, key, '') : simpleName;\n    })\n  );\n};\n\nexports.setClientCert = function (options, key, cert, isPfx, cacheKey) {\n  if (!cert) {\n    return;\n  }\n  options.cacheKey = cacheKey;\n  if (isPfx) {\n    options.pfx = cert;\n    if (key) {\n      options.passphrase = key;\n    }\n  } else {\n    options.key = key;\n    options.cert = cert;\n  }\n};\n\nfunction isDisableUserLogin(rule, req) {\n  if (rule.lineProps.enableUserLogin || (req && req.enable && req.enable.userLogin)) {\n    return false;\n  }\n  return rule.lineProps.disableUserLogin || (req && req.disable && req.disable.userLogin);\n}\n\nexports.isDisableUserLogin = isDisableUserLogin;\n\nexports.getStatusCodeFromRule = function (rules, req) {\n  var rule = rules.rule;\n  var isSpec = rule && rule.isSpec;\n  if (!isSpec) {\n    return;\n  }\n  var disableUserLogin = isDisableUserLogin(rule, req);\n  rule = getMatcherValue(rule) || '';\n  var isRedirect = isSpec === 2;\n  if (req && isRedirect && rule && compareUrl(rule, req.fullUrl)) {\n    req.isWebProtocol = true;\n    req.options = parseUrl(req.fullUrl);\n    return;\n  }\n  var code = rule || 200;\n  var result = { statusCode: code, headers: {} };\n  if (isRedirect) {\n    result.statusCode = 302;\n    result.headers.location = rule;\n  } else  if (!disableUserLogin) {\n    handleStatusCode(code, result.headers);\n  }\n  return result;\n};\n\nfunction removeBody(req, data, isRes) {\n  var rule = req['delete'] || '';\n  if (rule.body || rule[isRes ? 'res.body' : 'req.body']) {\n    delete data.top;\n    delete data.bottom;\n    data.body = EMPTY_BUFFER;\n  }\n}\n\nexports.removeReqBody = function (req, data) {\n  removeBody(req, data);\n};\n\nexports.removeResBody = function (req, data) {\n  removeBody(req, data, true);\n};\n\nfunction readOneChunk(stream, callback, timeout) {\n  if (!stream) {\n    return callback();\n  }\n  var timer;\n  var handler = function (chunk) {\n    timer && clearTimeout(timer);\n    stream.pause();\n    stream.removeListener('data', handler);\n    stream.removeListener('end', handler);\n    callback(chunk);\n  };\n  if (timeout > 0) {\n    timer = setTimeout(handler, timeout);\n  }\n  stream.on('data', handler);\n  stream.on('end', handler);\n}\n\nexports.readOneChunk = readOneChunk;\n\nvar AUTH_RE = /^(?:username|password)=/;\n\nfunction formatAuth(obj) {\n  if (!obj) {\n    return;\n  }\n  var username = obj.username;\n  var password = obj.password;\n  return {\n    username: username == null ? null : String(username),\n    password: password == null ? null : String(password)\n  };\n}\n\nexports.getAuthByRules = function (rules) {\n  if (!rules.auth) {\n    return;\n  }\n  var auth = getMatcherValue(rules.auth);\n  if (auth[0] === '{' && auth[auth.length - 1] === '}') {\n    return formatAuth(parseRawJson(auth)) || {};\n  }\n  var obj = AUTH_RE.test(auth) && formatAuth(parseQuery(auth, null, null, true));\n  if (obj || SLASH_RE.test(auth)) {\n    return obj;\n  }\n  var index = auth.indexOf(':');\n  return {\n    username: index == -1 ? auth : auth.substring(0, index),\n    password: index == -1 ? null : auth.substring(index + 1)\n  };\n};\n\nexports.lookupType = function (type) {\n  return type === 'sse' ? 'text/event-stream' : mime.lookup(type, 'application/octet-stream');\n};\n\nexports.getAuthBasic = function (auth) {\n  if (!auth) {\n    return;\n  }\n  var basic;\n  if (auth.username == null) {\n    if (auth.password == null) {\n      return;\n    }\n    basic = [''];\n  } else {\n    basic = [auth.username];\n  }\n  if (auth.password != null) {\n    basic[1] = auth.password;\n  }\n  return basic && 'Basic ' + toBuffer(basic.join(':')).toString('base64');\n};\n\nexports.delay = function (time, callback) {\n  if (time > 0) {\n    setTimeout(callback, time);\n  } else {\n    callback();\n  }\n};\n\nvar F_HOST_RE = /\\bhost\\b/i;\nvar F_PROTO_RE = /\\bproto\\b/i;\nvar F_IP_RE = /\\b(?:clientIp|ip|for)\\b/i;\n\nexports.handleForwardedProps = function (req) {\n  var headers = req.headers;\n  var props = headers[FWD_PROPS_HEADER];\n  var enableFwdHost = config.enableFwdHost;\n  var enableFwdProto = config.enableFwdProto;\n  var enableFwdFor = config.keepXFF;\n  if (props != null) {\n    enableFwdHost = enableFwdHost || F_HOST_RE.test(props);\n    enableFwdProto = enableFwdProto || F_PROTO_RE.test(props);\n    enableFwdFor = enableFwdFor || F_IP_RE.test(props);\n    if (config.master && enableFwdFor) {\n      headers[FWD_PROPS_HEADER] = 'ip';\n    } else {\n      delete headers[FWD_PROPS_HEADER];\n    }\n  }\n  req.enableXFF = enableFwdFor;\n  if (enableFwdHost) {\n    var host = headers[FWD_HOST_HEADER];\n    if (host) {\n      delete headers[FWD_HOST_HEADER];\n      headers[REAL_HOST_HEADER] =\n        headers[REAL_HOST_HEADER] || host;\n    }\n  }\n  if (enableFwdProto) {\n    var proto = headers[HTTPS_PROTO_HEADER];\n    if (proto) {\n      delete headers[HTTPS_PROTO_HEADER];\n      req.isHttps = proto === 'https';\n    }\n  }\n};\n\nexports.filterWeakRule = function (req) {\n  var rule = req.rules && req.rules.rule;\n  if (!rule) {\n    return;\n  }\n  var proxy = req.rules.proxy;\n  if ((!proxy || proxy.lineProps.proxyHostOnly) && !req.rules.host) {\n    return;\n  }\n  if (rule.lineProps.weakRule || isEnable(req, 'weakRule')) {\n    delete req.rules.rule;\n  }\n};\n\nexports.setPluginMgr = function(p) {\n  pluginMgr = p;\n};\n\n\nfunction setTunnelHeaders(headers, remoteData) {\n  var tunnelFirst = remoteData.tunnelFirst;\n  if (remoteData.clientId) {\n    headers[config.CLIENT_ID_HEADER] = remoteData.clientId;\n  }\n  if (\n      remoteData.proxyAuth &&\n      (tunnelFirst || !headers['proxy-authorization'])\n    ) {\n    headers['proxy-authorization'] = remoteData.proxyAuth;\n  }\n  if (remoteData.tunnelData) {\n    headers[TUNNEL_DATA_HEADER] = remoteData.tunnelData;\n  }\n  if (remoteData.sniPlugin) {\n    headers[SNI_PLUGIN_HEADER] = remoteData.sniPlugin;\n  }\n  var tunnelHeaders = remoteData.headers;\n  if (tunnelHeaders) {\n    Object.keys(tunnelHeaders).forEach(function (key) {\n      if (tunnelFirst || !headers[key]) {\n        headers[key] = tunnelHeaders[key];\n      }\n    });\n  }\n}\n\nexports.setTunnelHeaders = setTunnelHeaders;\n\nexports.addTunnelData = function(socket, headers) {\n  var data = socket[TUNNEL_DATA_HEADER];\n  if (!data) {\n    data = headers[TEMP_TUNNEL_DATA_HEADER];\n    if (data) {\n      delete headers[TEMP_TUNNEL_DATA_HEADER];\n      try {\n        data = decodeURIComponent(data);\n        data = JSON.parse(data);\n        socket[TUNNEL_DATA_HEADER] = data;\n      } catch(e) {\n        return;\n      }\n    }\n  }\n  data && setTunnelHeaders(headers, data);\n};\n\nfunction _isInternalProxy(rule) {\n  return rule && rule.lineProps.internalProxy;\n}\n\nexports.isInternalProxy = function(req) {\n  if (isEnable(req, 'internalProxy')) {\n    return true;\n  }\n  var rules = req.rules || '';\n  return _isInternalProxy(rules.proxy) || _isInternalProxy(rules.host);\n};\n\nvar IP_RE = /^(\\d{1,3}(?:\\.\\d{1,3}){3}|localhost|\\[[^\\]]+\\])(?::\\d+)$/;\nfunction checkProxyHost(host, filter) {\n  var result;\n  if (filter.hostPattern) {\n    result = filter.hostPattern.test(host);\n  } else if (filter.host === '<local>') {\n    if (IP_RE.test(host)) {\n      host = RegExp.$1;\n    }\n    result = host === 'localhost' || isLocalAddress(host);\n  } else if (filter.host) {\n    if (filter.host.slice(-1) === ':') {\n      result = !host.indexOf(filter.host);\n    } else {\n      result = host === filter.host;\n    }\n  }\n  return filter.not ? !result : result;\n}\n\nexports.checkProxyHost = function(proxy, host) {\n  var filters = proxy && proxy.hostFilter;\n  if (filters) {\n    if (!host) {\n      return false;\n    }\n    var hasIncludeFilter;\n    var include, exclude;\n    for (var i = 0, len = filters.length; i < len; i++) {\n      var filter = filters[i];\n      hasIncludeFilter = hasIncludeFilter || filter.isInclude;\n      if ((filter.isInclude ? !include : !exclude) && checkProxyHost(host, filter)) {\n        if (filter.isInclude) {\n          include = true;\n        } else {\n          exclude = true;\n        }\n      }\n    }\n    return hasIncludeFilter ? include && !exclude : !exclude;\n  }\n  return true;\n};\n\nexports.getInspectorTabs = function(conf) {\n  return conf.inspectorsTabs || conf.inspectorsTab || conf.inspectorTabs || conf.inspectorTab || '';\n};\n\n\nvar SPEC_PATH = '/_WHISTLE_5b6af7b9884e1165_/';\n\nexports.removeSpecPath = function(req) {\n  var specPath = config.SPEC_PATH || SPEC_PATH;\n  if (req.url && req.url.indexOf(specPath) !== -1) {\n    req.url = req.url.replace(specPath, '/');\n  }\n};\n\nfunction getRulesText(req) {\n  var rules = req.rules;\n  var keys = rules && Object.keys(rules);\n  if (!keys || !keys.length) {\n    return;\n  }\n  return safeEncodeURIComponent(keys.map(function(key) {\n    var rule = rules[key];\n    return rule.rawPattern + ' ' + rule.rawMatcher;\n  }).join('\\n'));\n}\n\nexports.addMatchedRules = function(req, res) {\n  var enable = req.enable || '';\n  if (!enable || (res ? !isEnable(req, 'responseWithMatchedRules') : !isEnable(req, 'requestWithMatchedRules'))) {\n    return;\n  }\n  var rules = getRulesText(req);\n  if (rules) {\n    var headers = res ? res.headers : req.headers;\n    headers['x-whistle-matched-rules'] = rules;\n  }\n};\n\nexports.needAbortReq = function(req) {\n  var disable = req.disable;\n  if (disable.abort || disable.abortReq) {\n    return false;\n  }\n  if (req.enable.abort || req._filters.abort ||\n    req.enable.abortReq || req._filters.abortReq) {\n    return true;\n  }\n  return req.isTunnel && disable.tunnel;\n};\n\nexports.needAbortRes = function(req) {\n  var disable = req.disable;\n  if (disable.abort || disable.abortRes) {\n    return false;\n  }\n  if (req.enable.abort || req._filters.abort ||\n    req.enable.abortRes || req._filters.abortRes) {\n    return true;\n  }\n  return req.isTunnel && disable.tunnel;\n};\n\nvar SSE_RE = /^\\s*text\\/event-stream\\s*;?/i;\n\nexports.isSSE = function(res) {\n  return SSE_RE.test(res.headers['content-type']);\n};\n\nexports.setCharset = function(headers, charset, removeType, removeCharset) {\n  if (!charset && !removeType && !removeCharset) {\n    return;\n  }\n  var type = headers['content-type'];\n  type = typeof type == 'string' ? type.trim() : '';\n  type = type.split(/\\s*;\\s*/);\n  if (removeType) {\n    type[0] = '';\n  }\n  if (removeCharset) {\n    type = [type[0]];\n  } else if (charset) {\n    type[1] = 'charset=' + charset;\n  }\n  type = type.join('; ');\n  if (type) {\n    headers['content-type'] = type;\n  } else {\n    delete headers['content-type'];\n  }\n};\n\nexports.getNewType = function(type, headers) {\n  if (type.indexOf(';') === -1) {\n    var curType = headers['content-type'];\n    if (typeof curType == 'string' && curType.indexOf(';') !== -1) {\n      curType = curType.split(';');\n      curType[0] = type;\n      type = curType.join(';');\n    }\n  }\n  return type;\n};\n\nexports.setRejectUnauthorized = function (req, options) {\n  if (req._isInternalReq && req.disable.rejectUnauthorized) {\n    options.rejectUnauthorized = false;\n  } else {\n    options.rejectUnauthorized = config.rejectUnauthorized;\n  }\n  return options;\n};\n\nexports.notStarted = function (err) {\n  return err && err.code === 'ECONNREFUSED';\n};\n\nexports.getLocalhostIP = function (err, req, domain, curIp) {\n  if (!err || req.rules.host || req.disable.lacalhostCompatible ||\n    err.code !== 'ECONNREFUSED' || domain !== 'localhost') {\n    return;\n  }\n  if (curIp === '::1') {\n    return LOCALHOST;\n  }\n  if (curIp === LOCALHOST) {\n    return '::1';\n  }\n};\n\nfunction checkHideProp(e, d, name, filterHide) {\n  var hide = 'hide' + name;\n  var show = 'show' + name;\n  return (e[hide] || d[show] || filterHide) && !e[show] && !d[hide];\n}\n\nexports.checkHideProp = checkHideProp;\n\nexports.isHide = function(req) {\n  if (!config.captureData) {\n    return true;\n  }\n  var e = req.enable || '';\n  var d = req.disable || '';\n  return req.fromComposer ? checkHideProp(e, d, 'Composer') : checkHideProp(e, d, '', req._filters && req._filters.hide);\n};\n"
  },
  {
    "path": "lib/util/is-utf8.js",
    "content": "var MAX_LEN = 1024 * 32;\n\nfunction isUtf8(buf, i) {\n  i = i || 0;\n  for (var len = Math.min(buf.length, MAX_LEN); i < len; i++) {\n    var byte = buf[i];\n    if (\n      byte == 0x09 ||\n      byte == 0x0a ||\n      byte == 0x0d ||\n      (0x20 <= byte && byte <= 0x7f)\n    ) {\n      continue;\n    }\n    ++i;\n    var byte1 = buf[i];\n    if (0xc2 <= byte && byte <= 0xdf) {\n      if (0x80 <= byte1 && byte1 <= 0xbf) {\n        continue;\n      }\n      return !byte1;\n    }\n    ++i;\n    var byte2 = buf[i];\n    if (byte == 0xe0) {\n      if (0xa0 <= byte1 && byte1 <= 0xbf && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n\n    if ((0xe1 <= byte && byte <= 0xec) || byte == 0xee || byte == 0xef) {\n      if (0x80 <= byte1 && byte1 <= 0xbf && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n\n    if (byte == 0xed) {\n      if (0x80 <= byte1 && byte1 <= 0x9f && 0x80 <= byte2 && byte2 <= 0xbf) {\n        continue;\n      }\n      return !byte2;\n    }\n    ++i;\n    var byte3 = buf[i];\n    if (byte == 0xf0) {\n      if (\n        0x90 <= byte1 &&\n        byte1 <= 0xbf &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n    if (0xf1 <= byte && byte <= 0xf3) {\n      if (\n        0x80 <= byte1 &&\n        byte1 <= 0xbf &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n    if (byte == 0xf4) {\n      if (\n        0x80 <= byte1 &&\n        byte1 <= 0x8f &&\n        0x80 <= byte2 &&\n        byte2 <= 0xbf &&\n        0x80 <= byte3 &&\n        byte3 <= 0xbf\n      ) {\n        continue;\n      }\n      return !byte3;\n    }\n\n    return false;\n  }\n  return true;\n}\n\nmodule.exports = function (buf) {\n  if (isUtf8(buf)) {\n    return true;\n  }\n  return buf[0] === 0 && isUtf8(buf, 5);\n};\n"
  },
  {
    "path": "lib/util/log-server.js",
    "content": "var MAX_LENGTH = 800;\nvar MIN_LENGTH = 720;\nvar SIZE = 1024 * 64;\nvar COUNT = 100;\nvar logIndex = 0;\nvar logs = [];\nvar LEVEL_RE = /^fatal|error|warn|info|debug$/;\n\nfunction sliceLogs(index, count, logId) {\n  if (!logId) {\n    return logs.slice(index, index + count);\n  }\n  var result = [];\n  for (var len = logs.length; index < len; index++) {\n    var log = logs[index];\n    if (log.logId === logId) {\n      result.push(log);\n      if (--count <= 0) {\n        return result;\n      }\n    }\n  }\n  return result;\n}\n\nfunction getLogs(startTime, count, logId) {\n  var len = logs.length;\n  if (!len || startTime == -1) {\n    return [];\n  }\n\n  count = Math.min(count || COUNT, len);\n  if (startTime === 0) {\n    return logs.slice(-1);\n  }\n\n  if (startTime != -2 && startTime) {\n    for (var i = 0; i < len; i++) {\n      var log = logs[i];\n      if (log.id === startTime) {\n        return sliceLogs(i + 1, count, logId);\n      }\n    }\n  }\n  return sliceLogs(0, count, logId);\n}\n\nmodule.exports = function init(proxy) {\n  proxy.addLog = function set(log) {\n    if (!log) {\n      return;\n    }\n\n    var now = Date.now();\n    var text = log.text;\n    if (text == null) {\n      text = '';\n    } else if (typeof text != 'string') {\n      text += '';\n    }\n    var overflow = text.length - SIZE;\n    logs.push({\n      id: now + '-' + ++logIndex,\n      logId: log.id,\n      date: (log.t && parseInt(log.t, 10)) || now,\n      level: LEVEL_RE.test(log.level)\n        ? log.level\n        : 'info',\n      text: overflow > 9 ? text.substring(0, SIZE) + '...(' + overflow + ')' : text\n    });\n    var len = logs.length;\n    if (len > MAX_LENGTH) {\n      logs = logs.slice(len - MIN_LENGTH, len);\n      if (logIndex > 100000) {\n        logIndex = 0;\n      }\n    }\n  };\n  proxy.getLogs = getLogs;\n  proxy.getLatestId = function () {\n    var last = logs[logs.length - 1];\n    return last && last.id;\n  };\n};\n"
  },
  {
    "path": "lib/util/logger.js",
    "content": "var util = require('util');\n\nvar MAX_LENGTH = 360;\nvar MIN_LENGTH = 280;\nvar COUNT = 100;\nvar logIndex = 0;\nvar logs = [];\nvar LEVELS = ['fatal', 'error', 'warn', 'info', 'debug'];\n\nfunction getLogs(startTime, count) {\n  var len = logs.length;\n  if (!len || startTime == -1) {\n    return [];\n  }\n\n  count = Math.min(count || COUNT, len);\n  if (startTime === 0) {\n    return logs.slice(-1);\n  }\n\n  if (startTime != -2 && startTime) {\n    for (var i = 0; i < len; i++) {\n      var log = logs[i];\n      if (log.id === startTime) {\n        ++i;\n        return logs.slice(i, i + count);\n      }\n    }\n  }\n  return logs.slice(0, count);\n}\n\nfunction log(text, level) {\n  var now = Date.now();\n  logs.push({\n    id: now + '-' + ++logIndex,\n    date: now,\n    level: level,\n    text: text\n  });\n  var len = logs.length;\n  if (len > MAX_LENGTH) {\n    logs = logs.slice(len - MIN_LENGTH, len);\n    if (logIndex > 100000) {\n      logIndex = 0;\n    }\n  }\n}\n\nexports.getLogs = getLogs;\n\nLEVELS.forEach(function (level) {\n  exports[level] = function (msg) {\n    if (msg == null && arguments.length < 2) {\n      return;\n    }\n    log(util.format.apply(null, arguments), level);\n  };\n});\n\nexports.getLatestId = function () {\n  var last = logs[logs.length - 1];\n  return last && last.id;\n};\n\nexports.log = exports.info;\n"
  },
  {
    "path": "lib/util/parse-query.js",
    "content": "var qs = require('querystring');\n\nvar TOKEN_RE = /\\r\\u0000\\n\\u0003\\r/g;\nvar PLUS_RE = /\\+/g;\nvar TOKEN = '\\r\\u0000\\n\\u0003\\r';\n\nvar decoder = {\n  decodeURIComponent: function (s) {\n    s = s.replace(TOKEN_RE, '+');\n    return qs.unescape(s);\n  }\n};\nvar rawDecoder = {\n  decodeURIComponent: function (s) {\n    return s.replace(TOKEN_RE, '+');\n  }\n};\nvar rawDecoder2 = {\n  decodeURIComponent: function (s) {\n    return s;\n  }\n};\n\nfunction parse(str, sep, eq, escape) {\n  try {\n    if (str.indexOf('+') === -1 || str.indexOf(TOKEN) !== -1) {\n      return qs.parse(str, sep, eq, escape ? rawDecoder2 : undefined);\n    }\n    str = str.replace(PLUS_RE, TOKEN);\n    return qs.parse(str, sep, eq, escape ? rawDecoder : decoder);\n  } catch (e) {}\n  return '';\n}\n\nmodule.exports = parse;\n"
  },
  {
    "path": "lib/util/parse-url-safe.js",
    "content": "var parseUrl = require('url').parse;\n\nvar CONTROL_RE = /^[\\n\\r\\t\\x00-\\x20\\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]+/;\nvar PROTOCOL_RE = /^([a-z][a-z0-9.+-]*:)?(\\/\\/)?([\\\\/]+)?([\\S\\s]*)/i;\nvar WIN_DRIVE_RE = /^[a-zA-Z]:/;\nvar PORT_RE = /:(\\d*)$/;\n\nfunction formatHost(host) {\n  if (host[0] === '[') {\n    var end = host.length - 1;\n    if (host[end] === ']') {\n      host = host.substring(1, end);\n    }\n  }\n  return host;\n}\n\nfunction needPort(port, protocol) {\n  protocol = protocol.split(':')[0];\n  port = +port;\n\n  if (!port) {\n    return false;\n  }\n\n  switch (protocol) {\n  case 'http':\n  case 'ws':\n    return port !== 80;\n\n  case 'https':\n  case 'wss':\n    return port !== 443;\n\n  case 'ftp':\n    return port !== 21;\n\n  case 'gopher':\n    return port !== 70;\n\n  case 'file':\n    return false;\n  }\n\n  return port !== 0;\n}\n\nfunction getProtocol(addr) {\n  var match = PROTOCOL_RE.exec(addr);\n  var protocol = match[1] ? match[1].toLowerCase() : '';\n  var forwardSlashes = match[2] || '';\n  var otherSlashes = match[3] || '';\n  var slashesCount = forwardSlashes.length + otherSlashes.length;\n  var rest = forwardSlashes + otherSlashes + match[4];\n\n  if (protocol === 'file:') {\n    if (slashesCount >= 2) {\n      rest = rest.slice(2);\n    }\n  } else if (protocol && forwardSlashes) {\n    rest = rest.slice(2);\n  }\n\n  return {\n    protocol: protocol,\n    slashes: !!forwardSlashes,\n    slashesCount: slashesCount,\n    rest: rest\n  };\n}\n\nfunction encode(str) {\n  try {\n    return encodeURIComponent(decodeURIComponent(str));\n  } catch (e) {\n    return '';\n  }\n}\n\nfunction formatPath(url) {\n  if (url.pathname.charAt(0) !== '/') {\n    url.pathname = '/' + url.pathname;\n  }\n}\n\nfunction formatPort(url) {\n  if (!needPort(url.port, url.protocol)) {\n    url.host = url.hostname;\n    url.port = '';\n  }\n}\n\nfunction parseAuth(url) {\n  url.username = url.password = '';\n  if (url.auth) {\n    var index = url.auth.indexOf(':');\n    if (index !== -1) {\n      url.username = url.auth.slice(0, index);\n      url.username = encode(url.username);\n      url.password = url.auth.slice(index + 1);\n      url.password = encode(url.password);\n    } else {\n      url.username = encode(url.auth);\n    }\n    url.auth = url.password ? url.username + ':' + url.password : url.username;\n  }\n}\n\nfunction formatOrigin(url) {\n  var protocol = url.protocol;\n  if (protocol !== 'file:' && url.host) {\n    url.origin = protocol +'//'+ url.host;\n  } else {\n    url.origin = 'null';\n  }\n}\n\nfunction formatUrl(url) {\n  var host = url.host;\n  var protocol = url.protocol;\n  var result = protocol + (protocol && url.slashes ? '//' : '');\n\n  if (url.username) {\n    result += url.username;\n    if (url.password) {\n      result += ':' + url.password;\n    }\n    result += '@';\n  } else if (url.password) {\n    result += ':' + url.password + '@';\n  }\n\n  result += host + url.pathname;\n\n  if (url.search) {\n    result += url.search;\n  }\n\n  if (url.hash) {\n    result += url.hash;\n  }\n  url.href = result;\n}\n\nmodule.exports = function(addr) {\n  try {\n    return parseUrl(addr);\n  } catch (e) {}\n\n  addr = String(addr || '').replace(CONTROL_RE, '');\n\n  var url = {};\n  var result = getProtocol(addr);\n  var protocol = result.protocol;\n\n  url.slashes = result.slashes;\n  url.protocol = protocol;\n  addr = result.rest;\n\n  var index = addr.indexOf('#');\n  if (index !== -1) {\n    url.hash = addr.substring(index);\n    addr = addr.substring(0, index);\n  }\n\n  index = addr.indexOf('?');\n  if (index !== -1) {\n    url.search = addr.substring(index);\n    url.query = addr.substring(index + 1);\n    addr = addr.substring(0, index);\n  }\n\n  if (\n    (protocol === 'file:' && (result.slashesCount !== 2 || WIN_DRIVE_RE.test(addr))) ||\n    (!result.slashes && (protocol || result.slashesCount < 2))\n  ) {\n    url.pathname = addr;\n    url.host = url.hostname = '';\n  } else {\n    index = addr.indexOf('/');\n    if (index !== -1) {\n      url.pathname = addr.substring(index);\n      addr = addr.substring(0, index);\n    } else {\n      url.pathname = '/';\n    }\n    url.path = url.pathname + (url.search || '');\n    index = addr.lastIndexOf('@');\n    if (index !== -1) {\n      url.auth = addr.substring(0, index);\n      addr = addr.substring(index + 1);\n    }\n\n    url.host = addr;\n    index = PORT_RE.exec(addr);\n    if (index) {\n      url.port = index[1];\n      addr = addr.substring(0, index.index);\n    }\n    url.hostname = formatHost(addr);\n  }\n\n  formatPath(url);\n  formatPort(url);\n  parseAuth(url);\n  formatOrigin(url);\n  formatUrl(url);\n  return url;\n};\n\nmodule.exports.formatHost = formatHost;\n"
  },
  {
    "path": "lib/util/parse-url.js",
    "content": "var parseUrl = require('./parse-url-safe');\n\nvar formatHost = parseUrl.formatHost;\nvar URL_RE = /^([a-z0-9.+-]+:)\\/\\/([^/?#]*)(\\/[^?#]*)?(\\?[^#]*)?(#[\\s\\S]*)?$/i;\nvar HOST_RE = /^(.+)(?::(\\d*))$/;\n\nmodule.exports = function (url) {\n  if (!URL_RE.test(url)) {\n    return parseUrl(url);\n  }\n  var protocol = RegExp.$1;\n  var host = RegExp.$2;\n  var pathname = RegExp.$3 || '/';\n  var search = RegExp.$4;\n  var hash = RegExp.$5 || null;\n  var port = null;\n  var hostname = host;\n  if (HOST_RE.test(host)) {\n    hostname = RegExp.$1;\n    port = RegExp.$2;\n  }\n\n  return {\n    protocol: protocol,\n    slashes: true,\n    auth: null,\n    host: host,\n    port: port,\n    hostname: formatHost(hostname),\n    hash: hash,\n    search: search || null,\n    query: search ? search.substring(1) : null,\n    pathname: pathname,\n    path: pathname + search,\n    href: url\n  };\n};\n\nmodule.exports.formatHost = formatHost;\n"
  },
  {
    "path": "lib/util/patch.js",
    "content": "var EventEmitter = require('events').EventEmitter;\nvar PassThrough = require('stream').PassThrough;\nvar Socket = require('net').Socket;\nvar http = require('http');\nvar https = require('https');\nvar hparser = require('hparser');\n\nvar httpRequest = http.request;\nvar httpsRequest = https.request;\nvar res = http.OutgoingMessage.prototype;\nvar noop = function () {};\nvar INVALID_PATH_RE = /[^\\u0021-\\u00ff]/;\nvar INVALID_PATH_RE_G = /[^\\u0021-\\u00ff]/g;\nvar HOST_RE = /^host$/i;\nvar STREAM_OPTS = { highWaterMark: 1 };\n\nprocess.emitWarning = noop;\n//see: https://github.com/joyent/node/issues/9272\nprocess.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';\n\nvar setHeader = res.setHeader;\nres.setHeader = function (field, val) {\n  try {\n    return setHeader.call(this, field, val);\n  } catch (e) {}\n};\n\nfunction listenerCount(emitter, eventName) {\n  if (typeof emitter.listenerCount === 'function') {\n    return emitter.listenerCount(eventName);\n  }\n  return EventEmitter.listenerCount(emitter, eventName);\n}\n\nexports.listenerCount = listenerCount;\n\nvar proto = Socket.prototype;\nvar destroy = proto.destroy;\nvar on = proto.on;\n// 避免第三方模块没处理好异常导致程序crash\nproto.destroy = function (err) {\n  if (this.destroyed) {\n    return;\n  }\n  if (err && !listenerCount(this, 'error')) {\n    this.on('error', noop);\n  }\n  return destroy.call(this, err);\n};\n// 避免一些奇奇怪怪的异常，导致整个进程 crash\n// 如：Error: This socket has been ended by the other party\nvar wrapOn = function () {\n  var evt = arguments[0];\n  if (this.on === wrapOn) {\n    this.on = on;\n  }\n  if (evt !== 'error' && !listenerCount(this, 'error')) {\n    on.call(this, 'error', noop);\n  }\n  return on.apply(this, arguments);\n};\nproto.on = wrapOn;\n\nfunction filterInvalidPath(options) {\n  if (!options) {\n    return options;\n  }\n  if (typeof options === 'string') {\n    if (INVALID_PATH_RE.test(options)) {\n      return options.replace(INVALID_PATH_RE_G, '');\n    }\n  } else if (options.path && INVALID_PATH_RE.test(options.path)) {\n    options.path = String(options.path).replace(INVALID_PATH_RE_G, '');\n  }\n  return options;\n}\n\nfunction hackRequest(requestFn, self, args, isApply) {\n  var client;\n  try {\n    client = requestFn[isApply ? 'apply' : 'call'](self, args);\n    var end = client.end;\n    client.end = function () {\n      try {\n        end.apply(this, arguments);\n      } catch (e1) {\n        client.emit('error', e1);\n      }\n    };\n  } catch (e2) {\n    client = new PassThrough(STREAM_OPTS);\n    process.nextTick(function () {\n      client.emit('error', e2);\n    });\n  }\n  return client.on('error', noop);\n}\n\nhttps.request = function () {\n  return hackRequest(httpsRequest, this, arguments, true);\n};\n\nhttp.request = function (options) {\n  var tunnelPath =\n    options && options.method === 'CONNECT' && options.proxyTunnelPath;\n  options = filterInvalidPath(options);\n  if (!tunnelPath) {\n    return hackRequest(httpRequest, this, arguments, true);\n  }\n  var client = hackRequest(httpRequest, this, options);\n  var on = client.on;\n  client.on = function (type, listener) {\n    if (type !== 'connect') {\n      return on.apply(this, arguments);\n    }\n    on.call(this, type, function (res, socket, head) {\n      socket.on('error', noop);\n      if (res.statusCode !== 200) {\n        return listener.apply(this, arguments);\n      }\n      var headers = {};\n      var isHost;\n      if (options.headers) {\n        isHost = options.headers.Host;\n        Object.keys(options.headers).forEach(function(key) {\n          if (!HOST_RE.test(key)) {\n            headers[key] = options.headers[key];\n          }\n        });\n      }\n      headers[isHost ? 'Host' : 'host'] = tunnelPath;\n      if (options.enableIntercept) {\n        headers['x-whistle-policy'] = 'intercept';\n        delete headers['X-Whistle-Policy'];\n      }\n      headers = hparser.getRawHeaders(headers);\n      var rawData = 'CONNECT ' + tunnelPath + ' HTTP/1.1';\n      if (headers) {\n        rawData += '\\r\\n' + headers;\n      }\n      rawData += '\\r\\n\\r\\n';\n      if (res.statusCode === 200 && res.headers['x-whistle-allow-tunnel-ack']) {\n        rawData = '1' + rawData;\n      }\n      socket.write(rawData);\n      hparser.parse(\n        socket,\n        function (err, _res) {\n          if (err) {\n            return client.emit('error', err);\n          }\n          res.statusCode = parseInt(_res.statusCode, 10);\n          res.headers = _res.headers;\n          !options.keepStreamResume && socket.pause();\n          listener.call(this, res, socket, head);\n        },\n        true\n      );\n    });\n    return this;\n  };\n  return client;\n};\n"
  },
  {
    "path": "lib/util/perf.js",
    "content": "var now = Date.now();\nvar preData = {\n  now: now,\n  totalHttpRequests: 0,\n  totalWsRequests: 0,\n  totalTunnelRequests: 0,\n  totalAllHttpRequests: 0,\n  totalAllWsRequests: 0\n};\nvar memUsage = process.memoryUsage();\nvar maxCpuElap = 0;\nvar procData = {\n  memUsage: memUsage,\n  uptime: 0,\n  cpuPercent: '0.0%',\n  startupTime: now,\n  updateTime: now,\n  httpRequests: 0,\n  allHttpRequests: 0,\n  wsRequests: 0,\n  allWsRequests: 0,\n  tunnelRequests: 0,\n  totalHttpRequests: 0,\n  totalWsRequests: 0,\n  totalTunnelRequests: 0,\n  totalAllHttpRequests: 0,\n  totalAllWsRequests: 0,\n  httpQps: 0,\n  tunnelQps: 0,\n  wsQps: 0,\n  totalQps: 0,\n  maxQps: 0,\n  maxAllQps: 0,\n  maxRss: memUsage.rss,\n  maxCpu: 0,\n  maxQpsTime: now,\n  maxAllQpsTime: now,\n  maxRssTime: now,\n  maxCpuTime: now\n};\nvar startTime = typeof process.hrtime === 'function' && process.hrtime();\nvar startUsage = typeof process.cpuUsage === 'function' && process.cpuUsage();\nvar proxy;\n\nfunction secNSec2ms(secNSec) {\n  if (Array.isArray(secNSec)) {\n    return secNSec[0] * 1000 + secNSec[1] / 1000000;\n  }\n  return secNSec / 1000;\n}\n\nif (startTime !== false && startUsage !== false) {\n  setInterval(function () {\n    var elapTime = process.hrtime(startTime);\n    var elapUsage = process.cpuUsage(startUsage);\n    startTime = process.hrtime();\n    startUsage = process.cpuUsage();\n    var elapTimeMS = secNSec2ms(elapTime) || 1;\n    var elapUserMS = secNSec2ms(elapUsage.user);\n    var elapSystMS = secNSec2ms(elapUsage.system);\n    var cpuElap = (100 * (elapUserMS + elapSystMS)) / elapTimeMS;\n    var curTime = Date.now();\n    procData.cpuPercent = cpuElap.toFixed(1) + '%';\n    procData.memUsage = process.memoryUsage();\n    procData.updateTime = curTime;\n    if (cpuElap > maxCpuElap) {\n      maxCpuElap = cpuElap;\n      procData.maxCpu = cpuElap.toFixed(1) + '%';\n      procData.maxCpuTime = curTime;\n    }\n    if (procData.memUsage.rss > procData.maxRss) {\n      procData.maxRss = procData.memUsage.rss;\n      process.maxRssTime = curTime;\n    }\n    proxy && proxy.emit('perfDataChange', procData);\n  }, 3000);\n  setInterval(function () {\n    var curTime = Date.now();\n    var costTime = curTime - preData.now || 1;\n    var newHttpReqs = procData.totalHttpRequests - preData.totalHttpRequests;\n    var newTunnelReqs =\n      procData.totalTunnelRequests - preData.totalTunnelRequests;\n    var newWsReqs = procData.totalWsRequests - preData.totalWsRequests;\n    var newHttpUIReqs =\n      procData.totalAllHttpRequests - preData.totalAllHttpRequests;\n    var newWsUIReqs = procData.totalAllWsRequests - preData.totalAllWsRequests;\n    preData.now = curTime;\n    preData.totalHttpRequests = procData.totalHttpRequests;\n    preData.totalTunnelRequests = procData.totalTunnelRequests;\n    preData.totalWsRequests = procData.totalWsRequests;\n    preData.totalAllHttpRequests = procData.totalAllHttpRequests;\n    preData.totalAllWsRequests = procData.totalAllWsRequests;\n    procData.uptime = curTime - now;\n    procData.httpQps = Math.floor((newHttpReqs * 100000) / costTime);\n    procData.tunnelQps = Math.floor((newTunnelReqs * 100000) / costTime);\n    procData.wsQps = Math.floor((newWsReqs * 100000) / costTime);\n    procData.allHttpQps = Math.floor((newHttpUIReqs * 100000) / costTime);\n    procData.allWsQps = Math.floor((newWsUIReqs * 100000) / costTime);\n    var totalQps = procData.httpQps + procData.tunnelQps + procData.wsQps;\n    var totalAllQps =\n      procData.allHttpQps + procData.allWsQps + procData.tunnelQps;\n    procData.totalQps = totalQps;\n    procData.totalAllQps = totalAllQps;\n    if (procData.maxQps < totalQps) {\n      procData.maxQps = totalQps;\n      procData.maxQpsTime = curTime;\n    }\n    if (procData.maxAllQps < totalAllQps) {\n      procData.maxAllQps = totalAllQps;\n      procData.maxAllQpsTime = curTime;\n    }\n  }, 1000);\n}\nexports.procData = procData;\nexports.setProxy = function (p) {\n  proxy = p;\n};\n"
  },
  {
    "path": "lib/util/process.js",
    "content": "module.exports = require('./perf').procData;\n"
  },
  {
    "path": "lib/util/replace-pattern-transform.js",
    "content": "var Transform = require('pipestream').Transform;\nvar util = require('util');\n\nvar LENGTH = 5120;\nvar slice = [].slice;\nvar SUB_MATCH_RE = /(^|\\\\{0,2})?(\\$\\$?(b)?[&\\d])/g;\nvar ALL_RE = /^\\/\\.[*+]\\/g?i?g?$/;\nvar MAX_SUB_MATCH_LEN = 512;\n\nfunction ReplacePatternTransform(pattern, value, isSSE) {\n  Transform.call(this);\n  this._pattern = pattern;\n  this._replaceAll = ALL_RE.test(pattern);\n  this._value = value == null ? '' : value + '';\n  this._isSSE = isSSE;\n  this._rest = '';\n}\n\nutil.inherits(ReplacePatternTransform, Transform);\n\nvar proto = ReplacePatternTransform.prototype;\nproto._transform = function (chunk, _, callback) {\n  var value = this._value;\n  if (this._replaceAll) {\n    this._value = '';\n    chunk = value;\n  } else if (chunk != null) {\n    chunk = this._rest + chunk;\n    var index = 0;\n    var len = chunk.length - MAX_SUB_MATCH_LEN;\n    var result = chunk.replace(this._pattern, function () {\n      var matcher = arguments[0];\n      var matcherLen = matcher.length;\n      var i = arguments[arguments.length - 2] + matcherLen;\n      var subLen = i - len;\n      if (subLen >= 0 && matcherLen <= LENGTH - subLen) {\n        return matcher;\n      }\n      index = i;\n      return replacePattern(value, arguments);\n    });\n    index = Math.max(index, chunk.length - LENGTH);\n    if (this._isSSE) {\n      var endIndex = chunk.lastIndexOf('\\n\\n');\n      if (endIndex !== -1) {\n        index = Math.max(endIndex + 2, index);\n      }\n    }\n    this._rest = chunk.substring(index);\n    chunk = result.substring(0, result.length - this._rest.length);\n  } else if (this._rest) {\n    chunk = this._rest.replace(this._pattern, function () {\n      return replacePattern(value, arguments);\n    });\n  }\n\n  callback(null, chunk);\n};\n\nfunction getSubMatchers(args) {\n  args = slice.call(args);\n  return args.slice(0, -2);\n}\nfunction replacePattern(replacement, args, vals) {\n  var arr = args && args.length ? getSubMatchers(args) : args;\n  return replacement\n    ? replacement.replace(SUB_MATCH_RE, function (_, $1, $2, $3) {\n      var list = $3 ? vals : arr;\n      if (!list) {\n        return $1 + $2;\n      }\n      if ($1 === '\\\\') {\n        return $2;\n      }\n      if ($1 === '\\\\\\\\') {\n        $1 = '\\\\';\n      }\n      var encode = $2[1] === '$';\n      $2 = $2.substring((encode ? 2 : 1) + ($3 ? 1 : 0));\n      if ($2 === '&') {\n        $2 = 0;\n      }\n      $2 = list[$2] || '';\n      if (encode && $2) {\n        try {\n          $2 = encodeURIComponent($2);\n        } catch (e) {}\n      }\n      return ($1 || '') + $2;\n    })\n    : '';\n}\nReplacePatternTransform.replacePattern = replacePattern;\nmodule.exports = ReplacePatternTransform;\n"
  },
  {
    "path": "lib/util/replace-string-transform.js",
    "content": "var Transform = require('pipestream').Transform;\nvar util = require('util');\n\nfunction ReplaceStringTransform(str, value, isSSE) {\n  Transform.call(this);\n  this._str = str;\n  this._length = this._str.length;\n  this._value = value == null ? '' : value + '';\n  this._isSSE = isSSE;\n  this._rest = '';\n}\n\nutil.inherits(ReplaceStringTransform, Transform);\n\nvar proto = ReplaceStringTransform.prototype;\nproto._transform = function (chunk, _, callback) {\n  if (chunk != null) {\n    chunk = this._rest + chunk;\n    var minIndex = chunk.length + 1 - this._length;\n    var index = chunk.lastIndexOf(this._str);\n\n    if (index != -1) {\n      index = Math.max(minIndex, index + this._length);\n    } else {\n      index = minIndex;\n    }\n    if (this._isSSE) {\n      var endIndex = chunk.lastIndexOf('\\n\\n');\n      if (endIndex !== -1) {\n        index = Math.max(endIndex + 2, index);\n      }\n    }\n    this._rest = chunk.substring(index);\n    chunk = chunk.substring(0, index);\n  } else {\n    chunk = this._rest;\n  }\n\n  callback(null, replace(chunk, this._str, this._value));\n};\n\nfunction replace(chunk, str, value) {\n  return chunk ? chunk.split(str).join(value) : null;\n}\n\nmodule.exports = ReplaceStringTransform;\n"
  },
  {
    "path": "lib/util/speed-transform.js",
    "content": "var Transform = require('pipestream').Transform;\nvar util = require('util');\n\nfunction SpeedTransform(options) {\n  Transform.call(this);\n  options = options || {};\n  var value = parseInt((options.speed * 1000) / 8);\n  if (value > 0) {\n    this._speed = value;\n  }\n  if ((value = parseInt(options.delay)) > 0) {\n    this._delay = value;\n  }\n}\n\nutil.inherits(SpeedTransform, Transform);\n\nSpeedTransform.prototype._transform = function (chunk, encoding, callback) {\n  var self = this;\n  var cb = function () {\n    if (chunk && self._speed) {\n      setTimeout(function () {\n        callback(null, chunk);\n      }, Math.round((chunk.length * 1000) / self._speed));\n    } else {\n      callback(null, chunk);\n    }\n  };\n\n  if (self._delay) {\n    var delay = self._delay;\n    self._delay = null;\n    return setTimeout(cb, delay);\n  }\n\n  cb();\n};\n\nmodule.exports = SpeedTransform;\n"
  },
  {
    "path": "lib/util/transproto.js",
    "content": "var Transform = require('stream').Transform;\n\nvar OPTIONS = { highWaterMark: 0, objectMode: true };\nvar LF = Buffer.from('\\n');\n\nfunction getBuffer(data) {\n  return Buffer.isBuffer(data) ? data : Buffer.from(String(data));\n}\n\nfunction pack(data) {\n  if (!data) {\n    return Buffer.from('\\n0\\n');\n  }\n  data = getBuffer(data);\n  return Buffer.concat([Buffer.from('\\n' + data.length + '\\n'), data]);\n}\n\nexports.pack = pack;\n\nexports.getEncodeTransform = function () {\n  var trans = new Transform(OPTIONS);\n  trans._transform = function (chunk, _, cb) {\n    cb(null, pack(chunk));\n  };\n  trans.push_ = trans.push;\n  trans.push = function (chunk, encoding) {\n    if (chunk) {\n      return trans.push_(chunk, encoding);\n    }\n  };\n  trans.end_ = trans.end;\n  trans.end = function (chunk) {\n    return trans.end_(function () {\n      chunk && trans.push_(pack(chunk));\n      trans.push_(pack());\n    });\n  };\n  return trans;\n};\n\nfunction Parser() {\n  var buf;\n  var len = 0;\n  var parseChunk = function () {\n    if (len <= 0) {\n      var index = buf.indexOf(LF, 1);\n      if (index === -1) {\n        return;\n      }\n      len = parseInt(String(buf.slice(1, index)), 10);\n      if (!len) {\n        return false;\n      }\n      buf = buf.slice(index + 1);\n      if (!buf.length) {\n        return;\n      }\n    }\n    var curLen = len;\n    var chunk = buf.slice(0, curLen);\n    len -= chunk.length;\n    buf = buf.length > curLen ? buf.slice(curLen) : null;\n    return chunk;\n  };\n  var self = this;\n  self.write = function (chunk) {\n    if (chunk) {\n      buf = buf ? Buffer.concat([buf, chunk]) : chunk;\n    } else if (!buf) {\n      return;\n    }\n    var data = parseChunk();\n    if (data === false) {\n      return self.onEnd && self.onEnd();\n    }\n    if (!data) {\n      if (chunk === false) {\n        return;\n      }\n      return self.onContinue && self.onContinue();\n    }\n    self.onData && self.onData(data);\n    self.write(false);\n  };\n}\n\nexports.getDecodeTransform = function () {\n  var trans = new Transform(OPTIONS);\n  var parser = new Parser();\n  var transCb, data;\n  parser.onEnd = function () {\n    data && trans.push(data);\n    trans.push(null);\n  };\n  parser.onContinue = function () {\n    if (transCb) {\n      transCb();\n      transCb = null;\n    }\n  };\n  parser.onData = function (chunk) {\n    data = data ? Buffer.concat([data, chunk]) : chunk;\n    if (transCb) {\n      transCb(null, data);\n      transCb = data = null;\n    }\n  };\n  trans._transform = function (chunk, _, cb) {\n    transCb = cb;\n    chunk && parser.write(getBuffer(chunk));\n  };\n  return trans;\n};\n"
  },
  {
    "path": "lib/util/whistle-transform.js",
    "content": "var Transform = require('pipestream').Transform;\nvar util = require('util');\nvar iconv = require('iconv-lite');\nvar fileMgr = require('./file-mgr');\n\nvar DOCTYPE = Buffer.from('<!DOCTYPE html>\\r\\n');\n\nfunction WhistleTransform(options) {\n  Transform.call(this);\n  options = options || {};\n  var value = parseInt((options.speed * 1000) / 8);\n  if (value > 0) {\n    this._speed = value;\n  }\n  if ((value = parseInt(options.delay)) > 0) {\n    this._delay = value;\n  }\n  this._noDoctype = options.noDoctype;\n\n  var charset = options.charset && String(options.charset);\n  if (!iconv.encodingExists(charset)) {\n    charset = 'utf8';\n  }\n  this._isHtml = options.isHtml;\n  this._body = getBuffer(options, 'body', charset);\n  this._top = getBuffer(options, 'top', charset);\n  this._bottom = getBuffer(options, 'bottom', charset);\n  if (this._body || this._top || this._bottom) {\n    if (options.strictHtml) {\n      this._strictHtml = true;\n    } else if (options.safeHtml) {\n      this._safeHtml = true;\n    }\n  }\n}\n\nfunction getBuffer(options, name, charset) {\n  var buf = options[name];\n  if (buf == null || Array.isArray(buf) || Buffer.isBuffer(buf)) {\n    return buf;\n  }\n  return iconv.encode(buf + '', charset);\n}\n\nutil.inherits(WhistleTransform, Transform);\n\nfunction filterHtml(list, isSafe) {\n  if (!Array.isArray(list)) {\n    return list;\n  }\n  list = list.filter(function(buf) {\n    if (!buf || buf._strictHtml) {\n      return false;\n    }\n    if (isSafe || !buf._safeHtml) {\n      return true;\n    }\n    return false;\n  });\n  return fileMgr.joinData(list);\n}\n\nfunction joinData(list) {\n  return Array.isArray(list) ? fileMgr.joinData(list) : list;\n}\n\nWhistleTransform.prototype.allowInject = function (chunk) {\n  if (!this._isHtml) {\n    return true;\n  }\n  var first = chunk && chunk.toString().trim()[0];\n  var isStrict = !first || first === '<';\n  if (isStrict || this._strictHtml) {\n    if (isStrict) {\n      this._top = joinData(this._top);\n      this._body = joinData(this._body);\n      this._bottom = joinData(this._bottom);\n    }\n    return isStrict;\n  }\n  var isSafe = first !== '{' && first !== '[';\n  if (this._safeHtml && !isSafe) {\n    return false;\n  }\n  this._top = filterHtml(this._top, isSafe);\n  this._body = filterHtml(this._body, isSafe);\n  this._bottom = filterHtml(this._bottom, isSafe);\n  return true;\n};\n\nWhistleTransform.prototype._transform = function (chunk, encoding, callback) {\n  var self = this;\n  var cb = function () {\n    if (self._allowInject && self._ended && self._bottom) {\n      chunk = chunk ? Buffer.concat([chunk, self._bottom]) : self._bottom;\n      self._bottom = null;\n    }\n    if (chunk && self._speed) {\n      setTimeout(function () {\n        callback(null, chunk);\n      }, Math.round((chunk.length * 1000) / self._speed));\n    } else {\n      callback(null, chunk);\n    }\n  };\n\n  if (!self._ended) {\n    self._ended = !chunk;\n  }\n\n  if (!self._inited) {\n    self._allowInject = self.allowInject(chunk);\n    self._inited = true;\n    if (self._allowInject) {\n      if (self._body) {\n        self._ended = true;\n        chunk = self._body;\n        self._body = null;\n      }\n      var top = self._top;\n      if (top) {\n        if (self._isHtml && !self._noDoctype) {\n          top = Buffer.concat([DOCTYPE, top]);\n        }\n        chunk = chunk ? Buffer.concat([top, chunk]) : top;\n        self._top = null;\n      }\n    }\n    return self._delay ? setTimeout(cb, self._delay) : cb();\n  }\n\n  if (self._ended) {\n    chunk = null;\n  }\n\n  cb();\n};\n\nmodule.exports = WhistleTransform;\n"
  },
  {
    "path": "lib/util/zlib.js",
    "content": "var zlib = require('zlib');\nvar Limiter = require('async-limiter');\n\nvar limiter = new Limiter({ concurrency: 10 });\n\nfunction createConvenienceMethod(ctor, sync) {\n  return function (buffer, opts, callback) {\n    if (typeof opts === 'function') {\n      callback = opts;\n      opts = {};\n    }\n    return zlibBuffer(new ctor(opts), buffer, callback);\n  };\n}\n\nfunction zlibBuffer(engine, buffer, callback) {\n  engine.buffers = [];\n  engine.nread = 0;\n  engine.cb = callback;\n  engine.on('data', zlibBufferOnData);\n  engine.on('error', zlibBufferOnError);\n  engine.on('end', zlibBufferOnEnd);\n  engine.end(buffer);\n}\n\nfunction zlibBufferOnData(chunk) {\n  if (!this.buffers) this.buffers = [chunk];\n  else this.buffers.push(chunk);\n  this.nread += chunk.length;\n}\n\nfunction zlibBufferOnError(err) {\n  this.removeAllListeners('end');\n  this.cb(err);\n}\n\nfunction zlibBufferOnEnd() {\n  var buf;\n  var err;\n  var bufs = this.buffers;\n  buf = bufs.length === 1 ? bufs[0] : Buffer.concat(bufs, this.nread);\n  this.close();\n  if (err) this.cb(err);\n  else if (this._info) this.cb(null, { buffer: buf, engine: this });\n  else this.cb(null, buf);\n}\n\nvar noop = function(body, callback) {\n  return callback(null, body);\n};\n\nvar inflate = createConvenienceMethod(zlib.Inflate, false);\nvar gunzip = createConvenienceMethod(zlib.Gunzip, false);\nvar inflateRaw = createConvenienceMethod(zlib.InflateRaw, false);\nvar brotliDecompress = zlib.BrotliDecompress ? createConvenienceMethod(zlib.BrotliDecompress, false) : noop;\n\nfunction unzip(encoding, body, callback) {\n  if (body && typeof encoding === 'string') {\n    encoding = encoding.trim().toLowerCase();\n    if (encoding === 'gzip') {\n      if (body[0] !== 31 || body[1] !== 139) {\n        callback(null, body);\n        return true;\n      }\n      limiter.push(function (done) {\n        gunzip(body, function (err, data) {\n          done();\n          callback(err, data);\n        });\n      });\n      return;\n    }\n    if (encoding === 'br') {\n      limiter.push(function (done) {\n        brotliDecompress(body, function (err, data) {\n          done();\n          callback(err, data);\n        });\n      });\n      return;\n    }\n    if (encoding === 'deflate') {\n      limiter.push(function (done) {\n        inflate(body, function (err, data) {\n          if (!err) {\n            done();\n            return callback(null, data);\n          }\n          inflateRaw(body, function (e2, data2) {\n            done();\n            callback(e2, data2);\n          });\n        });\n      });\n      return;\n    }\n  }\n  callback(null, body);\n}\n\nmodule.exports = {\n  unzip: unzip,\n  inflate: inflate,\n  gunzip: gunzip,\n  inflateRaw: inflateRaw\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"whistle\",\n  \"description\": \"HTTP, HTTP2, HTTPS, Websocket debugging proxy\",\n  \"version\": \"2.10.1\",\n  \"dataDirname\": \".whistle\",\n  \"localUIHost\": \"local.whistlejs.com\",\n  \"port\": 8899,\n  \"sockets\": 256,\n  \"timeout\": 360000,\n  \"author\": \"avenwu <avwu@qq.com>\",\n  \"contributors\": [],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/avwo/whistle/issues\"\n  },\n  \"homepage\": \"https://wproxy.org\",\n  \"keywords\": [\n    \"proxy\",\n    \"fiddler\",\n    \"charles\",\n    \"websocket\",\n    \"http2\",\n    \"hosts\",\n    \"debug\",\n    \"mock\"\n  ],\n  \"bin\": {\n    \"whistle\": \"./bin/whistle.js\",\n    \"w2\": \"./bin/whistle.js\",\n    \"wproxy\": \"./bin/whistle.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/avwo/whistle.git\"\n  },\n  \"dependencies\": {\n    \"adm-zip\": \"0.5.16\",\n    \"async-limiter\": \"2.0.0\",\n    \"body-parser\": \"^1.20.3\",\n    \"colors\": \"1.1.2\",\n    \"express\": \"^4.21.2\",\n    \"extend\": \"^3.0.2\",\n    \"fs-extra2\": \"^1.0.0\",\n    \"hagent\": \"^0.9.3\",\n    \"hparser\": \"^0.5.0\",\n    \"iconv-lite\": \"^0.4.24\",\n    \"json5\": \"^2.2.3\",\n    \"lru-cache\": \"^4.1.1\",\n    \"mime\": \"^1.6.0\",\n    \"multer2\": \"^1.1.1\",\n    \"node-forge\": \"^1.3.1\",\n    \"node-pac\": \"^0.5.1\",\n    \"parseurl\": \"^1.3.1\",\n    \"pfork\": \"^0.6.3\",\n    \"pipestream\": \"^0.7.5\",\n    \"set-global-proxy\": \"^0.3.0\",\n    \"sni\": \"1.0.0\",\n    \"sockx\": \"^0.2.3\",\n    \"starting\": \"^8.0.3\",\n    \"weinre2\": \"^1.3.6\",\n    \"ws-parser\": \"^0.6.4\",\n    \"xml2js\": \"0.5.0\"\n  },\n  \"scripts\": {\n    \"dev\": \"webpack --config ./biz/webui/htdocs/src/webpack.config -w\",\n    \"start\": \"node bin/whistle.js run\",\n    \"test\": \"npm run lintfix && node test/index.test.js\",\n    \"lint\": \"eslint *.js ./lib ./bin ./biz ./test ./docs/script\",\n    \"lintfix\": \"eslint --fix *.js ./lib ./bin ./biz ./test ./docs/script\",\n    \"cov\": \"node_modules/istanbul/lib/cli.js cover node_modules/.bin/tape -- test/index.test.js\",\n    \"docs:dev\": \"vitepress dev docs\",\n    \"docs\": \"vitepress build docs\"\n  },\n  \"engines\": {\n    \"node\": \">= 8.8\"\n  },\n  \"devDependencies\": {\n    \"babel-core\": \"^6.7.6\",\n    \"babel-eslint\": \"^6.1.2\",\n    \"babel-loader\": \"^6.2.4\",\n    \"babel-plugin-transform-class-properties\": \"^6.24.1\",\n    \"babel-plugin-transform-object-rest-spread\": \"^6.26.0\",\n    \"babel-preset-env\": \"^1.7.0\",\n    \"babel-preset-react\": \"^6.5.0\",\n    \"babel-runtime\": \"^6.26.0\",\n    \"base64-js\": \"^1.3.0\",\n    \"bootstrap\": \"3.3.5\",\n    \"clipboard\": \"^2.0.11\",\n    \"codemirror\": \"^5.65.16\",\n    \"coveralls\": \"^3.1.1\",\n    \"css-loader\": \"0.16.0\",\n    \"eslint\": \"^2.8.0\",\n    \"eslint-plugin-react\": \"^4.3.0\",\n    \"file-loader\": \"0.8.4\",\n    \"istanbul\": \"^0.4.5\",\n    \"jquery\": \"^4.0.0\",\n    \"js-base64\": \"^2.4.5\",\n    \"qrcode\": \"1.2.0\",\n    \"react\": \"^15.6.2\",\n    \"react-base16-styling\": \"^0.5.1\",\n    \"react-dom\": \"^15.6.2\",\n    \"react-virtualized\": \"^9.21.2\",\n    \"request\": \"^2.74.0\",\n    \"should\": \"^13.2.3\",\n    \"should-http\": \"^0.1.1\",\n    \"style-loader\": \"0.12.3\",\n    \"tape\": \"^4.6.0\",\n    \"url-loader\": \"0.5.6\",\n    \"vitepress\": \"^1.6.3\",\n    \"webpack\": \"1.11.0\",\n    \"ws\": \"^1.1.4\"\n  }\n}\n"
  },
  {
    "path": "require.js",
    "content": "module.exports = require;\n"
  },
  {
    "path": "test/assets/certs/_.cert.w2.org.crt",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIC/TCCAmagAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjDEoMCYGA1UEAxMfV0hJ\r\nU1RMRShyb290QDAwOjE2OjNlOjAwOjBhOjJhKTELMAkGA1UEBhMCQ04xCzAJBgNV\r\nBAgTAlpKMQswCQYDVQQHEwJIWjEoMCYGA1UEChMfV0hJU1RMRShyb290QDAwOjE2\r\nOjNlOjAwOjBhOjJhKTEPMA0GA1UECxMGV1BST1hZMB4XDTE2MDcxODE0NDIyNloX\r\nDTI2MDcxODE0NDIyNlowgYwxKDAmBgNVBAMTH1dISVNUTEUocm9vdEAwMDoxNjoz\r\nZTowMDowYToyYSkxCzAJBgNVBAYTAkNOMQswCQYDVQQIEwJaSjELMAkGA1UEBxMC\r\nSFoxKDAmBgNVBAoTH1dISVNUTEUocm9vdEAwMDoxNjozZTowMDowYToyYSkxDzAN\r\nBgNVBAsTBldQUk9YWTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEApf+IJO8v\r\nCc20oDR+9RaBEUd1wgTmS7yfS/Fwnv/N7xdmf2k0C/2m21bAB5JJ42aqcnr3+akw\r\nPFF3VZg1lhxIZWitJGhwtxETGT/nOZ9xjubUCsZUvDN2RKjOYiVnKbFzYl97IKuH\r\nB+74Jm91V9JJz5rs46jjM6aHJ0cynzc9TKcCAwEAAaNtMGswDAYDVR0TBAUwAwEB\r\n/zALBgNVHQ8EBAMCAvQwOwYDVR0lBDQwMgYIKwYBBQUHAwEGCCsGAQUFBwMCBggr\r\nBgEFBQcDAwYIKwYBBQUHAwQGCCsGAQUFBwMIMBEGCWCGSAGG+EIBAQQEAwIA9zAN\r\nBgkqhkiG9w0BAQsFAAOBgQBW5oM2eJClAXzAtSRyXKKNPbqh/tbO9xasXETm2nK4\r\nz+g/wQZfoeDhZRdPzT+rzmFF9Y6kRhVlvrx3xrq4kS9GoHsjYtI0xPtX2IqLA+4M\r\nCIQwYoeqTFVDmoHqUytbJGWHa+VmMI6aYIqz9Sld2Dw39qfSViEv6LKQURrmTE+d\r\ntA==\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "test/assets/certs/_.cert.w2.org.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\r\nMIICXAIBAAKBgQCl/4gk7y8JzbSgNH71FoERR3XCBOZLvJ9L8XCe/83vF2Z/aTQL\r\n/abbVsAHkknjZqpyevf5qTA8UXdVmDWWHEhlaK0kaHC3ERMZP+c5n3GO5tQKxlS8\r\nM3ZEqM5iJWcpsXNiX3sgq4cH7vgmb3VX0knPmuzjqOMzpocnRzKfNz1MpwIDAQAB\r\nAoGAOBafadtniWh4H6mdPDLeaXg70dLV/cE+EesCorbMXn0JpQNnEqYiOvqU5/oF\r\n/VAzR3tFTpZcNgVQzRshABeOXilnQeVnNdxYV7rtPe7DpqM5Wvp6izUorJWqNGet\r\nsIrC/hlQupaYDXciYl9AP3yGs70CLMeorxTn0OSHy8e1sAECQQDlSiqFiTNVErEz\r\nj+47wsPrPbU/vnoPKK9HuZL93z1OmjzMekNWjBLh8l49VZfUTvNxxrD2LOp7n0EB\r\nX0AMBA53AkEAuVXlw1qfEan6UpKRWskuavCe6jj3mhWzu9LX04ektGEu3Y9uonPh\r\no2fkSuE+jLTAvfuKoXJS0GAQTa3Rq/JPUQJATn3kLpB4PSBH/xG/iT+0V/xo5qhr\r\nGnNgBZq2gigA0b6lH46fLKqI8EZLEo4RisF4PzO4cp2Pq8ApvbGAuFxPIwJBAKAS\r\nE1bNfxOHhn8ovcf2eFO+rNJJD3kSg2CWcvfscJGmWg7cIcbHZTt3sJIHxrlKKCou\r\nBgb4sZPtVEdy9+OVbXECQBN1jXBPaSVGPQeqjvEHymU6G2ulJqzXMDZciZLpHAwz\r\nGbjNIDkDzWdRLqmmRUqUXcA0mD2dxdWvkxBWRjntKsY=\r\n-----END RSA PRIVATE KEY-----\r\n"
  },
  {
    "path": "test/assets/certs/_root.crt",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIDNjCCAh6gAwIBAgIUQFKqeZDSAOWSG4gA20igPmy6EhEwDQYJKoZIhvcNAQEL\r\nBQAwgYUxITAfBgNVBAMTGHdoaXN0bGUuMTcyMTM5MDE1NTQ5MjY1NDELMAkGA1UE\r\nBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQHEwJIWjEkMCIGA1UEChMbMTcyMTM5\r\nMDE1NTQ5MjY1NC53cHJveHkub3JnMRMwEQYDVQQLEwp3cHJveHkub3JnMB4XDTI1\r\nMDkyNzA2MjgwN1oXDTI2MTAxNzA2MjgwN1owDzENMAsGA1UEAxMEcm9vdDCCASIw\r\nDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN23tJ/Jh/8Rv6J8C4rT78gsO2+e\r\ng9Mwq4uHwpTTqmTJn01n4idxvWsmV+NciClqmHy5GG3UVXnTcVqJkPec0+8yBK/1\r\nh/DKlLfM4/TCtbwHR4a8K7id3lpKAIUnl0prn0u71lIaxiASwdU3NFRO3oRVGO/s\r\nXOtCgEy1zsuaFfjEzy3eV5o6WpJxL8eLz7z2iUR4VMa/7fRyOjUgvX5zSSx+Cgy0\r\n9Qkwb5xad8rsqg8rQi/KPfKKDkcaItw35oMxq4HiQJVFxD6XMylTiLocbJLAwYao\r\n+7C8aUb4Ib9uceB4LM9cyuWJt3XAGD+mQR6h0gi9g8l6o+AK/6l8jfHG4UMCAwEA\r\nAaMTMBEwDwYDVR0RBAgwBoIEcm9vdDANBgkqhkiG9w0BAQsFAAOCAQEAvQb32CAH\r\nVmEdGxJIkRdrvdt5dDKf1OUbcSIWIOI8EpNTwZOdzNbjNPq5edpuZZgJ06vO+XPY\r\nHCPoR6Y/4EQX6oLig72HFfFi1s/3mMg7rk2bJ1wL6GMCuzK72mO0/zVFK11gZOdj\r\npbIch+OgnV6AcdYErVnUPNjIdv7SI/S5Ksf5nxmULkK9KXmtRWAocXjgkB5R5kny\r\nZJfLaPV5HInJx6D/pl76JdoyAabgoXvXQce8k7FoKyy15X45Dr1o6kTBWtkcOqBJ\r\nQrQW62OnUI28q+fsUmCmOhdRHjeNBbwiX7HL/t4F23NNonSDwae57FpScVCRA7zO\r\nF97wbO+VCpbuHg==\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "test/assets/certs/cert.w2.org.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\r\nMIICXgIBAAKBgQCkl91qX33cnRX3gTTfUi72r8t/D1HavEm0McCaMVDQGJBX0VgV\r\nswLNIMGEyAcVmSMVleJAgu225M7rtpl9TKzSrOhdHAj7/AjZWOL5GJEuxu0LsuH8\r\nZG9J6sS7HnVO//ZGJGjS5gGWk/gAfiII64+gurwasS7xiBHYYZOFTjRARwIDAQAB\r\nAoGBAIDuOVJfNQ+AublkrA8XqJQyxtxkGsGWZsHRi0b9xIkOBNvVsANnc5VNyGmD\r\n6xC/IZ2CCHZyWVXATFqWcguV6XXouF9R8TN+HcEaiZ9epEOnwOSPz4DdEtB+TZp+\r\nIh3l/NEzLRNxp7XVqI1k9OG5yguctyzfYcydFem/ZRWcMaxhAkEA0Rb+qGF7jb84\r\nxRBxFXxnKJVa6C2OSuDG1eecepMcYqNiUTimbk1/3qzJZqXmNBcIAfG/3a5dyM0O\r\nl1f2xeZ41wJBAMmFOPXkqS4gx8vItUJ36HSgXVOePbdmpBFRWD9R0uj83TuNc3V/\r\npD9jKnU/ZQ0DacI57F5zvIxtVy9iAP1iVhECQEJ5sRUPiRyTwxTEGW/fUVzRv0k5\r\n0pdzx0OSk2lVBB1IHKX+AMvoz9KX1KBR9lJxUBZuKbXtDdwddZogWVCp6ZkCQQC9\r\nkXcdyPZlEC0ixDHOzyF65Igmass/xWw9ZjoPhpdS2Nv8c3nTZDlL76s3FGWosjdA\r\noGB8EX+i0hCb4CNyOJkhAkEADdZsXlSL7e0qy7iE1IP7uHrfse1ZZhIX48DSK+Yg\r\nnqZ8YdcUsS3RlVOgahqizD723klbKeGq8Pi9t0wHamjHjg==\r\n-----END RSA PRIVATE KEY-----\r\n"
  },
  {
    "path": "test/assets/certs/root.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpAIBAAKCAQEA3be0n8mH/xG/onwLitPvyCw7b56D0zCri4fClNOqZMmfTWfi\r\nJ3G9ayZX41yIKWqYfLkYbdRVedNxWomQ95zT7zIEr/WH8MqUt8zj9MK1vAdHhrwr\r\nuJ3eWkoAhSeXSmufS7vWUhrGIBLB1Tc0VE7ehFUY7+xc60KATLXOy5oV+MTPLd5X\r\nmjpaknEvx4vPvPaJRHhUxr/t9HI6NSC9fnNJLH4KDLT1CTBvnFp3yuyqDytCL8o9\r\n8ooORxoi3DfmgzGrgeJAlUXEPpczKVOIuhxsksDBhqj7sLxpRvghv25x4Hgsz1zK\r\n5Ym3dcAYP6ZBHqHSCL2DyXqj4Ar/qXyN8cbhQwIDAQABAoIBAENjtFdCyn6pFt2c\r\npWd3XnCxnQkinX/B0coxo99XbHmA3NYKpq8Ff6TDPUBiXhJ0j0+NdNLhEG/caB40\r\nJOdV+AbCZbFTyBNUh5pMANdinRk+/ysBI6rT9QLtbjYKoA3jI0JCH/TJpYGXIeoc\r\nAuBDLpf7Q36dEWsCRfvK4MV6moYHPqgJCZcm0Pht5g91NEatjg1GAGg91pu269NJ\r\nbODJE0DNA75PcsJd9TtGtaHtH7A8BKP5JVt+51emDliG8/RYMJErT4usuvibNq7x\r\nt3TW07mfl5DpRjNdOc035VteuY2e+OnFOmI3qnq1P9w+mTxkV5ETG5hX2wqu5O7J\r\n8B+W9DkCgYEA/s9sSpEJdN+OyCn0SRuU2bpjI/wIMsXU5NSP4ieZkvRj3httgIKe\r\nQbf6byc1JIyNeauDcq+Mgnc35HDNxDWzSmJkKW/opU0iEpZSiQBXYzo02SRPFygQ\r\npypG95qEygP0qMNRi2Ne0yaVRpdMXWXl/OIGBykc7wN69Mt91zquSikCgYEA3sC6\r\nAzb5+vIpW/lJG6q/CabHrfOkEEQqo0Mu/sTN0s2Qe+1vGhPcMOwOBy3ZswAwG7S/\r\n5D5d6rpR4wlLUnyHddQ3Eao1Yab1yeGA6DMp2Doq2VxH34a+fnLNcaKLMk1bpjkc\r\n9lMzRMT+0fj6GxmOwXalVOISNeLQ29CMHAz9VYsCgYEAkIirjmhSit68KMHTdRyW\r\nBCJ7VdAB/nrE1b/UlZ2MuRSzCStZo4lzpydqgF4nAMJRBXDKMOVuuBpTs9pgfSlQ\r\nt6Kz2eVGe20TJKPr0RZGe1xq2biEvEfXmlqawpv9MnGn94pC2OPWx8Kc7duoT1ob\r\naKP0c19YsCKzPeOnT8xTTHECgYAI9Z+FuZRcb5kSRfiW0EtWzAqECKS0ssk0P+OI\r\nA8pzjCkfkvFBD0NwBE6cI0/6TyugMaj2OTv10QCyLRGGg9O+/YYpg7sZ7mk8cYGJ\r\n1WT9eXl3vKp3ZygKVfvFclA++bWR/gIYNkh9n57QOz90D5caWPdVbrJk0HauILlB\r\n95PI+QKBgQDE3nAacysEMngk/uBJQEVgYvbcA4J9xtaVfrDso1pkyh3ri0mRmEHR\r\n9nrKe6mGKytW9taoWthLQnyK5Jdm/h3ZtY8I1juAyCI5/Nk78SKdJY0rmwr0tdGp\r\n7P7QC/xoq3EU1ahcCDWiNFF1Tjt/qWSUy55HYZ2m/5tkpURcKtSAQg==\r\n-----END RSA PRIVATE KEY-----\r\n"
  },
  {
    "path": "test/assets/certs/test.crt",
    "content": "-----BEGIN CERTIFICATE-----\r\nMIIDVjCCAj6gAwIBAgIUnH21ZB+NjfLs6IM3VH4lTbBWs08wDQYJKoZIhvcNAQEL\r\nBQAwgYUxITAfBgNVBAMTGHdoaXN0bGUuMTUyNzM5MDEwNzA4NzY0NzELMAkGA1UE\r\nBhMCQ04xCzAJBgNVBAgTAlpKMQswCQYDVQQHEwJIWjEkMCIGA1UEChMbMTUyNzM5\r\nMDEwNzA4NzY0Ny53cHJveHkub3JnMRMwEQYDVQQLEwp3cHJveHkub3JnMB4XDTE3\r\nMDUyNzAzMDE0N1oXDTI4MDUyNzAzMDE0N1owHzEdMBsGA1UEAxMUKi50ZXN0Lndo\r\naXN0bGVqcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9N9DF\r\nyKsahSOfS4HT/VBglbfcF2M4klGTyM4fGtbu9ESaQ7afEluoaNoR6JB+FaXw8SAu\r\nleZITvzMu/Z0mGWMMoqh7UFgQb7CtI0cm2zeJTCsv41Gs7LPXpe6PUJ2Bz5I59zu\r\nMRpJu8bHAKu7GluGmT48BpOOQ0v+qpQbZLZjNaHW2RObmNnqkrh+h/+JySfMzFYW\r\nAtm0+2x9mecdObuxB5J9CqDCK5fuLTeeReucnTlsc8uTj9VvHwIvAZZu/aEXAAi+\r\nl0dPurUon9fqnyOvgu7SXLNPA7A5LnA5Ik5ec8ArFe1VA9ns+qXn3KgJLT1fRhAE\r\n84ztVn7EeN1z3wizAgMBAAGjIzAhMB8GA1UdEQQYMBaCFCoudGVzdC53aGlzdGxl\r\nanMuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQCmjy3lCvLwqzjYxYDgupF8sKx8g9Ej\r\nDqtM2eCvxInraX5zTO0ZjKz1y8YqfxOjHBE/O+FnbNr0GDt9vCF1UevG51+7JjEe\r\njPBjpLSEAgnBColfAHGkIUxsw5col1bG/fM4d2vYGAKQ1vKdlSjevjl3qL7k3tMy\r\niydf+9PmRXpuI6yUEzCFYrhf7TFJmQh38/Kb69JiZQsUkMoWFMSpTUWtSSqIS/J+\r\n8X5YGTBlTQ2Rth/aJhv3Z3TMKm6M3JIjSasJhJZhp6HC++EYBp9OiAty5UzkBC9p\r\nrAKl5Sis78Ia4CbQ6yBHK9iLATP3diygO+lnx+to+G1fqeRZEDryb4bX\r\n-----END CERTIFICATE-----\r\n"
  },
  {
    "path": "test/assets/certs/test.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIEpAIBAAKCAQEAvTfQxcirGoUjn0uB0/1QYJW33BdjOJJRk8jOHxrW7vREmkO2\r\nnxJbqGjaEeiQfhWl8PEgLpXmSE78zLv2dJhljDKKoe1BYEG+wrSNHJts3iUwrL+N\r\nRrOyz16Xuj1Cdgc+SOfc7jEaSbvGxwCruxpbhpk+PAaTjkNL/qqUG2S2YzWh1tkT\r\nm5jZ6pK4fof/icknzMxWFgLZtPtsfZnnHTm7sQeSfQqgwiuX7i03nkXrnJ05bHPL\r\nk4/Vbx8CLwGWbv2hFwAIvpdHT7q1KJ/X6p8jr4Lu0lyzTwOwOS5wOSJOXnPAKxXt\r\nVQPZ7Pql59yoCS09X0YQBPOM7VZ+xHjdc98IswIDAQABAoIBAE1ajBbPOmPVGgL9\r\nM04euMVDb66iQQXXi1IHPXyp1ERLx9/o3reZIa9vbfb0RF3HK9ecO7PPxBy7bc2k\r\n6dQwmRL/dn1MKPXY6steVHHleFQ4j/WPOYo13Fu6J/0THiByRZw4bbFSkZSjF1gz\r\nhgEvfUtBSpfveFMoX+D0iyfHvGhBx/PmEksodT7Oc5ZUFHFhYR2yEz5C53sAjvtt\r\nByDgQJ/Yar8ykWoM9SgZmxDAEpo+c6D7toM4MEBSYfNTBOsgZ9GPCgiCJl5OHkB6\r\nd7nGyiwWlItGQuSSTgGKuFVfNYQgdAh0KCCGCxj+n3fFyfrNYcgQGqcycxR54yCy\r\n5zaxKsECgYEA8ZVky66syF7zXPBEYcvN9fW0pqeQgOzuh7LZkODa/6gwnkfN+AUM\r\nYqOUooQQ4F7wh35NootX/U62+cUZLy4CIyZzvtJaoPo/1LETxzLgz7GRpAQM/Nd/\r\nAS5xleXuqD1oCk+le8Yqhe/P9y0InNJzgX7yCyDio/KrO3o4EsI/9icCgYEAyIJy\r\nndVGkgyefnGGjUFTxADzmKYc++0MHD/5G6A1Iuw9ql6xh/pd0hl02relJoIUN1Mi\r\nMK+2Ugtvu06SLQYSePPE+TSpqjpgiaTLDO59PUL2GhhunMF2mBKuPBeBg7Lp/50j\r\nYsKd5icdwneCniCQVe3DhSpELheEIQUX0gRTnJUCgYEAukO3fZvmOG+NMCFIeCbN\r\nXFDfwob70Yvctd/eA+h6sheZtNAkLJCn5f2+AjfVU0mgJrfpX+QUKYhhZ97h9hCB\r\nMVzabUx++BM61+DYIQRHdZuxcUZ2RRRaJi0LAspP4oPGDf0u2GoIK1ClhizAsTlK\r\nOZQJRy9Jz6KxB6DKTxxaeI0CgYBKOeg+P4bJaDyAKHyb7BQbWhLTbqMKK92w04nf\r\nTTZ+gB/v4vArm2aB8YSNtzA/CiZ7uJ3Oki2XP76UA2kfm8DY370KWBF6//Ne4Q99\r\n8QeR1ykNzf+crg2+DCROAJGkdGYlh0FjPRKA8fgVVnKXIQztkoAv2s60lWYHZ0BD\r\nMfDV5QKBgQCMfUe8c41R+f0+jGmrUdrBBJua9WVSWwt7wrsAHX67/PqW3sETVwrP\r\n0C0YLpZcg9zSG+QC4tSK2sWZ6nKC6RuTgg436GkP/YJ6iv1VElbtUOb/bBUTsWvy\r\nAuixpI/TThr8AfGDHYwKX9olk5kuKY9G8DOcWg9twIDwt4c9tgZGhQ==\r\n-----END RSA PRIVATE KEY-----\r\n"
  },
  {
    "path": "test/assets/files/1.txt",
    "content": "1"
  },
  {
    "path": "test/assets/files/2.txt",
    "content": "2"
  },
  {
    "path": "test/assets/files/3.txt",
    "content": "3"
  },
  {
    "path": "test/assets/files/gb2312.txt",
    "content": "GB2312"
  },
  {
    "path": "test/assets/files/mock-remote-key.txt",
    "content": "mock.remote-key.test.w2.org/test file://`{test.txt}`\nmock.remote-key2.test.w2.org/test file://`{test2.txt}`\nmock.remote-key3.test.w2.org/test host://{test-custom-ip}\nmock.remote-key4.test.w2.org/test proxy://{test-custom-ip}\n\n``` test-custom-ip\n127.0.0.1:8080\n```\n\n``` test2.txt\n${url}mock123\n```\n"
  },
  {
    "path": "test/assets/files/mock-script-key.txt",
    "content": "\nrules.push('* file://{test.txt} excludeFilter://*/test/script/api excludeFilter://*/test/script/proxy');\nrules.push('*/test/script/api host://{customIp}');\nrules.push('*/test/script/proxy proxy://{customIp}');\n\nvalues['test.txt'] = url + 'mock';\nvalues['customIp'] = '127.0.0.1:8080';\n\n"
  },
  {
    "path": "test/assets/files/rules.txt",
    "content": "str.w2.org/index.html file://`(${search.replace(a,b)})`\nstr.w2.org/index2.html file://`(${query.replace(/a/g,b)})`"
  },
  {
    "path": "test/assets/files/service-remote-key.txt",
    "content": "service.remote-key.test.w2.org/test file://`{test.txt}`\nservice.remote-key2.test.w2.org/test file://`{test2.txt}`\nservice.remote-key3.test.w2.org/test host://{test-custom-ip1}\nservice.remote-key4.test.w2.org/test proxy://{test-custom-ip1}\n\n``` test-custom-ip1\n127.0.0.1:8080\n```\n\n``` test2.txt\n${url}service123\n```\n"
  },
  {
    "path": "test/assets/files/service-script-key.txt",
    "content": "\nrules.push('* file://{test.txt} excludeFilter://*/test/script/api excludeFilter://*/test/script/proxy');\nrules.push('*/test/script/api host://{customIp}');\nrules.push('*/test/script/proxy proxy://{customIp}');\n\nvalues['test.txt'] = url + 'service';\nvalues['customIp'] = '127.0.0.1:8080';\n\n\n"
  },
  {
    "path": "test/assets/files/shadow-remote-key.txt",
    "content": "shadow.remote-key.test.w2.org/test file://`{test.txt}`\nshadow.remote-key2.test.w2.org/test file://`{test2.txt}`\nshadow.remote-key3.test.w2.org/test host://{test-custom-ip2}\nshadow.remote-key4.test.w2.org/test proxy://{test-custom-ip2}\n\n``` test-custom-ip2\n127.0.0.1:8080\n```\n\n``` test2.txt\n${url}shadow123\n```\n"
  },
  {
    "path": "test/assets/files/shadow-script-key.txt",
    "content": "\n\nrules.push('* file://{test.txt} excludeFilter://*/test/script/api excludeFilter://*/test/script/proxy');\nrules.push(`*/test/script/api host://{customIp}`);\nrules.push(`*/test/script/proxy proxy://{customIp}`);\n\nvalues['test.txt'] = url + 'shadow';\nvalues.customIp = '127.0.0.1:8080';\n"
  },
  {
    "path": "test/assets/files/storage/.backup/1.test1.tx",
    "content": "1"
  },
  {
    "path": "test/assets/files/storage/.backup/2.test2.tx",
    "content": "2"
  },
  {
    "path": "test/assets/files/storage/.backup/3.test3.tx",
    "content": "3"
  },
  {
    "path": "test/assets/files/storage/.backup/properties",
    "content": "{\"filesOrder\":[\"test1.tx\",\"test2.tx\",\"test3.tx\"]}\n"
  },
  {
    "path": "test/assets/files/storage/files/1.test1.tx",
    "content": "1"
  },
  {
    "path": "test/assets/files/storage/files/2.test2.tx",
    "content": "2"
  },
  {
    "path": "test/assets/files/storage/files/3.test3.tx",
    "content": "3"
  },
  {
    "path": "test/assets/files/storage/properties",
    "content": "{\"filesOrder\":[\"test1.tx\",\"test2.tx\",\"test3.tx\"]}\n"
  },
  {
    "path": "test/assets/rules/mock.txt",
    "content": "mock.test.w2.org/path/to file://{test.txt}\n@$__dirname$/assets/files/mock-remote-key.txt\nmock.script-key.test.w2.org/test/script reqScript://$__dirname$/assets/files/mock-script-key.txt\n\n``` test.txt\nmock\n```\n"
  },
  {
    "path": "test/assets/rules/service.txt",
    "content": "service.test.w2.org/path/to file://{test.txt}\n\n@$__dirname$/assets/files/service-remote-key.txt\nservice.script-key.test.w2.org/test/script reqScript://$__dirname$/assets/files/service-script-key.txt\n\n``` test.txt\nservice\n```\n"
  },
  {
    "path": "test/assets/rules/shadow.txt",
    "content": "shadow.test.w2.org/path/to file://{test.txt}\n\n@$__dirname$/assets/files/shadow-remote-key.txt\nshadow.script-key.test.w2.org/test/script reqScript://$__dirname$/assets/files/shadow-script-key.txt\n\n``` test.txt\nshadow\n```\n\n``` customIp\n1.1.1.1\n```\n"
  },
  {
    "path": "test/assets/values/json5.txt",
    "content": "\"json5\": 5\n"
  },
  {
    "path": "test/assets/values/rawFile.html",
    "content": "HTTP/1.1 500 OK\ncontent-type: text/plain\n\ntest2"
  },
  {
    "path": "test/assets/values/rawFile2.js",
    "content": "HTTP/1.1 200 OK\ncontent-type: application/javascript\nset-cookie: res_cookie_name=123; domain=.test.com; samesite=Lax; path=/; max-age=20000; expires=sxxxx; secure; httponly\n\n"
  },
  {
    "path": "test/assets/values/reqScript.js",
    "content": "rules.push(headers.host + ' file://{test.html}');\nreqScriptData.test = body || 123;\nvalues['test.html'] = render('<%=test%>', {test: body || 123});\n"
  },
  {
    "path": "test/assets/values/resScript.js",
    "content": "values.test = {'x-test': render('<%=test%>', {test: reqScriptData.test})};\nrules.push(headers.host + ' resHeaders://{test}');\n"
  },
  {
    "path": "test/assets/values/rulesFile.js",
    "content": "if (isLocalAddress()) {\n  rules.push('rf1.w2.org file://{test.json}');\n}\n"
  },
  {
    "path": "test/assets/values/rulesFile.txt",
    "content": "# rules\nrf2.w2.org file://{test2.json}"
  },
  {
    "path": "test/assets/values/rulesFile2.js",
    "content": "rules = null;"
  },
  {
    "path": "test/assets/values/test.json",
    "content": "{\n  \"test\": \"values\"\n}"
  },
  {
    "path": "test/assets/values/test.txt",
    "content": "assets/values\n"
  },
  {
    "path": "test/assets/values/test2.json",
    "content": "{\n  \"test\": \"values2\"\n}"
  },
  {
    "path": "test/assets/values/test3.json",
    "content": "{\n  \"test\": \"values3\"\n}"
  },
  {
    "path": "test/assets/values/tps.rules",
    "content": "# rules\n2.tps.whistlejs.com jsAppend://`{${reqCookie.name}}`"
  },
  {
    "path": "test/assets/values/tps1.json",
    "content": "{\n\t\"url\": \"${url}\",\n\t\"search\": \"${url.search}\",\n\t\"query\": \"${url.query}\",\n\t\"queryValue\": \"${url.query.name}\",\n\t\"host\": \"${url.host}\",\n\t\"hostname\": \"${url.hostname}\",\n\t\"path\": \"${url.path}\",\n\t\"pathname\": \"${url.pathname}\",\n\t\"reqId\": \"${reqId}\",\n\t\"now\": ${now},\n\t\"method\": \"${method}\",\n\t\"xff\": \"${reqHeaders.x-test}\",\n\t\"other\": \"${reqHeaders.other}\",\n\t\"cookie\": \"${reqCookie}\",\n\t\"cookieValue\": \"${reqCookie.cookieName}\",\n\t\"clientIp\": \"${clientIp}\"\n}"
  },
  {
    "path": "test/assets/values/tps2.json",
    "content": "{\n\t\"url\": \"${url}\",\n\t\"search\": \"${url.search}\",\n\t\"query\": \"${url.query}\",\n\t\"queryValue\": \"${url.query.name}\",\n\t\"host\": \"${url.host}\",\n\t\"hostname\": \"${url.hostname}\",\n\t\"path\": \"${url.path}\",\n\t\"pathname\": \"${url.pathname}\",\n\t\"reqId\": \"${reqId}\",\n\t\"now\": ${now},\n\t\"method\": \"${method}\",\n\t\"xff\": \"${reqHeaders.x-forwarded-for}\",\n\t\"other\": \"${reqHeaders.other}\",\n\t\"cookie\": \"${reqCookie}\",\n\t\"cookieValue\": \"${reqCookie.cookieName}\",\n\t\"clientIp\": \"${clientIp}\",\n\t\"statusCode\": \"${statusCode}\",\n\t\"serverIp\": \"${serverIp}\",\n\t\"resHeaderValue\": \"${resHeaders.x-res-header-name}\",\n\t\"resCookieValue\": \"${resCookie.res_cookie_name}\"\n}"
  },
  {
    "path": "test/config.test.js",
    "content": "\nmodule.exports = {\n  port: 6666,\n  serverPort: 8080,\n  wsPort: 8081,\n  httpsPort: 5566,\n  socksPort: 1080,\n  authSocksPort: 1118,\n  proxyPort: 7788,\n  httpServerPort: 2080,\n  httpsServerPort: 2081\n};\n"
  },
  {
    "path": "test/events.js",
    "content": "var EventEmitter = require('events');\n\nmodule.exports = new EventEmitter();"
  },
  {
    "path": "test/index.test.js",
    "content": "var http = require('http');\nvar https = require('https');\nvar parseUrl = require('url').parse;\nvar net = require('net');\nvar path = require('path');\nvar StringDecoder = require('string_decoder').StringDecoder;\nrequire('should');\nrequire('should-http');\nvar fs = require('fs');\nvar fse = require('fs-extra2');\nvar startWhistle = require('../index');\nvar socks = require('sockx');\nvar util = require('./util.test');\nvar config = require('./config.test');\nvar events = require('./events');\nvar values = util.getValues();\nvar testList = fs.readdirSync(path.join(__dirname, './units'));\nvar defaultRules = util.readText('rules.txt');\nvar options = {\n  key: fs.readFileSync(path.join(__dirname, 'assets/certs/root.key')),\n  cert: fs.readFileSync(path.join(__dirname, 'assets/certs/_root.crt'))\n};\nvar count = 6;\n\nvar WebSocketServer = require('ws').Server;\nvar wss = new WebSocketServer({ port: config.wsPort });\nvar WHISTLE_PATH = process.env.WHISTLE_PATH = __dirname;\nvar PLUGINS_PATH = path.join(WHISTLE_PATH, '.whistle/node_modules');\nvar mockRules = util.setPath(util.readText('assets/rules/mock.txt'));\nvar serviceRules = util.setPath(util.readText('assets/rules/service.txt'));\nvar shadowRules = util.setPath(util.readText('assets/rules/shadow.txt'));\n\n//Node7及以下使用非SNI Server\nif (process.versions.modules <= 51) {\n  require('hagent').serverAgent.existsServer = function() {\n    return true;\n  };\n}\n\nfse.removeSync(path.join(WHISTLE_PATH, '.whistle'));\nfse.copySync(path.join(__dirname, 'plugins'), PLUGINS_PATH);\n\nwss.on('connection', function connection(ws) {\n  var req = ws.upgradeReq;\n  ws.on('message', function(msg) {\n    ws.send(JSON.stringify({\n      type: 'server',\n      method: req.method,\n      headers: req.headers,\n      body: msg\n    }, null, '\\t'));\n  });\n});\n\nhttp.createServer(function(req, res) {\n  req.on('error', util.noop);\n  res.on('error', util.noop);\n\n  var body = '';\n  var decoder = new StringDecoder('utf8');\n  req.on('data', function(data) {\n    body += decoder.write(data);\n  });\n  req.on('end', function() {\n    body += decoder.end();\n    res.end(JSON.stringify({\n      type: 'server',\n      url: req.url,\n      method: req.method,\n      headers: req.headers,\n      body: body\n    }, null, '\\t'));\n  });\n}).listen(config.serverPort, startTest);\n\nhttps.createServer(options, function(req, res) {\n  if (req.url.indexOf('test-remote.rules') !== -1) {\n    return res.end('str2.w2.org/index.html file://`(${search.replace(a,b)})`\\nstr2.w2.org/index2.html file://`(${query.replace(/a/g,b)})`');\n  }\n  res.end(JSON.stringify({\n    headers: req.headers,\n    body: 'test'\n  }));\n}).listen(config.httpsPort, startTest);\nvalues['options.html'] = {\n  method: 'options'\n};\nvar proxy = startWhistle({\n  port: config.port,\n  storage: 'test_',\n  httpPort: config.httpServerPort,\n  httpsPort: config.httpsServerPort,\n  certDir: path.join(__dirname, 'assets/certs'),\n  debugMode: true,\n  localUIHost: 'local.whistle.com|local2.whistle.com&localn.whistle.com',\n  pluginHost: 'test=test.local.whistle.com|b.test.local.whistle.com&test3.local.whistle.com,',\n  mode: 'enableRequestHeaderRules', // 允许通过请求头带规则，主要用于第三方扩张\n  rules: {\n    Default: defaultRules,\n    test: {\n      rules: 'test.options.com file://{options.html}\\n@'\n        + path.join(__dirname, 'assets/files/rules.txt')\n        + '\\n@https://127.0.0.1:' + config.httpsPort + '/test-remote.rules'\n        + '\\ntest.key.test.w2.org/test file://{test.txt}\\n``` test.txt\\ntest\\n```',\n      enable: true\n    },\n    abc: '123'\n  },\n  values: values,\n  copy: true\n}, startTest);\n\nproxy.on('tunnelRequest', util.noop);\nproxy.on('wsRequest', util.noop);\nproxy.on('_request', util.noop);\nproxy.setUIHost('_');\nproxy.setUIHost();\nproxy.setPluginUIHost('test', '_');\nproxy.setPluginUIHost('whistle.test', '');\nvar socksServer = socks.createServer(function(info, accept, deny) {\n  var socket, client;\n  if (info.dstPort === 443) {\n    if (socket = accept(true)) {\n      client = net.connect({\n        host: '127.0.0.1',\n        port: 5566\n      }, function() {\n        socket.pipe(client).pipe(socket);\n      });\n    }\n    return;\n  }\n  if (info.dstPort === 8081) {\n    if (socket = accept(true)) {\n      client = net.connect({\n        host: '127.0.0.1',\n        port: 8081\n      }, function() {\n        socket.pipe(client).pipe(socket);\n      });\n    }\n    return;\n  }\n  if (socket = accept(true)) {\n    var body = JSON.stringify({\n      port: config.socksPort\n    });\n    socket.end([\n      'HTTP/1.1 200 OK',\n      'Connection: close',\n      'Content-Type: text/plain;charset=utf8',\n      'Content-Length: ' + Buffer.byteLength(body),\n      '',\n      body\n    ].join('\\r\\n'));\n  }\n});\n\nvar authSocksServer = socks.createServer(function(info, accept, deny) {\n  var socket;\n  if (info.dstPort === 443) {\n    if (socket = accept(true)) {\n      var client = net.connect({\n        host: '127.0.0.1',\n        port: 5566\n      }, function() {\n        socket.pipe(client).pipe(socket);\n      });\n    }\n    return;\n  }\n  if (socket = accept(true)) {\n    var body = JSON.stringify({\n      port: config.authSocksPort\n    });\n    socket.end([\n      'HTTP/1.1 200 OK',\n      'Connection: close',\n      'Content-Type: text/plain;charset=utf8',\n      'Content-Length: ' + Buffer.byteLength(body),\n      '',\n      body\n    ].join('\\r\\n'));\n  }\n});\n\nsocksServer.useAuth(socks.auth.None());\nauthSocksServer.useAuth(socks.auth.UserPassword(function(user, password, cb) {\n  cb(user == 'test' && password == 'hello1234');\n}));\n\nsocksServer.listen(config.socksPort, startTest);\nauthSocksServer.listen(config.authSocksPort, startTest);\n\n/**\n * 不用处理异常\n */\nvar server = http.createServer(function(req, res) {\n  var fullUrl = /^http:/.test(req.url) ? req.url : 'http://' + req.headers.host + req.url;\n  var options = parseUrl(fullUrl);\n  delete options.hostname;\n  options.host = '127.0.0.1';\n  options.method = req.method;\n  options.headers = req.headers;\n  var client = http.request(options, function(_res) {\n    _res.pipe(res);\n  });\n  req.pipe(client);\n});\n\nserver.on('connect', function(req, socket) {\n  var tunnelUrl = 'tunnel://' + (/^[^:\\/]+:\\d+$/.test(req.url) ? req.url : req.headers.host);\n  var options = parseUrl(tunnelUrl);\n  var client = net.connect({\n    host: '127.0.0.1',\n    port: options.port || 443\n  }, function() {\n    socket.pipe(client).pipe(socket);\n    socket.write('HTTP/1.1 200 Connection Established\\r\\nProxy-Agent: whistle/test\\r\\n\\r\\n');\n  });\n});\n\nserver.listen(config.proxyPort, startTest);\n\nfunction startTest() {\n  if (--count > 0) {\n    return;\n  }\n\n  proxy.rulesUtil.setMockRules(mockRules);\n  proxy.rulesUtil.setServiceRules(serviceRules);\n  proxy.setShadowRules(shadowRules);\n\n  var done;\n  function testAll() {\n    if (done) {\n      return;\n    }\n    done = true;\n    var lastUnit = 'ui.test.js';\n    testList.splice(testList.indexOf(lastUnit), 1);\n    testList.splice(testList.indexOf('tplStr.test.js'), 1);\n    testList.push('tplStr.test.js');\n    testList = testList.map(function(name) {\n      return require('./units/' + name);\n    });\n    lastUnit = require('./units/' + lastUnit);\n    var stride = 30;\n    var execUnit = function() {\n      var list = testList.slice(0, stride);\n      if (!list.length) {\n        util.setEnd();\n        lastUnit();\n        return true;\n      }\n      testList = testList.slice(stride);\n      list.forEach(function(fn) {\n        fn();\n      });\n    };\n    execUnit();\n    events.on('next', execUnit);\n  }\n  var index = 0;\n  (function getData() {\n    var query = '?name=host&value=com&url=com';\n    var dataUrl = 'http://local.whistlejs.com/cgi-bin/get-data';\n    if (++index > 2) {\n      index = 0;\n      dataUrl += query + 'ip=self,1.1.1.1';\n    } else if (index === 1) {\n      dataUrl += query + 'ip=self';\n    }\n    util.request(dataUrl, function() {\n      testAll();\n      setTimeout(getData, 10000);\n    });\n  })();\n}\n\n\n\n"
  },
  {
    "path": "test/plugins/@test/whistle.test3/index.js",
    "content": "exports.reqRulesServer = function(server) {\n  server.on('request', function(req, res) {\n    res.end('tp.w2.org/index.html resType://html rulesFile://{rulesFile.js}\\nths.w2.org file://{test.json}');\n  });\n};\n\nexports.tunnelRulesServer = function(server) {\n  server.on('request', function(req, res) {\n    res.end('ths.w2.org filter://https');\n  });\n};"
  },
  {
    "path": "test/plugins/@test/whistle.test3/package.json",
    "content": "{\n  \"name\": \"@test/whistle.test3\",\n  \"version\": \"0.0.0\",\n  \"description\": \"test3\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/plugins/@test/whistle.test3/rules.txt",
    "content": "* whistle.test3://test3\nrf1.w2.org rulesFile://{rulesFile.js} file://{rulesFile.js}\nrf2.w2.org rulesFile://{rulesFile.txt} file://{rulesFile.txt}\nrf3.w2.org rulesFile://{rulesFile2.js} file://{test3.json}\ndelete.test.whistlejs.com reqHeaders://(x-delete-req=test-req&x-delete-all=all) resHeaders://(x-delete-res=test-res&x-delete-all=all) filter://https\ndelete1.test.whistlejs.com reqHeaders://(x-delete-req=test-req&x-delete-all=all) resHeaders://(x-delete-res=test-res&x-delete-all=all) filter://https\nhttps://tp.w2.org/index.html test://global\ntp.w2.org 127.0.0.1:5566 filter://https|socks|proxy\nunknownprotocol.w2.org/index.html?doNotParseJson unknownprotocol://\nrwf.w2.org/index.html rawfile://{rawFile.html}\nhttps3.w2.org filter://https proxy://test:123@127.0.0.1:7788\n127.0.0.1:6666 rd2webui.w2.org\n\nws2.w2.org proxy://127.0.0.1:7788 enable://intercept\nws3.w2.org socks://127.0.0.1:1080 enable://intercept\nws4.w2.org proxy://127.0.0.1:7788\nws5.w2.org socks://127.0.0.1:1080\n\n/\\d+\\.server-agent\\.com/ enable://intercept host://127.0.0.1:5566\n\n# wildcard\n*.cn file://test enable://intercept\n**.wildcard.cn file://test/abc enable://intercept\n**.cn file://test/abc enable://intercept\n\n*.wildcard1.com file://test/abc enable://intercept\n**.wildcard1.com file://test enable://intercept\n\n^*.wildcard3.com/** file://test/abc/$2 enable://intercept\n^**.wildcard3.com/*** file://test/$2 enable://intercept\n\nhttp://*.wildcard2.com/1/2/3 file://test\nhttp://**.wildcard2.com/1/2/3 file://test\n\nhttp://*.wildcard2.com file://test\nhttp://**.wildcard2.com file://test\nhttps://*.wildcard2.com file://test/abc\ntunnel://*.wildcard2.com enable://intercept\nhttps://**.wildcard2.com file://test/abc\ntunnel://**.wildcard2.com enable://intercept\n\n"
  },
  {
    "path": "test/plugins/@test/whistle.test3/test/abc/abc/index.html",
    "content": "{\n  \"name\": \"https\"\n}\n"
  },
  {
    "path": "test/plugins/@test/whistle.test3/test/abc/index.html",
    "content": "{\n  \"name\": \"http\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.pass/index.js",
    "content": "var assert = require('assert');\n\nexports.server = function(server, options) {\n  const streamUtils = options.streamUtils;\n\n  server.on('request', function(req, res) {\n    var remoteAddr = req.originalReq.remoteAddress;\n    assert(remoteAddr === '127.0.0.1' || remoteAddr === '6.6.6.6');\n    assert(req.clientIp === '3.3.5.5' || req.clientIp === '5.5.5.5');\n    switch (req.headers['x-test-cmd']) {\n    case 'reqBuffer':\n      // 出错不用管\n      streamUtils.readBuffer(req, function(buffer) {\n        // url 为空自动采用当前请求的 url\n        var body = Buffer.concat([buffer, Buffer.from(' reqBuffer')]);\n        // 不用自己处理错误，可以忽略回调函数，Whistle 会自动返回给客户端\n        req.request({ body: body }/*, function(svrRes) {\n          res.writeHead(svrRes.statusCode, svrRes.statusMessage, svrRes.headers);\n          svrRes.pipe(res);\n        }*/);\n      });\n      return;\n    case 'reqText':\n      // 出错不用管\n      streamUtils.readText(req, function(text) {\n        req.request({ body: text + ' reqText' }/*, function(svrRes) {\n          res.writeHead(svrRes.statusCode, svrRes.statusMessage, svrRes.headers);\n          svrRes.pipe(res);\n        }*/);\n      });\n      return;\n    case 'reqJson':\n      // 出错不用管\n      streamUtils.readJson(req, function(data) {\n        data.a.b.c = 'streamUtils.readJson';\n        req.request({ body: JSON.stringify(data) }/*, function(svrRes) {\n          res.writeHead(svrRes.statusCode, svrRes.statusMessage, svrRes.headers);\n          svrRes.pipe(res);\n        }*/);\n      });\n      return;\n    case 'reqJson2':\n      // 另外一种获取请求体的方式，功能和上面完全一样，参数和回调函数的用法也完全一样\n      req.passThrough({\n        transformReq: function(req, next) {\n          // getBuffer, getText, getJson 都可以用来获取请求体，参数和回调函数的用法也完全一样\n          req.getJson(function(err, data) {\n            if (err) {\n              return next();\n            }\n            data.a.b.c = 'streamUtils.readJson';\n            next(JSON.stringify(data));\n          });\n        }\n      });\n      return;\n    case 'resBuffer':\n      var client = req.request(function(svrRes) {\n        streamUtils.readBuffer(svrRes, function(buffer) {\n          var body = Buffer.concat([Buffer.from('['), buffer, Buffer.from(']')]);\n          delete svrRes.headers['content-length'];\n          res.writeHead(svrRes.statusCode, svrRes.statusMessage, svrRes.headers);\n          res.end(body);\n        });\n      });\n      req.pipe(client);\n      return;\n    case 'resText':\n      req.passThrough({\n        transformRes: function(svrRes, next) {\n          svrRes.getText(function(err, text) {\n            if (err) {\n              return next();\n            }\n            next('[' + text + ', 123' + ']');\n          });\n        }\n      });\n      return;\n    case 'resJson':\n      req.passThrough({\n        transformRes: function(svrRes, next) {\n          svrRes.getJson(function(err, data) {\n            if (err) {\n              return next();\n            }\n            data.type = {a: {b: {c: 'readJson'}}};\n            next(JSON.stringify(data));\n          });\n        }\n      });\n      return;\n    case 'reqJson+resJson':\n      req.passThrough({\n        transformReq: function(req, next) {\n          // getBuffer, getText, getJson 都可以用来获取请求体，参数和回调函数的用法也完全一样\n          req.getJson(function(err, data) {\n            if (err) {\n              return next();\n            }\n            data.a.b.c = 'reqJson';\n            next(JSON.stringify(data));\n          });\n        },\n        transformRes: function(svrRes, next) {\n          svrRes.getJson(function(err, data) {\n            if (err) {\n              return next();\n            }\n            data.type = {a: {b: {c: 'resJson'}}};\n            next(JSON.stringify(data));\n          });\n        }\n      });\n      return;\n    default:\n      req.passThrough();\n    }\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pass/package.json",
    "content": "{\n  \"name\": \"whistle.pass\",\n  \"version\": \"1.0.0\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/index.js",
    "content": "\nexports.reqReadServer = require('./lib/reqReadServer');\nexports.reqWriteServer = require('./lib/reqWriteServer');\nexports.resReadServer = require('./lib/resReadServer');\nexports.resWriteServer = require('./lib/resWriteServer');\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/lib/assert.js",
    "content": "var assert = require('assert');\n\nmodule.exports = function(req) {\n  req = req.originalReq;\n  assert(req.pipeValue === 'test-http+[optional]');\n  assert(req.globalPluginVars.indexOf('global=http') !== -1);\n  assert(req.pluginVars.indexOf('private=http') !== -1);\n  assert(req.remoteAddress === '127.0.0.1' || req.remoteAddress === '6.6.6.6');\n  assert(req.globalValue === 'test');\n  if (req.fullUrl === 'http://header.test.weso.org/test/script/proxy?doNotParseJson') {\n    assert(req.globalPluginVars.indexOf('header') !== -1 && req.pluginVars.indexOf('doNotParseJson') !== -1);\n  }\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/lib/reqReadServer.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('request', function(req, res) {\n    assert(req);\n    req.on('data', (data) => {\n      return res.write(data);\n    });\n    req.on('end', () => res.end());\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/lib/reqWriteServer.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('request', function(req, res) {\n    assert(req);\n    req.pipe(res);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/lib/resReadServer.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('request', function(req, res) {\n    assert(req);\n    var body;\n    req.on('data', (data) => {\n      body = body ? Buffer.concat([body, data]) : data;\n    });\n    req.on('end', () => res.end(body));\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/lib/resWriteServer.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('request', function(req, res) {\n    assert(req);\n    req.pipe(res);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/package.json",
    "content": "{\n  \"name\": \"whistle.pipe-http\",\n  \"version\": \"1.0.0\",\n  \"whistleConfig\": {\n    \"pluginVars\": true,\n    \"hintList\": [\n      {\n        \"value\": \"123456\",\n        \"label\": \"ssssssss\",\n        \"help\": \"http://www.qq.com\"\n      },\n      \"sdfasfasfd\"\n    ],\n    \"hideShortProtocol\": true,\n    \"hideLongProtocol\": true,\n    \"hintUrl\": \"/\",\n    \"rulesUrl\": \"/\",\n    \"valuesUrl\": \"/\"\n  }\n}\n"
  },
  {
    "path": "test/plugins/whistle.pipe-http/rules.txt",
    "content": "http://* pipe://pipe-http(test-http+[optional]) %pipe-http.private=http @test\n%pipe-http.global=http\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/index.js",
    "content": "\nexports.tunnelReqRead = require('./lib/tunnelReqRead');\nexports.tunnelReqWrite = require('./lib/tunnelReqWrite');\nexports.tunnelResRead = require('./lib/tunnelResRead');\nexports.tunnelResWrite = require('./lib/tunnelResWrite');\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/lib/assert.js",
    "content": "var assert = require('assert');\n\nmodule.exports = function(req) {\n  req = req.originalReq;\n  assert(req.pipeValue === 'test-tunnel*[optional]');\n  assert(req.headers['user-agent'] !== 'test/whistle' || req.clientIp === '3.3.3.3');\n  assert(req.globalPluginVars.join() === [ 'test1', 'test2', 'global=321' ].join());\n  assert(req.pluginVars.join() === [ 'private=123' ].join());\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/lib/tunnelReqRead.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.pipe(socket);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/lib/tunnelReqWrite.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.on('data', (data) => {\n      socket.write(data);\n    });\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/lib/tunnelResRead.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.pipe(socket);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/lib/tunnelResWrite.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.on('data', (data) => {\n      socket.write(data);\n    });\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/package.json",
    "content": "{\n  \"name\": \"whistle.pipe-tunnel\",\n  \"version\": \"1.1.1\",\n  \"whistleConfig\": {\n    \"pluginVars\": true\n  }\n}\n"
  },
  {
    "path": "test/plugins/whistle.pipe-tunnel/rules.txt",
    "content": "tunnel://* pipe://pipe-tunnel(test-tunnel*[optional]) %pipe-tunnel.private=123\n%pipe-tunnel.global=321\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/index.js",
    "content": "\nexports.wsReqRead = require('./lib/wsReqRead');\nexports.wsReqWrite = require('./lib/wsReqWrite');\nexports.wsResRead = require('./lib/wsResRead');\nexports.wsResWrite = require('./lib/wsResWrite');\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/lib/assert.js",
    "content": "var assert = require('assert');\n\nmodule.exports = function(req) {\n  req = req.originalReq;\n  assert(req.pipeValue === 'test-ws&[optional]');\n  assert(req.globalPluginVars.indexOf('global=ws') !== -1);\n  assert(req.pluginVars.indexOf('private=ws') !== -1);\n  assert(req.remoteAddress === '127.0.0.1' || req.remoteAddress === '6.6.6.6');\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/lib/wsReqRead.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.on('data', (data, opts) => {\n      socket.write(data, opts);\n    });\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/lib/wsReqWrite.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.pipe(socket);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/lib/wsResRead.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.on('data', (data, opts) => {\n      socket.write(data, opts);\n    });\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/lib/wsResWrite.js",
    "content": "var assert = require('./assert');\n\nmodule.exports = function(server) {\n  server.on('connect', function(req, socket) {\n    assert(req);\n    socket.pipe(socket);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/package.json",
    "content": "{\n  \"name\": \"whistle.pipe-ws\",\n  \"version\": \"1.1.1\",\n  \"whistleConfig\": {\n    \"pluginVars\": true\n  }\n}\n"
  },
  {
    "path": "test/plugins/whistle.pipe-ws/rules.txt",
    "content": "ws://* pipe://pipe-ws(test-ws&[optional]) %pipe-ws.private=ws\n%pipe-ws.global=ws\n"
  },
  {
    "path": "test/plugins/whistle.test/_rules.txt",
    "content": "/statuscode4/ statusCode://100\n/statuscode5/ statusCode://1000\n\nreqBody://assets/files/body.txt /reqbody/ reqbody.test.whistlejs.com\nresBody://assets/files/body.txt /resbody/ resbody.test.whistlejs.com\nreqAppend://assets/files/append.txt /reqappend/ reqappend.test.whistlejs.com\nresAppend://assets/files/append.txt /resappend/ resappend.test.whistlejs.com\n\n/reqreplace/ reqReplace://assets/values/replace.json reqType://text\n/resreplace/ resReplace://assets/values/replace.json resType://html\n\n/dispatch/ dispatch://assets/dispatch.js host://127.0.0.1:8080\n\n/reqcors/ reqCors://assets/values/reqCors.json\n/rescors/ resCors://assets/values/resCors.json\n\n/reqheaders/ reqHeaders://assets/values/headers.json\n/resheaders/ resHeaders://assets/values/headers.json\n\nforward.test.whistlejs.com host.test.whistlejs.com\n\nlog://assets/files/log.js /log2/ /log3/\n\n/hostname/ hostname://xxx\n\n/weinre1/ resType://html weinre://xxx\n/weinre2/ resType://js weinre://xxx\n/weinre3/ resType://css weinre://xxx\n/weinre4/ weinre://xxx \n\n/tpl/ tpl://<assets/files/tpl.js>\n/raw/  rawfile://assets/files/rawfile.html\n/auth/ auth://xx:oo\n\n/req\\./ req://assets/values/req.json reqType://text\n\n/upload/ params://assets/values/upload.json host://127.0.0.1:8080\n\n/params2/ filter://rule\n\nrule.test.whistlejs.com host.test.whistlejs.com\nrule1.test.whistlejs.com host.test.whistlejs.com?test1\nrule2.test.whistlejs.com?abc=1 host.test.whistlejs.com?test2\nrule3.test.whistlejs.com/abc host.test.whistlejs.com?test2\nhttp://rule4.test.whistlejs.com/abc?abc=1 host.test.whistlejs.com?test2\n\n/prependbin/ reqPrepend://assets/files/bin/top.txt resPrepend://assets/files/bin/top.txt\n/bodybin/ reqBody://assets/files/bin/body.txt resBody://assets/files/bin/body.txt\n/appendbin/ reqAppend://assets/files/bin/bottom.txt resAppend://assets/files/bin/bottom.txt\n\n/plugin.whistlejs.com/ host://127.0.0.1:8080\n$http://ssi-include.whistlejs.com/index.html?doNotParseJson file://assets/files/ssi-include.html\n"
  },
  {
    "path": "test/plugins/whistle.test/assets/dispatch.js",
    "content": "params.dispatch='test';\nparams.timestamp = Date.now();"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/append.txt",
    "content": "append"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/bin/body.txt",
    "content": "会主"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/bin/bottom.txt",
    "content": "接班人!"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/bin/file.txt",
    "content": "我们是社会主义接班人!"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/bin/top.txt",
    "content": "我们是"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/body.txt",
    "content": "body"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/css.css",
    "content": "css"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/html.html",
    "content": "html"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/index.html",
    "content": "{\n  \"body\": \"html\"\n}"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/js.js",
    "content": "js"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/log.js",
    "content": "log"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/pac.js",
    "content": "function FindProxyForURL(url, host) {\n    // our local URLs from the domains below example.com don't need a proxy:\n    if (shExpMatch(host, \"*.example.com\"))\n    {\n        return \"DIRECT\";\n    }\n\n    // URLs within this network are accessed through\n    // port 8080 on fastproxy.example.com:\n    if (isInNet(host, \"10.0.0.0\", \"255.255.248.0\"))\n    {\n        return \"PROXY fastproxy.example.com:8080\";\n    }\n\n    // All other requests go through port 8080 of proxy.example.com.\n    // should that fail to respond, go directly to the WWW:\n    return \"PROXY 127.0.0.1:8080; DIRECT\";\n}"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/prepend.txt",
    "content": "prepend"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/rawfile.html",
    "content": "HTTP/1.1 500 OK\ncontent-type: text/plain\n\ntest"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/ssi-include.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<title>Test ssi include</title>\n</head>\n<body>\nHello world~~~\n#include('assets/files/ssi1.html')\nHello world~~~\n#include('assets/files/ssi2.html')\nHello world~~~\n#include('assets/files/ssi3.html')\n</body>\n</html>"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/ssi1.html",
    "content": "include1.html"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/ssi2.html",
    "content": "include2.html"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/ssi3.html",
    "content": "include3.html"
  },
  {
    "path": "test/plugins/whistle.test/assets/files/tpl.js",
    "content": "{callback}({ec: 0})"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/headers.json",
    "content": "x-test1: value1 \nx-test2: value2\nx-testN: valueN"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/replace.json",
    "content": "test: abc"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/req.json",
    "content": "{\n    \"method\": \"post\",\n    \"headers\": {\n        \"referer\": \"http://wproxy.org\"\n    },\n    \"top\": \"top\",\n    \"body\": \"body\",\n    \"bottom\": \"bottom\"\n}"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/reqCookies.json",
    "content": "test: abc"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/reqCors.json",
    "content": "origin: *\nmethod: POST\nheaders: x-test"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/res.json",
    "content": "{\n    \"headers\": {\n        \"Content-type\": \"text/plain\"\n    },\n    \"top\": \"top\",\n    \"body\": \"body\",\n    \"bottom\": \"bottom\"\n}"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/resCookies.json",
    "content": "{\n    \"key1\": \"value1\",\n    \"key2\": \"value2\",\n    \"keyN\": {\n        \"value\": \"value1\",\n        \"maxAge\": 60,\n        \"httpOnly\": true,\n        \"path\": \"/\",\n        \"secure\": true,\n        \"domain\": \".example.com\"\n    }\n}"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/resCors.json",
    "content": "origin: * \nmethods: POST\nheaders: x-test \ncredentials: true \nmaxAge: 300000"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/upload.json",
    "content": " {\n     \"name1\": \"value1\",\n     \"name2\": \"value2\",\n     \"file1\": {\n         \"filename\": \"text.txt\",\n         \"content\": \"xxxxxxxxxxxxxxx\"\n     },\n     \"file2\": {\n         \"value\": \"1234567890\"\n     }\n }"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/urlParams.json",
    "content": "test: abc"
  },
  {
    "path": "test/plugins/whistle.test/assets/values/urlReplace.json",
    "content": "/a/: e\nneme: user\n/a/g: tt"
  },
  {
    "path": "test/plugins/whistle.test/index.js",
    "content": "exports.server = require('./lib/server');\nexports.uiServer = require('./lib/uiServer');\nexports.rulesServer = require('./lib/rulesServer');\nexports.resRulesServer = require('./lib/resRulesServer');\nexports.statusServer = require('./lib/statusServer');\nexports.tunnelRulesServer = require('./lib/tunnelRulesServer');\nexports.tunnelServer = require('./lib/tunnelServer');"
  },
  {
    "path": "test/plugins/whistle.test/lib/resRulesServer.js",
    "content": "var express = require('express');\nvar app = express();\nvar util = require('./util');\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n\n    var fullUrl = util.getFullUrl(req);\n    var rules = [];\n\n    if (/js\\d/.test(fullUrl)) {\n      rules.push(req.hostname + ' js://assets/files/js.js');\n    }\n\n    if (/html\\d/.test(fullUrl)) {\n      rules.push('/html/ html://assets/files/html.html');\n    }\n\n    if (/css\\d/.test(fullUrl)) {\n      rules.push(req.hostname + ' css://assets/files/css.css');\n    }\n\n    if (/js1/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://html');\n    }\n\n    if (/js2/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://js');\n    }\n\n    if (/css1/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://html');\n    }\n\n    if (/css2/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://css');\n    }\n\n    if (/css3/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://js');\n    }\n\n    if (/html1/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://html');\n    }\n\n    if (/html2/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://css');\n    }\n\n    if (/html3/.test(fullUrl)) {\n      rules.push(req.hostname + ' resType://js');\n    }\n\n    if (/log1/.test(fullUrl)) {\n      rules.push('/log1/ resType://html');\n    }\n\n    if (/log2/.test(fullUrl)) {\n      rules.push('/log2/ resType://js');\n    }\n\n    if (/reswrite2/.test(fullUrl)) {\n      rules.push(req.hostname + ' resWriteRaw://assets/write/res/raw');\n    }\n\n    if (/reswrite/.test(fullUrl)) {\n      rules.push(req.hostname + ' resWrite://assets/write/res/body');\n    }\n\n    if (/disable/.test(fullUrl)) {\n      rules.push(req.hostname + ' disable://cache|cookie cache://60000 resCookies://assets/values/resCookies.json');\n    }\n\n    if (/attachment/.test(fullUrl)) {\n      rules.push('/attachment/ attachment://');\n    }\n\n    if (/res\\./.test(fullUrl)) {\n      rules.push('/res\\./ res://assets/values/res.json');\n    }\n\n    if (/values2.test.com/.test(fullUrl)) {\n      rules.push(JSON.stringify({\n        rules: '* resHeaders://{resHeaders}',\n        values: {\n          resHeaders: {\n            'x-res-test2': 'res'\n          }\n        }\n      }));\n    }\n\n    res.end(rules.join('\\n'));\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/rulesServer.js",
    "content": "var express = require('express');\nvar path = require('path');\nvar fs = require('fs');\nvar app = express();\nvar util = require('./util');\nvar ssi1 = fs.readFileSync(path.join(__dirname, '../assets/files/ssi1.html'), {encoding: 'utf8'});\nvar ssi2 = fs.readFileSync(path.join(__dirname, '../assets/files/ssi2.html'), {encoding: 'utf8'});\nvar ssi3 = fs.readFileSync(path.join(__dirname, '../assets/files/ssi3.html'), {encoding: 'utf8'});\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n    var fullUrl = util.getFullUrl(req);\n    var rules = [];\n\n    if (/host/.test(fullUrl)) {\n      rules.push(req.hostname + ' filter://rule');\n    }\n\n    if (/reqspeed/.test(fullUrl)) {\n      rules.push(req.hostname + ' reqSpeed://1');\n    }\n\n    if (/resspeed/.test(fullUrl)) {\n      res.end(req.hostname + ' resSpeed://1');\n    }\n\n    if (/xfile/.test(fullUrl)) {\n      rules.push(req.hostname + ' xfile://assets/files');\n    }\n\n    if (/file/.test(fullUrl)) {\n      rules.push(req.hostname + ' file://assets/files');\n    }\n\n    if (/xtpl/.test(fullUrl)) {\n      rules.push(req.hostname + ' xtpl://assets/files');\n    }\n\n    if (/xraw/.test(fullUrl)) {\n      rules.push(req.hostname + ' xrawfile://assets/files');\n    }\n\n    if (/referer/.test(fullUrl)) {\n      rules.push('/referer/ referer://xxx');\n    }\n\n    if (/reqtype/.test(fullUrl)) {\n      rules.push('/reqtype/ reqType://text');\n    }\n\n    if (/restype/.test(fullUrl)) {\n      rules.push('/restype/ resType://html');\n    }\n\n    if (/reqcookies/.test(fullUrl)) {\n      rules.push('/reqcookies/ reqCookies://assets/values/reqCookies.json');\n    }\n\n    if (/rescookies/.test(fullUrl)) {\n      rules.push('/rescookies/ resCookies://assets/values/resCookies.json');\n    }\n\n    if (/reqprepend/.test(fullUrl)) {\n      rules.push('/reqprepend/ reqPrepend://assets/files/prepend.txt');\n    }\n\n    if (/resprepend/.test(fullUrl)) {\n      rules.push('/resprepend/ resPrepend://assets/files/prepend.txt');\n    }\n\n    if (/urlparams/.test(fullUrl)) {\n      rules.push('/urlparams/ urlParams://assets/values/urlParams.json');\n    }\n\n    if (/params/.test(fullUrl)) {\n      rules.push('/params/ params://assets/values/urlParams.json');\n    }\n\n    if (/reqwriteraw/.test(fullUrl)) {\n      rules.push(req.hostname + ' reqWriteRaw://assets/write/req/raw');\n    }\n\n    if (/reqwrite/.test(fullUrl)) {\n      rules.push(req.hostname + ' reqWrite://assets/write/req/body');\n    }\n\n    if (/exportsurl/.test(fullUrl)) {\n      rules.push(req.hostname + ' exportsUrl://assets/write/exportsUrl.txt');\n    }\n\n    if (/exports/.test(fullUrl)) {\n      rules.push(req.hostname + ' exports://assets/write/exports.txt');\n    }\n\n    if (/ws1/.test(fullUrl)) {\n      rules.push('/ws1/ host://127.0.0.1:9999');\n    }\n\n    if (/mp1.w2.org/.test(fullUrl)) {\n      rules.push('* host://127.0.0.1:8080');\n    }\n\n    if (/values1.avenwu.com/.test(fullUrl)) {\n      return res.end(JSON.stringify({\n        rules: '* file://{test} reqHeaders://{reqHeaders} resHeaders://{resHeaders}',\n        values: {\n          test: {\n            abc: 123\n          },\n          reqHeaders: {\n            'x-req-test': 'req'\n          },\n          resHeaders: {\n            'x-res-test': 'res'\n          }\n        }\n      }));\n    }\n\n    if (/values2.test.com/.test(fullUrl)) {\n      return res.end(JSON.stringify({\n        rules: '*  reqHeaders://{reqHeaders} resHeaders://{resHeaders} reqReplace://{reqReplace}',\n        values: {\n          test: {\n            abc: 123\n          },\n          reqHeaders: {\n            'x-req-test': 'req'\n          },\n          resHeaders: {\n            'x-res-test': 'res'\n          },\n          reqReplace: {\n            ssi1: ssi1,\n            ssi2: ssi2,\n            '/ssi3/': ssi3\n          }\n        }\n      }));\n    }\n\n    if (/ssi-include.whistlejs.com/.test(fullUrl)) {\n      return res.end(JSON.stringify({\n        rules: '* resReplace://{resReplace}',\n        values: {\n          resReplace: {\n            '#include(\\'assets/files/ssi2.html\\')': ssi2,\n            '#include(\\'assets/files/ssi3.html\\')': ssi3,\n            '/#include\\\\((([\\'\\\"])?([^\\\\s]+)\\\\2)\\\\)/': ssi1\n          }\n        }\n      }));\n    }\n\n    res.end(rules.join('\\n'));\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/server.js",
    "content": "var express = require('express');\nvar app = express();\nvar WebSocketServer = require('ws').Server;\nvar StringDecoder = require('string_decoder').StringDecoder;\nvar qs = require('querystring');\nvar util = require('./util');\nvar RES_BODY_RE = /\\?resBody=([^&#]*)/;\n\nfunction startHttpServer(app) {\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n\n    var body = '';\n    var decoder = new StringDecoder('utf8');\n    req.on('data', function(data) {\n      body += decoder.write(data);\n    });\n    req.on('end', function() {\n      body += decoder.end();\n      var fullUrl = util.getFullUrl(req);\n      if (RES_BODY_RE.test(fullUrl)) {\n        res.end(qs.unescape(RegExp.$1));\n      } else {\n        res.end(JSON.stringify({\n          url: fullUrl,\n          method: req.method,\n          headers: req.headers,\n          ruleValue: util.getRuleValue(req),\n          host: util.getHost(req),\n          body: body\n        }, null, '\\t'));\n      }\n    });\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n}\n\nfunction startWebsocketServer(ws) {\n  ws.on('connection', function(ws) {\n    var req = ws.upgradeReq;\n    ws.on('message', function(msg) {\n      ws.send(JSON.stringify({\n        url: util.getFullUrl(req),\n        method: req.method,\n        headers: req.headers,\n        ruleValue: util.getRuleValue(req),\n        host: util.getHost(req),\n        body: msg\n      }, null, '\\t'));\n    });\n  });\n}\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  startHttpServer(app);\n  startWebsocketServer(new WebSocketServer({ server: server }));\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/statusServer.js",
    "content": "var express = require('express');\nvar app = express();\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n    req.setEncoding('utf8');\n    var body = '';\n    req.on('data', function(data) {\n      body += data;\n    });\n\n    req.on('end', function() {\n      res.end();\n    });\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/tunnelRulesServer.js",
    "content": "var express = require('express');\nvar app = express();\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n    var rules = [];\n    var host = req.headers.host;\n    if (/^(\\d+)\\.tnl[56]\\.whistlejs\\.com/.test(host)) {\n      rules.push('* host://127.0.0.1:' + RegExp.$1);\n    } else if (host == 'tnl2.whistlejs.com') {\n      rules.push('tunnel://tnl2.whistlejs.com host://127.0.0.1:8080');\n    } else if (host == 'tnl3.whistlejs.com') {\n      rules.push('tnl3.whistlejs.com host://127.0.0.1:8080');\n    } else if (host == 'break.whistlejs.com') {\n      rules.push('tunnel://break.whistlejs.com disable://tunnel');\n    }\n\n    if ('ts.whistlejs.com' != req.headers.host) {\n      rules.push('* filter://rule');\n    }\n    res.end(rules.join('\\n'));\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/tunnelServer.js",
    "content": "var net = require('net');\nvar config = require('../package.json');\n\nfunction close() {\n  this.destroy();\n}\n\nmodule.exports = function(server, options) {\n  server.on('connect', function(req, socket) {\n    socket.on('error', close);\n    if (req.originalReq.ruleValue != 'none') {\n      throw new Error('wrong rule value');\n    }\n    var resSocket = net.connect({\n      port: 8080,\n      host: '127.0.0.1'\n    }, function() {\n      socket.write('HTTP/1.1 200 Connection Established\\r\\nProxy-Agent: ' + config.name + '\\r\\n\\r\\n');\n      socket.pipe(resSocket).pipe(socket);\n      resSocket.on('error', close);\n    });\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/uiServer.js",
    "content": "var express = require('express');\nvar app = express();\nvar WebSocketServer = require('ws').Server;\n\nfunction startWebsocketServer(ws) {\n  ws.on('connection', function(ws) {\n    var req = ws.upgradeReq;\n    ws.on('message', function(msg) {\n      ws.send(JSON.stringify({\n        headers: req.headers,\n        body: msg\n      }, null, '\\t'));\n    });\n  });\n}\n\nmodule.exports = function(server, options) {\n  server.on('request', app);\n  startWebsocketServer(new WebSocketServer({ server: server }));\n  app.use(function(req, res, next) {\n    req.on('error', next);\n    res.on('error', next);\n\n    res.end('uiServer');\n  });\n\n  app.use(function(err, req, res, next) {\n    res.sendStatus(500);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test/lib/util.js",
    "content": "\nfunction getRuleValue(req) {\n  return req.originalReq.ruleValue;\n}\n\nexports.getRuleValue = getRuleValue;\n\nfunction getFullUrl(req) {\n  var fullUrl = req.fullUrl;\n  return fullUrl || '';\n}\n\nexports.getFullUrl = getFullUrl;\n\nfunction getRealUrl(req) {\n  return req.originalReq.realUrl;\n}\n\nexports.getRealUrl = getRealUrl;\n\nfunction getReqId(req) {\n  return req.originalReq.reqId;\n}\n\nexports.getReqId = getReqId;\n\nfunction getStatusCode(req) {\n  return req.originalReq.statusCode;\n}\n\nexports.getStatusCode = getStatusCode;\n\n\nfunction getHost(req) {\n  return req.originalReq.hostValue;\n}\n\nexports.getHost = getHost;\n"
  },
  {
    "path": "test/plugins/whistle.test/package.json",
    "content": "{\n  \"name\": \"whistle.test\",\n  \"version\": \"1.0.0\",\n  \"description\": \"whistle的单元测试用例\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.test/rules.txt",
    "content": "/test.whistlejs.com/ test://none host://127.0.0.1:8080 filter://https\n/reqdelay/ reqDelay://1000\n/resdelay/ resDelay://1000\n/ua/ ua://xxx\n/reqcharset/ reqCharset://utf8 \n/rescharset/ resCharset://utf8\n/etag/ etag://xxx\n/accept/ accept://xxx\n/cache/ cache://60000\n/statuscode1/ statusCode://999\n/statuscode2/ statusCode://101\n/statuscode3/ statusCode://500\n/redirect/ redirect://http://test.whistlejs.com\n/replacestatus/ replaceStatus://500\n\n/log1/ log://assets/files/log.js\n\n/proxy/ proxy://127.0.0.1:8080\n\n/connect/ host://127.0.0.1:8081\n/testrule5\\.([^\\/]+)\\/([^?]*\\/?)\\?.*test=([^&]+)/ host.$1/$2?test=$3\n/testrule6\\.([^\\/]+)\\/([^?]*\\/?)\\?.*test=([^&]+)/ $1/$2?test=$3\n\n/urlreplace/ urlReplace://assets/values/urlReplace.json\n\n/plugin/ plugin://test\n\n/321/ test://abc filter://https\n\ntnl1.whistlejs.com host://127.0.0.1:8080 filter://https\ntnl2.whistlejs.com 127.0.0.1 plugin://test filter://tunnel\ntnl3.whistlejs.com test://none filter://tunnel\ntnl4.whistlejs.com proxy://127.0.0.1:8080\n/tnl5.whistlejs.com/ plugin://test\n/tnl6.whistlejs.com/ test://test\nbreak.whistlejs.com test://test\nts.whistlejs.com test://none\n/values1/ test://none\n/values2/ test://none\npac.test.com pac://assets/files/pac.js\n$ssi-include.whistlejs.com/index.html?doNotParseJson test://none\n\nhttps.w2.org host://127.0.0.1:5566\nhttps1.w2.org proxy://127.0.0.1:7788 \nhttp://https2.w2.org https1.w2.org:8080\nhttps2.w2.org https://https1.w2.org:5566\n\nsocks1.w2.org socks://127.0.0.1\nsocks2.w2.org socks://test:hello1234@127.0.0.1:1118\nsocks3.w2.org socks://127.0.0.1 filter://https\nsocks4.w2.org socks://test:hello1234@127.0.0.1:1118 filter://https\n\nsocks5.w2.org socks1.w2.org\nsocks6.w2.org socks2.w2.org\nsocks7.w2.org socks3.w2.org\nsocks8.w2.org socks4.w2.org\n\nmp1.w2.org test://123\n"
  },
  {
    "path": "test/plugins/whistle.test-values/index.js",
    "content": "\nexports.uiServer = function(server) {\n  server.on('request', function(req, res) {\n    var key = decodeURIComponent(req.url.substring(req.url.indexOf('=') + 1));\n    res.end(key + '/' + req.originalReq.ruleProtocol);\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test-values/package.json",
    "content": "{\n  \"name\": \"whistle.test-values\",\n  \"version\": \"1.0.0\",\n  \"description\": \"whistle的单元测试用例\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.test-values/rules.txt",
    "content": "\ntest-values.whistlejs.com reqHeaders://$test-values/x-test-req=123  resHeaders://$test-values/x-test-res=456 file://$test-values/x-test-body=测试789\n"
  },
  {
    "path": "test/plugins/whistle.test1/_rules.txt",
    "content": "delete.test.whistlejs.com delete://req.headers.x-delete-req|res.headers.x-delete-res|headers.x-delete-all"
  },
  {
    "path": "test/plugins/whistle.test1/index.js",
    "content": "exports.rulesServer = require('./lib/rulesServer');\n"
  },
  {
    "path": "test/plugins/whistle.test1/lib/rulesServer.js",
    "content": "\nmodule.exports = function(server, options) {\n  server.on('request', function(req, res) {\n    if (req.headers.host === 'var1.wproxy.org') {\n      return res.end(JSON.stringify({\n        rules: '* file://(${json0}${json1},${json2},${json3},${test.txt},${json5.txt}${jsonN})\\n``` test.txt\\n\"json4\": 4\\n```',\n        values: {\n          json0: '{',\n          json1: '\"json1\": 1',\n          json2: '\"json2\": 2',\n          json3: '\"json3\": 3',\n          jsonN: '}'\n        }\n      }));\n    }\n    res.end('mp1.w2.org/index.html resType://html\\nhttps2.w2.org filter://https');\n  });\n};\n"
  },
  {
    "path": "test/plugins/whistle.test1/package.json",
    "content": "{\n  \"name\": \"whistle.test1\",\n  \"version\": \"0.0.0\",\n  \"whistleConfig\": {\n    \"pluginVars\": \"http://www.test.com\"\n  },\n  \"description\": \"test1\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.test1/rules.txt",
    "content": "* whistle.test1://test1 %test1=123\n\n%test1=abc\n"
  },
  {
    "path": "test/plugins/whistle.test2/_rules.txt",
    "content": "mp1.w2.org/index.html resCharset://gbk\nenable3.w2.org test://abc\nenable4.w2.org test://123\n\nrange2.whistlejs.com file://test.txt\n"
  },
  {
    "path": "test/plugins/whistle.test2/index.js",
    "content": "exports.statsServer = function(server, options) {\n  server.on('request', function(req, res) {\n    res.end();\n  });\n};\n\nexports.auth = function(req) {\n  var url = req.fullUrl;\n  if (url.indexOf('/test-plugin-auth-hook/') === -1) {\n    return true;\n  }\n  if (url.indexOf('/test-plugin-auth-hook/forbidden/login') !== -1) {\n    req.setLogin(true);\n    req.setHtml('test-plugin-auth-hook/login');\n    return false;\n  }\n  if (url.indexOf('/test-plugin-auth-hook/redirect') !== -1) {\n    req.setRedirect('https://test/test-plugin-auth-hook/redirect');\n    return false;\n  }\n  if (url.indexOf('/test-plugin-auth-hook/forbidden') !== -1) {\n    req.setHtml('test-plugin-auth-hook');\n    return false;\n  }\n  req.setUrl('http://127.0.0.1:8080');\n  return false;\n};\n"
  },
  {
    "path": "test/plugins/whistle.test2/package.json",
    "content": "{\n  \"name\": \"whistle.test2\",\n  \"version\": \"0.0.0\",\n  \"description\": \"test2\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/plugins/whistle.test2/rules.txt",
    "content": "* whistle.test2://test2\nenable1.w2.org enable://intercept test://test proxy://test:123@127.0.0.1:7788\nenable2.w2.org enable://intercept test://test proxy://test:123@127.0.0.1:7788\nenable2.w2.org ignore://test\n\nenable3.w2.org enable://intercept proxy://test:123@127.0.0.1:7788 whistle.test2://\nenable4.w2.org enable://intercept proxy://test:123@127.0.0.1:7788 whistle.test2://\n\nenable4.w2.org ignore://whistle.test2\n\nvar1.wproxy.org/index.html whistle.test1://\n"
  },
  {
    "path": "test/plugins/whistle.test2/test.txt",
    "content": "0123456789"
  },
  {
    "path": "test/proxy/disable.test.js",
    "content": "var disableProxy = require('../../bin/proxy').disableProxy;\n\nconsole.log(disableProxy()); // eslint-disable-line\n"
  },
  {
    "path": "test/proxy/enable.test.js",
    "content": "var enableProxy = require('../../bin/proxy').enableProxy;\n\nconsole.log(enableProxy({  // eslint-disable-line\n  host: '127.0.0.1',\n  port: 8899,\n  bypass: '<local>'\n}));\n"
  },
  {
    "path": "test/rules.txt",
    "content": "/redirect/ redirect://http://test.whistlejs.com\nrf1.w2.org rulesFile://{rulesFile.js} file://{rulesFile.js}\nrf2.w2.org rulesFile://{rulesFile.txt} file://{rulesFile.txt}\nrf3.w2.org rulesFile://{rulesFile2.js} file://{test3.json}\ndelete.test.whistlejs.com reqHeaders://(x-delete-req=test-req&x-delete-all=all) resHeaders://(x-delete-res=test-res&x-delete-all=all) filter://https\ndelete1.test.whistlejs.com reqHeaders://(x-delete-req=test-req&x-delete-all=all) resHeaders://(x-delete-res=test-res&x-delete-all=all) filter://https\nhttps://tp.w2.org/index.html test://global\ntp.w2.org 127.0.0.1:5566 filter://https\ntest.whistlejs.com/ups.html filter://https urlParams://(rule=ws1.test.whistlejs.com)\nwss://test.whistlejs.com/index2.html?abc=321 filter://https params://(rule=ws1.test.whistlejs.com)\n\nreqscript.w2.org reqScript://{reqScript.js} resScript://{resScript.js}\n\nws://status.whistlejs.com status://101\ntunnel://127.0.0.1:8080 filter://https xproxy://127.0.0.1:19999\nhttp://127.0.0.1:6666/abc.htm 127.0.0.1:8080\ntest.internal.path.com proxy://127.0.0.1:8080\n\n%pipe-ws=test1\n%pipe-ws=test2\n%pipe-http=test1\n%pipe-http=test2\n%pipe-tunnel=test1\n%pipe-tunnel=test2\n%test1=test1\n%test1=test2\n%test2=test1\n%test2=test2\n%test3=test1\n%test3=test2\n%test32=test1\n%test32=test2\n\nhttp://test1.pass.through.com/index.html pass://passThrough 127.0.0.1:8080 includeFilter://clientIp:3.3.5.5\nhttp://test2.pass.through.com/index.html pass://passThrough 127.0.0.1:8080 includeFilter://reqH.x-whistle-client-id:avenwu-test-id\nhttp://test3.pass.through.com/index.html pass://passThrough 127.0.0.1:8080 includeFilter://remoteAddress:6.6.6.6\nhttp://test-*.pass.through.com/index.html pass://passThrough 127.0.0.1:8080\n\nparams.test.whistlejs.com/index.html resMerge://(resMerge=test) resType://js\nweinre1.test2.whistlejs.com:1234 weinre://xxx file://(hello) log://test resType://html enable://capture ignore://plugin\nweinre1.test2.whistlejs.com weinre://xxx file://(hello) log://test resType://js enable://capture ignore://plugin\n\nfilter.com enable://capture\nfilter.com/index.html file://({\"ec\":0}) excludeFilter://m:post excludeFilter://m:/delete/\nfilter.com/index.html file://({\"ec\":1}) includeFilter://m:delete includeFilter://b:test\nfilter.com/index.html file://({\"ec\":2}) includeFilter://b:555 excludeFilter://reqH:x-test=/\\d+/\nfilter.com/index.html file://({\"ec\":3}) excludeFilter://h:x-test=/\\d+/\nfilter.com/index.html file://({\"ec\":4})\n\nfilter2.com/test.html file://({\"ec\":123}) includeFilter:///abc/\nfilter2.com/test.html file://({\"ec\":321}) excludeFilter:///cba/\nfilter2.com/test.html file://({\"ec\":333})\n\n1.tps.whistlejs.com file://() enable://capture resAppend://`{${url.query.name}}`\n2.tps.whistlejs.com resScript://{tps.rules} rawfile://{rawFile2.js} enable://capture\nline`\njsbody.whistlejs.com resType://js jsBody://(1) jsBody://(2)\njsPrepend://(-1) jsPrepend://(-2) jsAppend://(3) jsAppend://(4)\nfile://(0)\n`\njsbody2.whistlejs.com resType://html jsPrepend://http://1 jsAppend://https://2 file://(0) disable://keepAlive\nhttp://127.0.0.1:8080/xhost.html xhost://127.0.0.1:37621\nhttp://127.0.0.1:8080/xproxy.html xproxy://127.0.0.1:37621 enable://showHost\n\nline`\nprependhtml.whistlejs.com resType://html htmlBody://(1) htmlBody://(2)\nhtmlPrepend://(-1) htmlPrepend://(-2) htmlAppend://(3) htmlAppend://(4)\nfile://(0)\n`\n\n``` range.txt\n0123456789\n```\nrange1.whistlejs.com file://{range.txt}\nrange2.whistlejs.com whistle.test2://\nheaderreplace.plugin.whistlejs.com headerReplace://req.host:headerreplace.=&/Plugin/i=test headerReplace://res.set-cookie:test=abc resHeaders://(set-cookie=test&set-cookie=test222)\n\n**/wildcard5/_____/** file://({\"ec\":\"$2\"})\n***.test.wildcard5.com file://({\"ec\":0}) resType://json enable://capture\n\n``` w1.json\nlist[10]: `a\\rb\\nc`\nlist[20]: 123\nlist[30]: \"123\"\nlist[40].a: \"123\"\n```\n.test.wildcard5.com resMerge://{w1.json}\n\n/testx.whistlejs.com/ enable://capture\n\n``` test.txt\nDefault\n```\n\ndefault.key.test.w2.org/test file://{test.txt}\nhtt*://wilcardx.wd* file://({\"ec\":\"x\"})\nh*t*p://wilcardy.wd*.w2.org file://({\"ec\":\"y\"})\nenable://capture ***.wilcardz.wd*.w2.org\nh*t*p*://***.wilcardz.wd*.w2.org file://({\"ec\":\"z\"})\n\nhttp://sep.path.test.w2.org/.././../index.html file://({\"ec\":\"sep\"})\n\nhttp://sep1.path.test.w2.org/.././../index.html?doNotParseJson file://{notExist.test} resBody://{notExist.test2} excludeFilter://b:test\n"
  },
  {
    "path": "test/units/_normalizeConnectArgs.test.js",
    "content": "var net = require('net');\nvar util = require('../util.test');\n\nmodule.exports = function() {\n  var normalizeConnectArgs = function() {\n    return net._normalizeConnectArgs(arguments);\n  };\n  var path1 = normalizeConnectArgs('test');\n  var path2 = normalizeConnectArgs('test', util.noop);\n  var port1 = normalizeConnectArgs('8899');\n  var port2 = normalizeConnectArgs('8899', 'www.test.com');\n  var port3 = normalizeConnectArgs('8899', 'www.test.com', util.noop);\n  path1.length.should.be.equal(1);\n  path1[0].path.should.be.equal('test');\n  path2.length.should.be.equal(2);\n  path2[0].path.should.be.equal('test');\n  path2[1].should.be.equal(util.noop);\n  port1.length.should.be.equal(1);\n  port1[0].port.should.be.equal('8899');\n  port2.length.should.be.equal(1);\n  port2[0].port.should.be.equal('8899');\n  port2[0].host.should.be.equal('www.test.com');\n  port3.length.should.be.equal(2);\n  port3[0].port.should.be.equal('8899');\n  port3[0].host.should.be.equal('www.test.com');\n  port3[1].should.be.equal(util.noop);\n};\n"
  },
  {
    "path": "test/units/attachment.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://attachment.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('content-disposition', 'attachment; filename=\"index.html\"');\n  });\n\n  util.request({\n    method: 'put',\n    url: 'https://attachment.test.whistlejs.com/index2.html'\n  }, function(res, data) {\n    res.headers.should.have.property('content-disposition', 'attachment; filename=\"index2.html\"');\n  });\n};\n"
  },
  {
    "path": "test/units/auth.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://au2th.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.not.have.property('authorization');\n  });\n\n  util.request({\n    method: 'put',\n    url: 'https://auth.test.whistlejs.com/index2.html'\n  }, function(res, data) {\n    data.headers.should.have.property('authorization');\n  });\n  util.request('http://auth-test.whistlejs.com/test-plugin-auth-hook/forbidden?doNotParseJson', function(res, data) {\n    data.should.equal('test-plugin-auth-hook');\n  });\n  util.request('http://auth-test.whistlejs.com/test-plugin-auth-hook/forbidden/login?doNotParseJson', function(res, data) {\n    res.headers['www-authenticate'].should.equal('Basic realm=User Login');\n    data.should.equal('test-plugin-auth-hook/login');\n  });\n  util.request('http://auth-test.whistlejs.com/test-plugin-auth-hook/', function(res, data) {\n    data.type.should.equal('server');\n  });\n};\n"
  },
  {
    "path": "test/units/cache.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://cache.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('cache-control', 'max-age=60000');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://cache.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    res.headers.should.have.property('cache-control', 'max-age=60000');\n  });\n};\n"
  },
  {
    "path": "test/units/common.test.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar isUtf8 = require('../../lib/util/is-utf8');\nvar createStorage = require('../../lib/rules/storage');\n\nvar BASE_DIR = path.join(__dirname, '../assets/files/');\n\nmodule.exports = function() {\n  var gb2312Buf = fs.readFileSync(path.join(BASE_DIR, 'gb2312.txt'));\n  var utf8Buf = fs.readFileSync(path.join(BASE_DIR, '1.txt'));\n  isUtf8(gb2312Buf).should.equal(false);\n  isUtf8(utf8Buf).should.equal(true);\n  createStorage(path.join(BASE_DIR, 'storage'));\n};"
  },
  {
    "path": "test/units/composer.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function () {\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    json: {\n      url: 'http://127.0.0.1:8080',\n      headers: 'test: 123',\n      method: 'POST',\n      body: 'test'\n    }\n  });\n};\n"
  },
  {
    "path": "test/units/connect.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('ws://connect.whistlejs.com/index.html', function(data) {\n    data.type.should.equal('server');\n  });\n\n  util.request('ws://connect.whistlejs.com/index.html', function(data) {\n    data.type.should.equal('server');\n  });\n};\n"
  },
  {
    "path": "test/units/css.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://css1.test.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.equal('_<style>css</style>');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://css2.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_css');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://css3.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_');\n  });\n};\n"
  },
  {
    "path": "test/units/delete.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://delete.test.whistlejs.com/index.html?get11', function(res, data) {\n    data.headers.should.not.have.property('x-delete-all');\n    data.headers.should.not.have.property('x-delete-req');\n    res.headers.should.not.have.property('x-delete-res');\n  });\n\n  util.request({\n    method: 'post',\n    headers: {\n      'x-delete-test': 123\n    },\n    url: 'http://delete.test.whistlejs.com/index.html?post11'\n  }, function(res, data) {\n    data.headers.should.have.property('x-delete-test');\n    data.headers.should.not.have.property('x-delete-all');\n    data.headers.should.not.have.property('x-delete-req');\n    res.headers.should.not.have.property('x-delete-res');\n  });\n  \n  util.request('http://delete1.test.whistlejs.com/index.html?get', function(res, data) {\n    data.headers.should.have.property('x-delete-all');\n    data.headers.should.have.property('x-delete-req');\n    res.headers.should.have.property('x-delete-res');\n  });\n\n  util.request({\n    method: 'post',\n    headers: {\n      'x-delete-test': 123\n    },\n    url: 'http://delete1.test.whistlejs.com/index.html?post'\n  }, function(res, data) {\n    data.headers.should.have.property('x-delete-test');\n    data.headers.should.have.property('x-delete-all');\n    data.headers.should.have.property('x-delete-req');\n    res.headers.should.have.property('x-delete-res');\n  });\n};\n"
  },
  {
    "path": "test/units/disable.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://disable.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('cache-control', 'no-cache');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://disable.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    res.headers.should.have.property('cache-control', 'no-cache');\n    res.headers.should.not.have.property('set-cookie');\n  });\n};\n"
  },
  {
    "path": "test/units/file.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://file.test.whistlejs.com/index.html', function(res, data) {\n    data.should.have.property('body', 'html');\n  });\n\n  util.request({\n    url: 'https://file.test.whistlejs.com/index.html',\n    method: 'post',\n    body: util.getTextBySize(3072)\n  }, function(res, data) {\n    data.should.have.property('body', 'html');\n  });\n  util.request({\n    url: 'http://test-values.whistlejs.com/index.html?doNotParseJson',\n    method: 'post',\n    body: util.getTextBySize(3072)\n  }, function(res, data) {\n    res.headers['x-test-res'].should.be.equal('456/resHeaders');\n    data.should.equal('x-test-body=测试789/file');\n  });\n};\n"
  },
  {
    "path": "test/units/filter.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://filter.com/index.html', function(res, data) {\n    data.should.have.property('ec', 0);\n  });\n\n  util.request('http://filter2.com/test.html?abc', function(res, data) {\n    data.should.have.property('ec', 123);\n  });\n\n  util.request('http://filter2.com/test.html?cba', function(res, data) {\n    data.should.have.property('ec', 333);\n  });\n\n  util.request('http://filter2.com/test.html?cb2a', function(res, data) {\n    data.should.have.property('ec', 321);\n  });\n\n\n  util.request({\n    url: 'https://filter.com/index.html',\n    method: 'delete',\n    body: '1'.repeat(1024 * 250) + 'test'\n  }, function(res, data) {\n    data.should.have.property('ec', 1);\n  });\n  util.request({\n    url: 'https://filter.com/index.html',\n    method: 'delete',\n    body: 'test',\n    headers: {\n      'test': 'abc'\n    }\n  }, function(res, data) {\n    data.should.have.property('ec', 1);\n  });\n\n  util.request({\n    url: 'https://filter.com/index.html',\n    method: 'post',\n    body: '555555',\n    headers: {\n      'test': 'abc',\n      'x-test': 'abc'\n    }\n  }, function(res, data) {\n    data.should.have.property('ec', 2);\n  });\n\n  util.request({\n    url: 'https://filter.com/index.html',\n    method: 'post',\n    headers: {\n      'test': 'abc',\n      'x-test': 'hehe'\n    }\n  }, function(res, data) {\n    data.should.have.property('ec', 3);\n  });\n\n  util.request({\n    url: 'https://filter.com/index.html',\n    method: 'post',\n    headers: {\n      'test': 'abc',\n      'x-test': '123'\n    }\n  }, function(res, data) {\n    data.should.have.property('ec', 4);\n  });\n};"
  },
  {
    "path": "test/units/fm.test.js",
    "content": "var path = require('path');\nvar assert = require('assert');\nvar fm = require('../../lib/util/file-mgr');\n\nmodule.exports = function() {\n  var file1 = path.join(__dirname, '../assets/files/1.txt');\n  var file2 = path.join(__dirname, '../assets/files/2.txt');\n  var file3 = path.join(__dirname, '../assets/files/3.txt');\n  var path1 = file1 + '|' + file2;\n  var path2 = file2 + '|' + file3;\n  var path3 = file3 + '|' + file1;\n  fm.readFile(path1, function(result) {\n    assert(result.length === 4, 'Error');\n  });\n  fm.readFileList([null, null, path1, path2, null], function(result) {\n    assert(result.length === 5, 'Error');\n  });\n  fm.readFileList(null, function(result) {\n    assert(!result, 'Error');\n  });\n  fm.readFileList([null, null, null], function(result) {\n    assert(result.length === 3, 'Error');\n  });\n  fm.readFileList([], function(result) {\n    assert(!result, 'Error');\n  });\n  fm.readFilesText([null, null, path1, path2, null], function(result) {\n    assert(result[2] === '1\\r\\n2', 'Error');\n  });\n  fm.readFilesText(null, function(result) {\n    assert(!result, 'Error');\n  });\n  fm.readFilesText([], function(result) {\n    assert(!result, 'Error');\n  });\n  fm.readFilesText([null, null, null], function(result) {\n    assert(result.length === 3, 'Error');\n  });\n  fm.readFile(path1, function(result) {\n    assert(result.length === 4, 'Error');\n  });\n  fm.readFileText(path3, function(result) {\n    assert(result === '3\\r\\n1', 'Error');\n  });\n};\n\nmodule.exports();"
  },
  {
    "path": "test/units/forward.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://forward.test.whistlejs.com/index.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://forward.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n};\n"
  },
  {
    "path": "test/units/host.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://host.test.whistlejs.com/index.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n  util.request('http://127.0.0.1:8080/xhost.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n};\n"
  },
  {
    "path": "test/units/html.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://html1.test.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.equal('_html');\n  });\n  util.request('http://prependhtml.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.equal(['<!DOCTYPE html>', '-1', '-21', '23', '4'].join('\\r\\n'));\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://html2.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://html3.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_');\n  });\n};\n"
  },
  {
    "path": "test/units/https.test.js",
    "content": "var util = require('../util.test');\nvar config = require('../config.test');\nvar w2Conf = require('../../lib/config');\n\nmodule.exports = function() {\n  util.request('http://127.0.0.1:8080/auto2http.html', function(res, data) {\n    data.url.should.be.equal('/auto2http.html');\n  });\n  util.request('https://https.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('https://https1.w2.org:' + config.httpsPort + '/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://https1.w2.org:' + config.serverPort + '/index.html', function(res, data) {\n    data.type.should.be.equal('server');\n  });\n\n  util.request('https://https2.w2.org:' + config.httpsPort + '/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://https2.w2.org/index.html', function(res, data) {\n    data.type.should.be.equal('server');\n  });\n\n  util.request('https://https3.w2.org:5566/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request({\n    url: 'http://test1.pass.through.com/index.html',\n    headers: {\n      'x-forwarded-for': '3.3.5.5'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n  });\n  util.request({\n    url: 'http://test2.pass.through.com/index.html',\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n  });\n  util.request({\n    url: 'http://test3.pass.through.com/index.html',\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n  });\n\n  util.request({\n    url: 'http://test-req.pass.through.com/index.html',\n    method: 'POST',\n    body: 'test1',\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqBuffer',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n    data.body.should.be.equal('test1 reqBuffer');\n  });\n  util.request({\n    url: 'http://test-req.pass.through.com/index.html',\n    method: 'POST',\n    body: 'test2',\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqText',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n    data.body.should.be.equal('test2 reqText');\n  });\n  util.request({\n    url: 'http://test-req.pass.through.com/index.html',\n    method: 'POST',\n    body: JSON.stringify({ a: { b: { c: 'test3' } } }),\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqJson',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n    JSON.parse(data.body).a.b.c.should.be.equal('streamUtils.readJson');\n  });\n  util.request({\n    url: 'http://test-req.pass.through.com/index.html',\n    method: 'POST',\n    body: JSON.stringify({ a: { b: { c: 'test3' } } }),\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqJson',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n    JSON.parse(data.body).a.b.c.should.be.equal('streamUtils.readJson');\n  });\n\n  util.request({\n    url: 'http://test-req.pass.through.com/index.html',\n    method: 'POST',\n    body: JSON.stringify({ a: { b: { c: 'test3' } } }),\n    headers: {\n      'x-whistle-client-id': 'avenwu-test-id',\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqJson2',\n      [w2Conf.CLIENT_INFO_HEADER]: '5.5.5.5,1234,6.6.6.6,5678'\n    }\n  }, function(res, data) {\n    data.type.should.be.equal('server');\n    JSON.parse(data.body).a.b.c.should.be.equal('streamUtils.readJson');\n  });\n\n  util.request({\n    url: 'http://test-res.pass.through.com/index.html',\n    method: 'POST',\n    headers: {\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'resText'\n    }\n  }, function(res, data) {\n    data[1].should.be.equal(123);\n  });\n  util.request({\n    url: 'http://test-res.pass.through.com/index.html',\n    method: 'POST',\n    headers: {\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'resJson'\n    }\n  }, function(res, data) {\n    data.type.a.b.c.should.be.equal('readJson');\n  });\n\n  util.request({\n    url: 'http://test-res.pass.through.com/index.html',\n    method: 'POST',\n    body: JSON.stringify({ a: { b: { c: 'test3' } } }),\n    headers: {\n      'x-forwarded-for': '3.3.5.5',\n      'x-test-cmd': 'reqJson+resJson'\n    }\n  }, function(res, data) {\n    data.type.a.b.c.should.be.equal('resJson');\n    JSON.parse(data.body).a.b.c.should.be.equal('reqJson');\n  });\n};\n"
  },
  {
    "path": "test/units/ignore.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('https://enable1.w2.org', function(res, data) {\n    data.url.should.be.equal('https://enable1.w2.org/');\n  });\n  util.request('https://enable2.w2.org:5566', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n  util.request('https://enable3.w2.org:5566', function(res, data) {\n    data.url.should.be.equal('https://enable3.w2.org:5566/');\n  });\n  util.request('https://enable4.w2.org:5566', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n};\n"
  },
  {
    "path": "test/units/insertFile.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n\n  util.request({\n    url: 'http://prependbin.bodybin.appendbin.test.whistlejs.com/?doNotParseJson',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n    body.should.be.equal('我们是社会主义接班人!');\n  });\n\n  util.request({\n    url: 'http://prependbin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n\n  util.request({\n    url: 'http://bodybin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n\n  util.request({\n    url: 'http://appendbin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n\n  util.request({\n    url: 'http://prependbin.bodybin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n\n  util.request({\n    url: 'http://prependbin.appendbin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n\n  util.request({\n    url: 'http://bodybin.appendbin.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.be.equal(res.body);\n  });\n};\n"
  },
  {
    "path": "test/units/js.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://js1.test.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.equal('_<script>js</script>');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://js2.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_js');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://js3.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_');\n  });\n  util.request('http://jsbody2.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.equal('<!DOCTYPE html>\\r\\n<script src=\"http://1\"></script>0<script src=\"https://2\"></script>');\n  });\n};\n"
  },
  {
    "path": "test/units/keys.test.js",
    "content": "var util = require('../util.test');\n\nvar mockScriptUrl = 'http://mock.script-key.test.w2.org/test/script/path/to?doNotParseJson';\nvar serviceScriptUrl = 'http://service.script-key.test.w2.org/test/script/path/to?doNotParseJson';\nvar shadowScriptUrl = 'http://shadow.script-key.test.w2.org/test/script/path/to?doNotParseJson';\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://mock.test.w2.org/path/to?doNotParseJson',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal('mock');\n  });\n\n  util.request({\n    url: 'http://mock.script-key.test.w2.org/test/script/api',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('api');\n  });\n\n  util.request({\n    url: 'http://mock.script-key.test.w2.org/test/script/proxy',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('proxy');\n  });\n\n  util.request({\n    url: mockScriptUrl,\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal(mockScriptUrl + 'mock');\n  });\n\n  util.request({\n    url: 'http://service.test.w2.org/path/to/index2.html?doNotParseJson',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal('service');\n  });\n\n  util.request({\n    url: 'http://service.script-key.test.w2.org/test/script/api',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('api');\n  });\n\n  util.request({\n    url: 'http://service.script-key.test.w2.org/test/script/proxy',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('proxy');\n  });\n\n  util.request({\n    url: serviceScriptUrl,\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal(serviceScriptUrl + 'service');\n  });\n\n  util.request({\n    url: 'http://shadow.test.w2.org/path/to/index.html?doNotParseJson',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal('shadow');\n  });\n\n  util.request({\n    url: shadowScriptUrl,\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.should.equal(shadowScriptUrl + 'shadow');\n  });\n\n  util.request({\n    url: 'http://shadow.script-key.test.w2.org/test/script/api',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('api');\n  });\n\n  util.request({\n    url: 'http://shadow.script-key.test.w2.org/test/script/proxy',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.url.should.containEql('proxy');\n  });\n  util.request('http://default.key.test.w2.org/test/to/index.html?doNotParseJson', function(_, data) {\n    data.should.equal('Default');\n  });\n  util.request('http://test.key.test.w2.org/test/to/index.html?doNotParseJson', function(_, data) {\n    data.should.equal('test');\n  });\n  util.request({\n    url: 'http://header.test.weso.org/test/script/proxy?doNotParseJson',\n    headers: {\n      test: 'abc',\n      'x-whistle-rule-value': encodeURIComponent('* file://{test.txt} %pipe-http=doNotParseJson\\n%pipe-http=header')\n    }\n  }, function(res, data) {\n    data.trim().should.equal('assets/values');\n  });\n  util.request({\n    url: 'http://header2.test.weso.org/test/script/proxy?doNotParseJson',\n    headers: {\n      test: 'abc',\n      'x-whistle-rule-value': encodeURIComponent('* file://{test.txt}\\n``` test.txt\\ntest-header\\n```')\n    }\n  }, function(res, data) {\n    data.should.equal('test-header');\n  });\n\n  util.request('http://wilcardx.wd.w2.org/test/script/proxy', function(_, data) {\n    data.ec.should.equal('x');\n  });\n\n  util.request('http://wilcardy.wd.w2.org/test/script/proxy', function(_, data) {\n    data.ec.should.equal('y');\n  });\n  util.request('https://wilcardz.wd.w2.org/test/script/proxy', function(_, data) {\n    data.ec.should.equal('z');\n  });\n  util.request('http://sep.path.test.w2.org/.././../index.html', function(_, data) {\n    data.ec.should.equal('sep');\n  });\n  util.request('http://sep1.path.test.w2.org/.././../index.html?doNotParseJson', function(res, data) {\n    res.statusCode.should.equal(404);\n    data.should.containEql('notExist.test');\n  });\n};\n"
  },
  {
    "path": "test/units/log.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://log1.test.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.length.should.above(500);\n    body.should.containEql('<script>log</script>\\r\\n_');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://log2.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.length.should.above(500);\n    body.should.containEql('_\\r\\nlog');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://log3.test.whistlejs.com/index.html?resBody=_'\n  }, function(res, body) {\n    body.should.equal('_');\n  });\n};\n"
  },
  {
    "path": "test/units/method.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://test.whistlejs.com/index.html', function(res, data) {\n    data.method.should.equal('GET');\n  });\n\n  util.request({\n    method: 'put',\n    url: 'https://test.whistlejs.com/index.html'\n  }, function(res, data) {\n    data.method.should.equal('PUT');\n  });\n};\n"
  },
  {
    "path": "test/units/options.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://test.options.com/index.html', function(res, data) {\n    data.method.should.equal('options');\n  });\n};\n"
  },
  {
    "path": "test/units/others.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://unknownprotocol.w2.org/index.html?doNotParseJson', function(res, data) {\n    res.statusCode.should.be.equal(502);\n  });\n  require('../../biz/webui/cgi-bin/util').formatDate();\n  util.request('http://test.internal.path.com/...whistle-path.5b6af7b9884e1165...///whistle._abc/index.html', function(res, data) {\n    data.url.should.be.equal('http://test.internal.path.com/...whistle-path.5b6af7b9884e1165...///whistle._abc/index.html');\n  });\n};\n"
  },
  {
    "path": "test/units/pac.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://pac.test.com/index.html', function(res, data) {\n    data.should.be.have.property('type', 'server');\n  });\n  util.request({\n    url: 'http://pac.test.com/index.html',\n    method: 'post'\n  }, function(res, data) {\n    data.should.be.have.property('type', 'server');\n  });\n};\n"
  },
  {
    "path": "test/units/params.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://params.test.whistlejs.com/index.html',\n    method: 'POST',\n    form: {key: 'value'}\n  }, function(res, data) {\n    data.body.should.equal('key=value&test=abc');\n  });\n\n  util.request({\n    url: 'http://params.test.whistlejs.com/index.html',\n    method: 'POST',\n    headers: {\n      'content-type': 'application/json'\n    },\n    body: JSON.stringify({key: 'value'})\n  }, function(res, data) {\n    data.body.should.containEql('\"test\":\"abc\"');\n    data.resMerge.should.equal('test');\n  });\n\n  util.request({\n    url: 'http://upload.test.whistlejs.com/index.html',\n    method: 'post',\n    formData: {\n      name1: 'my_value',\n      file1: {\n        value:  'ok',\n        options: {\n          filename: 'topsecret.jpg',\n          contentType: 'text/plain'\n        }\n      }\n    }\n  }, function(res, data) {\n    data.body.should.containEql('filename=\"file2\"');\n    data.body.should.containEql('name=\"name2\"');\n    data.body.should.containEql('name=\"file2\"');\n    data.body.should.containEql('filename=\"text.txt\"');\n    data.body.should.containEql('1234567890');\n  });\n\n  util.request('http://params2.test.whistlejs.com/index.html?name=aven', function(res, data) {\n    data.url.should.containEql('?name=aven&test=abc');\n  });\n};\n"
  },
  {
    "path": "test/units/plugin.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://headerreplace.plugin.whistlejs.com:1234/index.html',\n    headers: { test: 'abc' }\n  }, function(res, data) {\n    data.headers.host.should.equal('test.whistlejs.com:1234');\n    res.headers['set-cookie'].join().should.equal('abc,abc222');\n  });\n\n  util.request('http://plugin.whistlejs.com:1234/index.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n\n  util.request('wss://321.whistlejs.com/index.html', function(data) {\n    data.ruleValue.should.equal('abc');\n  });\n\n  util.request('wss://321.ws1.whistlejs.com:2222/index.html', function(data) {\n    data.host.should.equal('127.0.0.1:9999');\n  });\n};\n"
  },
  {
    "path": "test/units/plugins.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://mp1.w2.org/index.html', function(res, data) {\n    res.headers['content-type'].should.be.equal('text/html; charset=gbk');\n    data.ruleValue.should.be.equal('123');\n  });\n\n  util.request('https://ths.w2.org/index.html', function(res, data) {\n    data.test.should.be.equal('values');\n  });\n};\n"
  },
  {
    "path": "test/units/proxy.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://proxy.test.whistlejs.com/index.html', function(res, data) {\n    data.should.have.property('url', 'http://proxy.test.whistlejs.com/index.html');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://proxy.test.whistlejs.com/index.html',\n    body: 'xxxxx'\n  }, function(res, data) {\n    data.should.have.property('url', 'http://proxy.test.whistlejs.com/index.html');\n  });\n  util.request('ws://ws2.w2.org:8081/index.html', function(res, data) {\n    res.type.should.be.equal('server');\n  });\n  util.request('ws://ws3.w2.org:8081/index.html', function(res, data) {\n    res.type.should.be.equal('server');\n  });\n  util.request('ws://ws4.w2.org:8081/index.html', function(res, data) {\n    res.type.should.be.equal('server');\n  });\n  util.request('ws://ws5.w2.org:8081/index.html', function(res, data) {\n    res.type.should.be.equal('server');\n  });\n  util.request('http://127.0.0.1:8080/xproxy.html', function(res, data) {\n    data.type.should.be.equal('server');\n  });\n};\n"
  },
  {
    "path": "test/units/range.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    uri: 'http://range1.whistlejs.com/index.html',\n    headers: {\n      'range': 'bytes=2-3,5-6'\n    }\n  }, function(res, body) {\n    body.should.equal(23456);\n  });\n  util.request({\n    url: 'http://range2.whistlejs.com/',\n    headers: {\n      'range': 'bytes=2-3,1-8'\n    }\n  }, function(res, body) {\n    body.should.equal(12345678);\n  });\n};\n"
  },
  {
    "path": "test/units/rawfile.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n\n  util.request('http://raw.test.whistlejs.com/index.html?doNotParseJson', function(res, body) {\n    res.statusCode.should.equal(500);\n    body.should.equal('test');\n    res.headers.should.have.property('content-type', 'text/plain');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://raw.test.whistlejs.com/?doNotParseJson'\n  }, function(res, body) {\n    res.statusCode.should.equal(500);\n    body.should.equal('test');\n    res.headers.should.have.property('content-type', 'text/plain');\n  });\n\n  util.request('http://xraw.test.whistlejs.com/index2.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n  util.request('http://rwf.w2.org/index.html?doNotParseJson', function(res, body) {\n    res.statusCode.should.equal(500);\n    body.should.equal('test2');\n  });\n};\n"
  },
  {
    "path": "test/units/redirect.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('https://redirect.test.whistlejs.com/index.html', function(res, data) {\n    data.should.have.property('url', 'http://test.whistlejs.com/');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://redirect.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    data.should.have.property('url', 'http://test.whistlejs.com/');\n  });\n};\n"
  },
  {
    "path": "test/units/referer.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://referer.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('referer', 'xxx');\n  });\n\n  util.request({\n    url: 'https://referer.test.whistlejs.com/index.html',\n    method: 'post'\n  }, function(res, data) {\n    data.headers.should.have.property('referer', 'xxx');\n  });\n};\n"
  },
  {
    "path": "test/units/replaceStatus.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://replacestatus.test.whistlejs.com/index.html', function(res, data) {\n    res.statusCode.should.equal(500);\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://replacestatus.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    res.statusCode.should.equal(500);\n  });\n};\n"
  },
  {
    "path": "test/units/req.prepend.body.append.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://reqprepend.reqbody.reqappend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('prependbodyappend');\n  });\n\n  util.request({\n    url: 'http://reqprepend.reqbody.reqappend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('prependbodyappend');\n  });\n};\n"
  },
  {
    "path": "test/units/reqAppend.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://reqappend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('append\\r\\nappend');\n  });\n\n  util.request({\n    url: 'http://reqappend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('append\\r\\nappend');\n  });\n};\n"
  },
  {
    "path": "test/units/reqBody.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://reqbody.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('body\\r\\nbody');\n  });\n\n  util.request({\n    url: 'http://reqbody.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('body\\r\\nbody');\n  });\n};\n"
  },
  {
    "path": "test/units/reqCharset.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqcharset.test.whistlejs.com/index.html', function(res, data) {\n    data.headers['content-type'].should.containEql('utf8');\n  });\n};\n"
  },
  {
    "path": "test/units/reqCookies.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqcookies.test.whistlejs.com/', function(res, data) {\n    data.headers.cookie.should.be.equal('test=abc');\n  });\n};\n"
  },
  {
    "path": "test/units/reqCors.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqcors.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('origin', '*');\n    data.headers.should.have.property('access-control-request-method', 'POST');\n    data.headers.should.have.property('access-control-request-headers', 'x-test');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://reqcors.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    data.headers.should.have.property('origin', '*');\n    data.headers.should.have.property('access-control-request-method', 'POST');\n    data.headers.should.have.property('access-control-request-headers', 'x-test');\n  });\n};\n"
  },
  {
    "path": "test/units/reqDelay.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  var now = Date.now();\n  util.request('http://reqdelay.test.whistlejs.com/', function(res, data) {\n    now = Date.now() - now;\n    now.should.above(1000);\n  });\n};\n"
  },
  {
    "path": "test/units/reqHeaders.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqheaders.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('x-test1', 'value1');\n    data.headers.should.have.property('x-test2', 'value2');\n    data.headers.should.have.property('x-testn', 'valueN');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://reqheaders.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    data.headers.should.have.property('x-test1', 'value1');\n    data.headers.should.have.property('x-test2', 'value2');\n    data.headers.should.have.property('x-testn', 'valueN');\n  });\n};\n"
  },
  {
    "path": "test/units/reqPrepend.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://reqprepend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('prepend');\n  });\n\n  util.request({\n    url: 'http://reqprepend.test.whistlejs.com/',\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.equal('prepend');\n  });\n};\n"
  },
  {
    "path": "test/units/reqReplace.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://reqreplace.test.whistlejs.com/',\n    method: 'post',\n    body: 'testxxx'\n  }, function(res, data) {\n    data.body.should.equal('abcxxx');\n  });\n\n  util.request({\n    url: 'http://reqreplace.test.whistlejs.com/',\n    method: 'post',\n    body: 'test123'\n  }, function(res, data) {\n    data.body.should.equal('abc123');\n  });\n};\n"
  },
  {
    "path": "test/units/reqSpeed.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  var now = Date.now();\n  util.request({\n    url: 'http://reqspeed.test.whistlejs.com/',\n    method: 'post',\n    body: util.getTextBySize(128 * 2 + 1)\n  }, function(res, data) {\n    now = Date.now() - now;\n    now.should.above(2000);\n  });\n};\n"
  },
  {
    "path": "test/units/reqType.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://restype.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('content-type', 'text/html');\n  });\n};\n"
  },
  {
    "path": "test/units/res.prepend.body.append.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://resprepend.resbody.resappend.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.equal('prependbodyappend');\n  });\n\n  util.request('http://resprepend.resbody.resappend.test.whistlejs.com/?resBody=', function(res, body) {\n    body.should.equal('prependbodyappend');\n  });\n};\n"
  },
  {
    "path": "test/units/resAppend.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://resappend.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.equal('append\\r\\nappend');\n  });\n\n  util.request('http://resappend.test.whistlejs.com/?resBody=', function(res, body) {\n    body.should.equal('append\\r\\nappend');\n  });\n};\n"
  },
  {
    "path": "test/units/resBody.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('https://resbody.test.whistlejs.com/?resBody=', function(res, body) {\n    body.should.equal('body\\r\\nbody');\n  });\n\n  util.request({\n    url: 'https://resbody.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.equal('body\\r\\nbody');\n  });\n};\n"
  },
  {
    "path": "test/units/resCharset.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://rescharset.test.whistlejs.com/index.html', function(res, data) {\n    res.headers['content-type'].should.containEql('utf8');\n  });\n};\n"
  },
  {
    "path": "test/units/resCookies.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://rescookies.test.whistlejs.com/', function(res, data) {\n    res.headers['set-cookie'].should.containEql('key2=value2');\n  });\n};\n"
  },
  {
    "path": "test/units/resCors.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://rescors.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('access-control-max-age', '300000');\n    res.headers.should.have.property('access-control-allow-credentials', 'true');\n    res.headers.should.have.property('access-control-expose-headers', 'x-test');\n    res.headers.should.have.property('access-control-allow-methods', 'POST');\n    res.headers.should.have.property('access-control-allow-origin', '*');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://rescors.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    res.headers.should.have.property('access-control-max-age', '300000');\n    res.headers.should.have.property('access-control-allow-credentials', 'true');\n    res.headers.should.have.property('access-control-expose-headers', 'x-test');\n    res.headers.should.have.property('access-control-allow-methods', 'POST');\n    res.headers.should.have.property('access-control-allow-origin', '*');\n  });\n};\n"
  },
  {
    "path": "test/units/resDelay.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  var now = Date.now();\n  util.request('http://resdelay.test.whistlejs.com/', function(res, data) {\n    now = Date.now() - now;\n    now.should.above(1000);\n  });\n};\n"
  },
  {
    "path": "test/units/resHeaders.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://resheaders.test.whistlejs.com/index.html', function(res, data) {\n    res.headers.should.have.property('x-test1', 'value1');\n    res.headers.should.have.property('x-test2', 'value2');\n    res.headers.should.have.property('x-testn', 'valueN');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://resheaders.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    res.headers.should.have.property('x-test1', 'value1');\n    res.headers.should.have.property('x-test2', 'value2');\n    res.headers.should.have.property('x-testn', 'valueN');\n  });\n};\n"
  },
  {
    "path": "test/units/resPrepend.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://resprepend.test.whistlejs.com/?resBody=',\n    method: 'post'\n  }, function(res, body) {\n    body.should.equal('prepend');\n  });\n\n  util.request('https://resprepend.test.whistlejs.com/?resBody=', function(res, body) {\n    body.should.equal('prepend');\n  });\n};\n"
  },
  {
    "path": "test/units/resReplace.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'https://resreplace.test.whistlejs.com/?resBody=123test',\n    method: 'post'\n  }, function(res, body) {\n    body.should.equal('123abc');\n  });\n\n  util.request('http://resreplace.test.whistlejs.com/?resBody=test123', function(res, body) {\n    body.should.equal('abc123');\n  });\n};\n"
  },
  {
    "path": "test/units/resSpeed.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  var now = Date.now();\n  util.request({\n    url: 'http://resspeed.test.whistlejs.com/',\n    body: util.getTextBySize(128 * 2 + 1),\n    method: 'post'\n  }, function(res, data) {\n    now = Date.now() - now;\n    now.should.above(2000);\n  });\n};\n"
  },
  {
    "path": "test/units/resType.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqtype.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('content-type', 'text/plain');\n  });\n};\n"
  },
  {
    "path": "test/units/rule.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  var body = 'test__';\n  util.request({\n    url: 'http://test.whistlejs.com/',\n    body: body,\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.be.equal(body);\n  });\n\n  util.request({\n    url: 'http://rule4.test.whistlejs.com/abc?abc=1',\n    body: body,\n    method: 'post'\n  }, function(res, data) {\n    data.body.should.be.equal(body);\n  });\n\n  util.request('http://rule.test.whistlejs.com/', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n  });\n\n  util.request('http://rule1.test.whistlejs.com/?abc=123#cdb', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n    // data.url.should.endWith('/?test1abc=123');\n  });\n\n  util.request('http://rule2.test.whistlejs.com/?abc=1', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n    // data.url.should.endWith('/?test2');\n  });\n\n  util.request('http://rule2.test.whistlejs.com/?abc=', function(res, data) {\n    data.should.not.have.property('type');\n    data.headers.host.should.equal('rule2.test.whistlejs.com');\n  });\n\n  util.request('http://rule3.test.whistlejs.com/abcd', function(res, data) {\n    // data.should.not.have.property('type');\n    // data.headers.host.should.not.equal('host.test.whistlejs.com');\n  });\n\n  util.request('http://rule3.test.whistlejs.com/abc', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n    // data.url.should.endWith('/?test2');\n  });\n\n  util.request('http://rule4.test.whistlejs.com/abc', function(res, data) {\n    // data.should.not.have.property('type');\n    // data.headers.host.should.not.equal('host.test.whistlejs.com');\n  });\n\n  util.request('http://rule4.test.whistlejs.com/abc?abc=1', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n    // data.url.should.endWith('/?test2');\n  });\n\n  util.request('http://testrule5.test.whistlejs.com/abc?abc=1#aaaa', function(res, data) {\n    // data.should.not.have.property('type');\n    // data.headers.host.should.not.equal('host.test.whistlejs.com');\n  });\n\n  var test = '9999999999';\n\n  util.request('http://testrule5.test.whistlejs.com/abc?test=' + test + '#aaaa', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.test.whistlejs.com');\n    data.url.should.endWith('/abc?test=' + test + '');\n  });\n\n  util.request('http://testrule5.abc.test.whistlejs.com/?test=' + test + '#aaaa', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('host.abc.test.whistlejs.com');\n    data.url.should.endWith('/?test=' + test + '');\n  });\n\n  util.request('http://testrule6.tt.abc.test.whistlejs.com/?test=' + test + '#aaaa', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('tt.abc.test.whistlejs.com');\n    data.url.should.endWith('/?test=' + test + '');\n  });\n\n  util.request('http://testrule6.abc.test.whistlejs.com/?test=' + test + '#aaaa', function(res, data) {\n    // data.type.should.equal('server');\n    // data.headers.host.should.equal('abc.test.whistlejs.com');\n    data.url.should.endWith('/?test=' + test + '');\n  });\n};\n"
  },
  {
    "path": "test/units/rulesFile.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://rf1.w2.org/index.html', function(res, data) {\n    data.should.have.property('test', 'values');\n  });\n  util.request('http://rf2.w2.org/index.html', function(res, data) {\n    data.should.have.property('test', 'values2');\n  });\n  util.request('http://rf3.w2.org/index.html', function(res, data) {\n    data.should.have.property('test', 'values3');\n  });\n};\n"
  },
  {
    "path": "test/units/script.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqscript.w2.org/index.html?doNotParseJson', function(res, data) {\n    data.should.have.equal('123');\n    res.headers['x-test'].should.have.equal('123');\n  });\n  util.request({\n    url: 'http://reqscript.w2.org/index.html?doNotParseJson',\n    method: 'POST',\n    body: 'test'\n  }, function(res, data) {\n    data.should.have.equal('test');\n    res.headers['x-test'].should.have.equal('test');\n  });\n};\n"
  },
  {
    "path": "test/units/socks.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://socks1.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1080);\n  });\n  util.request('https://socks1.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks2.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1118);\n  });\n  util.request('https://socks2.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks3.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1080);\n  });\n  util.request('https://socks3.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks4.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1118);\n  });\n  util.request('https://socks4.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks5.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1080);\n  });\n  util.request('https://socks5.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks6.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1118);\n  });\n  util.request('https://socks6.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks7.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1080);\n  });\n  util.request('https://socks7.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n\n  util.request('http://socks8.w2.org/index.html', function(res, data) {\n    data.port.should.be.equal(1118);\n  });\n  util.request('https://socks8.w2.org/index.html', function(res, data) {\n    data.body.should.be.equal('test');\n  });\n};\n"
  },
  {
    "path": "test/units/ssi-include.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://ssi-include.whistlejs.com/index.html?doNotParseJson', function(res, data) {\n    data.should.be.containEql('include1.html');\n    data.should.be.containEql('include2.html');\n    data.should.be.containEql('include3.html');\n  });\n};\n"
  },
  {
    "path": "test/units/statusCode.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://statuscode1.testx.whistlejs.com/index.html?resBody=',\n    method: 'POST',\n    body: 'xxxxxxxxxxxx'\n  }, function(res) {\n    res.statusCode.should.equal(999);\n  });\n\n  util.request('http://statuscode3.testx.whistlejs.com/index.html?resBody=', function(res) {\n    res.statusCode.should.equal(500);\n  });\n\n  util.request({\n    url: 'http://statuscode4.test.whistlejs.com/index.html?resBody=',\n    method: 'POST',\n    body: 'xxxxxxxxxxxx'\n  }, function(res, body, err) {\n    err.should.be.ok();\n  });\n\n  util.request('https://statuscode5.test.whistlejs.com/index.html?resBody=', function(res, body, err) {\n    err.should.be.ok();\n  });\n};\n"
  },
  {
    "path": "test/units/tpl.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://tpl.test.whistlejs.com/index.html?resBody=%7Bcallback%7D(%7Bec%3A%200%7D)&callback=test', function(res, body) {\n    body.should.equal('test({ec: 0})');\n  });\n\n  util.request('http://tpl.test.whistlejs.com/index.html?doNotParseJson&callback=test', function(res, body) {\n    body.should.equal('test({ec: 0})');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'https://tpl2.test.whistlejs.com/?resBody=%7Bcallback%7D(%7Bec%3A%200%7D)&callback=test'\n  }, function(res, body) {\n    body.should.equal('test({ec: 0})');\n  });\n\n  util.request('http://xtpl.test.whistlejs.com/index2.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n};\n"
  },
  {
    "path": "test/units/tplStr.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://str.w2.org/index.html?doNotParseJson&a=1&a=2', function(res, body) {\n    body.should.equal('?doNotPbrseJson&a=1&a=2');\n  });\n  util.request('http://str.w2.org/index2.html?doNotParseJson&a=1&a=2', function(res, body) {\n    body.should.equal('doNotPbrseJson&b=1&b=2');\n  });\n  util.request('http://str2.w2.org/index.html?doNotParseJson&a=1&a=2', function(res, body) {\n    body.should.equal('?doNotPbrseJson&a=1&a=2');\n  });\n\n  util.request('http://str2.w2.org/index2.html?doNotParseJson&a=1&a=2', function(res, body) {\n    body.should.equal('doNotPbrseJson&b=1&b=2');\n  });\n};\n"
  },
  {
    "path": "test/units/tps.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request({\n    url: 'http://1.tps.whistlejs.com/index.html?name=tps1.json',\n    headers: {\n      'x-test': 'test',\n      cookie: 'cookieName=123; test=abc',\n      other: 'otherhaha'\n    }\n  }, function(res, data) {\n    data.cookieValue.should.equal('123');\n  });\n\n  util.request({\n    method: 'post',\n    headers: {\n      'x-test': 'test',\n      cookie: 'cookieName=123; test=abc; name=tps2.json',\n      other: 'otherhaha'\n    },\n    url: 'https://2.tps.whistlejs.com/?test=abc'\n  }, function(res, data) {\n    data.statusCode.should.equal('200');\n  });\n};\n"
  },
  {
    "path": "test/units/tunnel.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.proxy('http://test.whistlejs.com');\n  util.proxy('http://tnl1.whistlejs.com');\n  util.proxy('http://tnl2.whistlejs.com');\n  util.proxy('http://tnl3.whistlejs.com');\n  util.request('http://tnl4.whistlejs.com', function(res) {\n    var data = JSON.parse(res.body);\n    data.type.should.equal('server');\n  });\n  util.proxy('http://8080.tnl5.whistlejs.com');\n  util.proxy('http://8080.tnl6.whistlejs.com');\n  util.proxy('http://break.whistlejs.com', function(err) {\n    if (!err) {\n      throw Error('error');\n    }\n  });\n  util.proxy('http://ts.whistlejs.com/');\n};\n"
  },
  {
    "path": "test/units/tunnelPolicy.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('https://tp.w2.org/index.html', function(res, data) {\n    data.ruleValue.should.be.equal('global');\n    res.headers['content-type'].should.be.equal('text/html');\n  });\n\n  // util.request({\n  //   method: 'post',\n  //   url: 'https://tp.w2.org/index.html',\n  //   isTunnel: true\n  // }, function(res, data) {\n  //   data.body.should.be.equal('test');\n  //   (res.headers['content-type'] | '').should.not.be.equal('text/html');\n  // });\n};\n"
  },
  {
    "path": "test/units/ua.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://ua.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('user-agent', 'xxx');\n  });\n\n  util.request('https://ua.test.whistlejs.com/index.html', function(res, data) {\n    data.headers.should.have.property('user-agent', 'xxx');\n  });\n};\n"
  },
  {
    "path": "test/units/ui.test.js",
    "content": "\nvar util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('https://local.whistlejs.com/favicon.ico?doNotParseJson');\n  util.request('https://local.whistlejs.com/?doNotParseJson');\n  util.request('http://rd2webui.w2.org/index.html?doNotParseJson');\n  util.request('http://rd2webui.w2.org/?doNotParseJson');\n  util.request('http://local.whistlejs.com/index.html?doNotParseJson');\n  util.request('http://local.whistlejs.com/?doNotParseJson');\n  util.request('http://local.whistlejs.com:1234/index.html?doNotParseJson');\n  util.request('http://local.whistlejs.com/cgi-bin/log/get');\n  util.request('http://local.whistlejs.com/cgi-bin/init');\n  util.request('http://local.whistlejs.com:2345/cgi-bin/init');\n  util.request('http://local.whistlejs.com/cgi-bin/get-data');\n  util.request('http://local.whistlejs.com/cgi-bin/server-info');\n  util.request('http://local.whistlejs.com/cgi-bin/values/list');\n  util.request('http://local.whistlejs.com/cgi-bin/plugins/get-plugins');\n  util.request('http://local.whistlejs.com/cgi-bin/rules/list');\n  util.request('http://local.whistlejs.com/cgi-bin/rootca');\n  util.request('http://local.whistlejs.com/cgi-bin/root?doNotParseJson', function(res) {\n    res.statusCode.should.be.equal(404);\n  });\n\n  util.request('https://local.whistlejs.com/index.html?doNotParseJson');\n  util.request('https://local.whistlejs.com:1234/index.html?doNotParseJson');\n  util.request('https://local.whistlejs.com/cgi-bin/log/get');\n  util.request('https://local.whistlejs.com/cgi-bin/init');\n  util.request('https://local.whistlejs.com:2345/cgi-bin/init');\n  util.request('https://local.whistlejs.com/cgi-bin/get-data');\n  util.request('https://local.whistlejs.com/cgi-bin/server-info');\n  util.request('https://local.whistlejs.com/cgi-bin/values/list');\n  util.request('https://local.whistlejs.com/cgi-bin/plugins/get-plugins');\n  util.request('https://local.whistlejs.com/cgi-bin/rules/list');\n  util.request('https://local.whistlejs.com/whistle.test');\n  util.request('http://local.whistlejs.com/whistle.test');\n  util.request('https://local.whistlejs.com/plugin.test');\n  util.request('http://local.whistlejs.com/plugin.test');\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/log/get',\n    headers: {\n      origin: 'http://wproxy.org'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/values/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '123'\n    }\n  }, function() {\n    util.request({\n      url: 'http://local.whistlejs.com/cgi-bin/values/rename',\n      method: 'post',\n      form: {\n        name: 'test',\n        newName: '123'\n      }\n    }, function() {\n      util.request({\n        url: 'http://local.whistlejs.com/cgi-bin/values/remove',\n        method: 'post',\n        form: {\n          name: '123'\n        }\n      });\n    });\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/values/move-to',\n    method: 'post',\n    form: {\n      to: 'test',\n      from: 'abc'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/enable-default',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/remove',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/rename',\n    method: 'post',\n    form: {\n      name: 'test',\n      newName: 'sssss'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/select',\n    method: 'post',\n    form: {\n      name: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/unselect',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/allow-multiple-choice',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/log/set',\n    method: 'get',\n    form: {\n      level: 'error',\n      text: 'teset error log'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/composer',\n    method: 'post',\n    form: {\n      url: 'http://test.whistlejs.com/'\n    }\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/intercept-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/hide-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/do-not-show-again',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/check-update',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/move-to',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistlejs.com/cgi-bin/rules/get-sys-hosts',\n    method: 'post'\n  });\n\n\n  util.request('http://local.wproxy.org:1234/index.html?doNotParseJson');\n  util.request('http://local.wproxy.org:1234/index.html?doNotParseJson');\n  util.request('http://local.wproxy.org:1234/cgi-bin/log/get');\n  util.request('http://local.wproxy.org:1234/cgi-bin/init');\n  util.request('http://local.wproxy.org:1234/cgi-bin/get-data');\n  util.request('http://local.wproxy.org:1234/cgi-bin/server-info');\n  util.request('http://local.wproxy.org:1234/cgi-bin/values/list');\n  util.request('http://local.wproxy.org:1234/cgi-bin/plugins/get-plugins');\n  util.request('http://local.wproxy.org:1234/cgi-bin/rules/list');\n\n  util.request('https://local.wproxy.org:1234/index.html?doNotParseJson');\n  util.request('https://local.wproxy.org:1234/index.html?doNotParseJson');\n  util.request('https://local.wproxy.org:1234/cgi-bin/log/get');\n  util.request('https://local.wproxy.org:1234/cgi-bin/init');\n  util.request('https://local.wproxy.org:1234/cgi-bin/get-data');\n  util.request('https://local.wproxy.org:1234/cgi-bin/server-info');\n  util.request('https://local.wproxy.org:1234/cgi-bin/values/list');\n  util.request('https://local.wproxy.org:1234/cgi-bin/plugins/get-plugins');\n  util.request('https://local.wproxy.org:1234/cgi-bin/rules/list');\n  util.request('https://local.wproxy.org:1234/whistle.test');\n  util.request('http://local.wproxy.org:1234/whistle.test');\n  util.request('https://local.wproxy.org:1234/plugin.test');\n  util.request('http://local.wproxy.org:1234/plugin.test');\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/values/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '123'\n    }\n  }, function() {\n    util.request({\n      url: 'http://local.wproxy.org:1234/cgi-bin/values/rename',\n      method: 'post',\n      form: {\n        name: 'test',\n        newName: '123'\n      }\n    }, function() {\n      util.request({\n        url: 'http://local.wproxy.org:1234/cgi-bin/values/remove',\n        method: 'post',\n        form: {\n          name: '123'\n        }\n      });\n    });\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/values/move-to',\n    method: 'post',\n    form: {\n      to: 'test',\n      from: 'abc'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/enable-default',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/remove',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/rename',\n    method: 'post',\n    form: {\n      name: 'test',\n      newName: 'sssss'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/select',\n    method: 'post',\n    form: {\n      name: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/unselect',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/allow-multiple-choice',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/log/set',\n    method: 'get',\n    form: {\n      level: 'warn',\n      text: 'teset warn log'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/composer',\n    method: 'post',\n    form: {\n      url: 'http://test.whistlejs.com/'\n    }\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/intercept-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/hide-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/do-not-show-again',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/check-update',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/move-to',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.wproxy.org:1234/cgi-bin/rules/get-sys-hosts',\n    method: 'post'\n  });\n\n\n  util.request('http://local.whistle.com/index.html?doNotParseJson');\n  util.request('http://local.whistle.com:1234/index.html?doNotParseJson');\n  util.request('http://local.whistle.com/cgi-bin/log/get');\n  util.request('http://local.whistle.com/cgi-bin/init');\n  util.request('http://local.whistle.com:2345/cgi-bin/init');\n  util.request('http://local.whistle.com/cgi-bin/get-data');\n  util.request('http://local.whistle.com/cgi-bin/server-info');\n  util.request('http://local.whistle.com/cgi-bin/values/list');\n  util.request('http://local.whistle.com/cgi-bin/plugins/get-plugins');\n  util.request('http://local.whistle.com/cgi-bin/rules/list');\n\n  util.request('https://local.whistle.com/index.html?doNotParseJson');\n  util.request('https://local.whistle.com:1234/index.html?doNotParseJson');\n  util.request('https://local.whistle.com/cgi-bin/log/get');\n  util.request('https://local.whistle.com/cgi-bin/init');\n  util.request('https://local.whistle.com:2345/cgi-bin/init');\n  util.request('https://local.whistle.com/cgi-bin/get-data');\n  util.request('https://local.whistle.com/cgi-bin/server-info');\n  util.request('https://local.whistle.com/cgi-bin/values/list');\n  util.request('https://local.whistle.com/cgi-bin/plugins/get-plugins');\n  util.request('https://local.whistle.com/cgi-bin/rules/list');\n  util.request('https://local.whistle.com/whistle.test');\n  util.request('http://local.whistle.com/whistle.test');\n  util.request('https://local.whistle.com/plugin.test');\n  util.request('http://local.whistle.com/plugin.test');\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/values/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '123'\n    }\n  }, function() {\n    util.request({\n      url: 'http://local.whistle.com/cgi-bin/values/rename',\n      method: 'post',\n      form: {\n        name: 'test',\n        newName: '123'\n      }\n    }, function() {\n      util.request({\n        url: 'http://local.whistle.com/cgi-bin/values/remove',\n        method: 'post',\n        form: {\n          name: '123'\n        }\n      });\n    });\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/values/move-to',\n    method: 'post',\n    form: {\n      to: 'test',\n      from: 'abc'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/add',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/enable-default',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/remove',\n    method: 'post',\n    form: {\n      name: 'test',\n      value: '/test/ file://xxx'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/rename',\n    method: 'post',\n    form: {\n      name: 'test',\n      newName: 'sssss'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/select',\n    method: 'post',\n    form: {\n      name: 'test'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/unselect',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/allow-multiple-choice',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/log/set',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/composer',\n    method: 'post',\n    form: {\n      url: 'http://test.whistlejs.com/'\n    }\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/intercept-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/hide-https-connects',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/do-not-show-again',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/check-update',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/move-to',\n    method: 'post'\n  });\n  util.request({\n    url: 'http://local.whistle.com/cgi-bin/rules/get-sys-hosts',\n    method: 'post'\n  });\n\n  var mockUrl = 'http://mock.remote-key2.test.w2.org/test/path?doNotParseJson';\n  var serviceUrl = 'http://service.remote-key2.test.w2.org/test/path?doNotParseJson';\n  var shadowUrl = 'http://shadow.remote-key2.test.w2.org/test/path?doNotParseJson';\n\n  util.request('http://mock.remote-key.test.w2.org/test/path?doNotParseJson', function(_, data) {\n    data.should.equal('mock');\n  });\n  util.request('http://service.remote-key.test.w2.org/test/path?doNotParseJson', function(_, data) {\n    data.should.equal('service');\n  });\n  util.request('http://shadow.remote-key.test.w2.org/test/path?doNotParseJson', function(_, data) {\n    data.should.equal('shadow');\n  });\n  util.request(mockUrl, function(_, data) {\n    data.should.equal(mockUrl + 'mock123');\n  });\n  util.request(serviceUrl, function(_, data) {\n    data.should.equal(serviceUrl + 'service123');\n  });\n  util.request(shadowUrl, function(_, data) {\n    data.should.equal(shadowUrl + 'shadow123');\n  });\n\n  ['shadow', 'service', 'mock'].forEach(function(name) {\n    util.request({\n      url: 'http://' + name + '.remote-key3.test.w2.org/test/script/api',\n      headers: { test: 'abc' }\n    }, function(res, data) {\n      data.url.should.containEql('api');\n    });\n\n    util.request({\n      url: 'http://' + name + '.remote-key4.test.w2.org/test/script/proxy',\n      headers: { test: 'abc' }\n    }, function(res, data) {\n      data.url.should.containEql('proxy');\n    });\n  });\n};\n"
  },
  {
    "path": "test/units/urlParams.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://urlparams.test.whistlejs.com/index.html', function(res, data) {\n    // data.url.substring(data.url.indexOf('?') + 1).should.equal('test=abc');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://urlparams.test.whistlejs.com/index.html'\n  }, function(res, data) {\n    // data.url.substring(data.url.indexOf('?') + 1).should.equal('test=abc');\n  });\n};\n"
  },
  {
    "path": "test/units/urlReplace.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://urlreplace.host.test.whistlejs.com/index.html?name=aven&test=abc', function(res, data) {\n    data.url.should.equal('/index.html?user=ttven&test=ttbc');\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://urlreplace.host.test.whistlejs.com/index.html?name=aven&test=abc'\n  }, function(res, data) {\n    data.url.should.equal('/index.html?user=ttven&test=ttbc');\n  });\n};\n"
  },
  {
    "path": "test/units/utils.test.js",
    "content": "var fs = require('fs');\nvar path = require('path');\nvar parseUrl = require('../../lib/util/parse-url');\nvar zlib = require('../../lib/util/zlib');\n\nvar delims = ['<', '>', '\"', '`', ' '];\nvar unwise = ['{', '}', '|', '\\\\', '^', '`'].concat(delims);\nvar autoEscape = ['\\''].concat(unwise).join('');\nvar emptyGzipContent = fs.readFileSync(path.join(__dirname, '../assets/files/empty.txt'));\nvar testGzipContent = fs.readFileSync(path.join(__dirname, '../assets/files/test.txt'));\n\n\nmodule.exports = function() {\n  var path = '/$%^{}?{{}}嗖嗖嗖' + autoEscape;\n  var options = parseUrl('http://www.qq.com:8888' + path + '#hash' + autoEscape);\n  options.path.should.be.equal(path);\n  options.port.should.be.equal('8888');\n  options.host.should.be.equal('www.qq.com:8888');\n  options.hostname.should.be.equal('www.qq.com');\n  options.pathname.should.be.equal(path.replace(/[\\?#].*$/, ''));\n  options.protocol.should.be.equal('http:');\n  options = parseUrl('http://www.qq.com' + path + '#hash' + autoEscape);\n  options.path.should.be.equal(path);\n  (options.port + '').should.be.equal('null');\n  options.host.should.be.equal('www.qq.com');\n  options.hostname.should.be.equal('www.qq.com');\n  options.pathname.should.be.equal(path.replace(/[\\?#].*$/, ''));\n  options.protocol.should.be.equal('http:');\n  zlib.gunzip(emptyGzipContent, function(err, content) {\n    content.length.should.be.equal(0);\n  });\n  zlib.gunzip(testGzipContent, function(err, content) {\n    (content + '').should.be.equal('test');\n  });\n};\n\n"
  },
  {
    "path": "test/units/values.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://values1.avenwu.com/index.html', function(res, data) {\n    res.headers.should.have.property('x-res-test', 'res');\n    data.should.have.property('abc', 123);\n  });\n\n  util.request({\n    method: 'post',\n    url: 'http://values2.test.com/index.html',\n    body: 'ssi1,ssi2,ssi3',\n    headers: {\n      'content-type': 'text/plain'\n    }\n  }, function(res, data) {\n    res.headers.should.have.property('x-res-test2', 'res');\n    data.headers.should.have.property('x-req-test', 'req');\n    data.body.should.be.equal('include1.html,include2.html,include3.html');\n  });\n};\n"
  },
  {
    "path": "test/units/var.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://var1.wproxy.org/index.html', function(res, data) {\n    data.should.have.property('json1', 1);\n    data.should.have.property('json2', 2);\n    data.should.have.property('json3', 3);\n    data.should.have.property('json4', 4);\n    data.should.have.property('json5', 5);\n  });\n};\n"
  },
  {
    "path": "test/units/weinre.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  // util.request('http://weinre1.test2.whistlejs.com/index.html?resBody=_', function(res, body) {\n  //   body.should.containEql('http://local.whistlejs.com/...whistle-path.5b6af7b9884e1165...///weinre.6668/target/target-script-min.js#xxx');\n  // });\n  util.request('http://weinre1.test.whistlejs.com/index.html?resBody=_', function(res, body) {\n    body.should.containEql('/target/target-script-min.js#xxx');\n  });\n  util.request({\n    url: 'http://weinre2.test.whistlejs.com/index.html',\n    method: 'post',\n    body: 'sssssss'\n  });\n\n  util.request('https://weinre3.test.whistlejs.com/index.html');\n\n  util.request({\n    url: 'https://weinre4.test.whistlejs.com/index.html',\n    method: 'post',\n    body: 'sssssss'\n  });\n\n  // util.request('https://weinre1.test2.whistlejs.com:1234/index.html?resBody=_', function(res, body) {\n  //   body.should.containEql('https://local.whistlejs.com/...whistle-path.5b6af7b9884e1165...///weinre.6668/target/target-script-min.js#xxx');\n  // });\n  util.request('https://weinre1.test.whistlejs.com:1234/index.html?resBody=_', function(res, body) {\n    body.should.containEql('/target/target-script-min.js#xxx');\n  });\n\n  util.request({\n    url: 'http://weinre2.test.whistlejs.com:2345/index.html',\n    method: 'post',\n    body: 'sssssss'\n  });\n\n  util.request('https://weinre3.test.whistlejs.com:3456/index.html');\n\n  util.request({\n    url: 'https://weinre4.test.whistlejs.com:4567/index.html',\n    method: 'post',\n    body: 'sssssss'\n  });\n};\n"
  },
  {
    "path": "test/units/wildcard.test.js",
    "content": "var util = require('../util.test');\nvar assert = require('assert');\n\nmodule.exports = function() {\n  util.request('https://wildcard.cn/abc/index.html', function(res, data) {\n    data.name.should.be.equal('http');\n  });\n\n  util.request('https://www.qq.wildcard.cn/abc/index.html', function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request({\n    url: 'http://www.qq.wildcard1.cn/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request('http://www.wildcard1.com/abc/index.html', function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request({\n    url: 'https://w2.w1.wildcard1.com/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('http');\n  });\n\n  util.request({\n    url: 'http://www.qq.wildcard3.cn/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request('http://www.wildcard3.com/abc/index.html', function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request({\n    url: 'http://w2.w1.wildcard3.com/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('http');\n  });\n\n\n  util.request('http://www.wildcard2.com/1/2/3/abc/index.html', function(res, data) {\n    data.name.should.be.equal('http');\n  });\n  util.request({\n    url: 'http://w2.w1.wildcard2.com/1/2/3/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('http');\n  });\n\n  util.request('http://www.wildcard2.com/1/2/3/abc/index.html', function(res, data) {\n    data.name.should.be.equal('http');\n  });\n  util.request({\n    url: 'http://w2.w1.wildcard2.com/1/2/3/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('http');\n  });\n\n  util.request('https://www.wildcard2.com/abc/index.html', function(res, data) {\n    data.name.should.be.equal('https');\n  });\n  util.request({\n    url: 'https://w2.w1.wildcard2.com/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    data.name.should.be.equal('https');\n  });\n\n  util.request('https://test.wildcard5.com/abc/index.html', function(res, data) {\n    data.list[10].should.be.equal('a\\rb\\nc');\n    data.list[20].should.be.equal(123);\n    data.list[30].should.be.equal('123');\n    data.list[40].a.should.be.equal('123');\n  });\n\n  util.request('https://test.wildcard5.com/wildcard5/_____/abc/index.html?test=123', function(res, data) {\n    data.list[10].should.be.equal('a\\rb\\nc');\n    data.list[20].should.be.equal(123);\n    data.list[30].should.be.equal('123');\n    data.list[40].a.should.be.equal('123');\n    data.ec.should.be.equal('abc/index.html');\n  });\n\n  util.request('https://w1.test.wildcard5.com/abc/index.html', function(res, data) {\n    data.list[10].should.be.equal('a\\rb\\nc');\n    data.list[20].should.be.equal(123);\n    data.list[30].should.be.equal('123');\n    data.list[40].a.should.be.equal('123');\n  });\n  util.request({\n    url: 'https://w2.w1.test.wildcard5.com/abc/index.html',\n    method: 'post',\n    body: 'sssssss'\n  }, function(res, data) {\n    assert(data.list === undefined);\n  });\n};\n"
  },
  {
    "path": "test/units/write.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://reqwrite.test.whistlejs.com/index.html');\n  util.request({\n    url: 'https://reqwrite.test.whistlejs.com/index.html',\n    method: 'post',\n    body: util.getTextBySize(32)\n  });\n\n  util.request('https://reswrite2.test.whistlejs.com/index.html');\n  util.request({\n    url: 'http://reswrite2.test.whistlejs.com/index.html',\n    method: 'post',\n    body: util.getTextBySize(128)\n  });\n};\n"
  },
  {
    "path": "test/units/ws.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('ws://test.whistlejs.com/index.html', function(data) {\n    data.host.should.equal('127.0.0.1:8080');\n  });\n\n  util.request('wss://ws1.test.whistlejs.com/index.html', function(data) {\n    data.host.should.equal('127.0.0.1:9999');\n  });\n  util.request('wss://test.whistlejs.com/ups.html?abc=123', function(data) {\n    // data.host.should.equal('127.0.0.1:9999');\n  });\n  util.request('wss://test.whistlejs.com/index2.html?abc=321', function(data) {\n    data.host.should.equal('127.0.0.1:8080');\n  });\n  util.requestWS('ws://test.whistlejs.com/index2.html?abc=321', function(data) {\n    data.host.should.equal('127.0.0.1:8080');\n  });\n  util.requestWS('ws://status.whistlejs.com/checkStatusCode.html', function(data) {\n    data.should.equal('checkStatusCode');\n  });\n};\n"
  },
  {
    "path": "test/units/xfile.test.js",
    "content": "var util = require('../util.test');\n\nmodule.exports = function() {\n  util.request('http://xfile.test.whistlejs.com/index2.html', function(res, data) {\n    data.should.have.property('type', 'server');\n  });\n};\n"
  },
  {
    "path": "test/util.test.js",
    "content": "var http = require('http');\nvar fs = require('fs');\nvar path = require('path');\nvar tls = require('tls');\nvar httpsOverHttp = require('hagent').agent.httpsOverHttp;\nvar parseUrl = require('url').parse;\nvar httpsAgent = require('https').Agent;\nvar httpAgent = require('http').Agent;\nvar WebSocket = require('ws');\nvar events = require('./events');\nvar config = require('./config.test');\nvar request = require('request');\nvar requestProxy = request.defaults({\n  proxy : 'http://127.0.0.1:' + config.port\n});\nvar count = 0;\nvar end;\nexports.setEnd = function() {\n  end = true;\n};\n\nfunction exit() {\n  if (--count <= 0) {\n    if (end) {\n      process.exit(0);\n    } else {\n      events.emit('next');\n    }\n  }\n}\n\nfunction setHost(fullUrl, opts) {\n  var host = opts.host;\n  if (host || opts.port > 0) {\n    var urlOpts = parseUrl(fullUrl);\n    host = host || urlOpts.hostname;\n    var port = opts.port || urlOpts.port;\n    if (port) {\n      host = host + ':' + port;\n    }\n    fullUrl = fullUrl.replace(/\\/\\/[^/]+/, '//' + host);\n  }\n  return fullUrl.replace(/^ws/, 'http');\n}\n\nexports.requestWS = function(url, callback) {\n  ++count;\n  var options = parseUrl(url);\n  var ws = new WebSocket(setHost(url, { host: '127.0.0.1', port: config.port }), {\n    headers: { host: options.host },\n    rejectUnauthorized: true\n  });\n  var done;\n  ws.on('open', function open() {\n    if (/checkStatusCode/.test(url)) {\n      if (done) {\n        return;\n      }\n      done = true;\n      callback && callback('checkStatusCode');\n      exit();\n    } else {\n      ws.send('something');\n    }\n  });\n  ws.on('message', function(data) {\n    if (done) {\n      return;\n    }\n    done = true;\n    callback && callback(JSON.parse(data));\n    exit();\n  });\n};\n\nexports.request = function(options, callback) {\n  ++count;\n\n  if (/^ws/.test(options)) {\n    var url = options;\n    var opts = parseUrl(url);\n    var isSsl = /^wss:/.test(url);\n    require('../lib/config').connect({\n      proxyHost: '127.0.0.1',\n      proxyPort: config.port,\n      host: opts.hostname,\n      port: opts.port || (isSsl ? 443 : 80),\n      headers: {\n        host: opts.host,\n        'proxy-connection': 'keep-alive'\n      }\n    }, function(socket) {\n      var agent = isSsl ? new httpsAgent() : httpAgent();\n      if (isSsl) {\n        socket = tls.connect({\n          rejectUnauthorized: false,\n          socket: socket,\n          servername: opts.hostname\n        });\n      }\n      agent.createConnection = function() {\n        return socket;\n      };\n      var ws = new WebSocket(url, {\n        agent: agent,\n        rejectUnauthorized: true\n      });\n\n      ws.on('open', function open() {\n        ws.send('something');\n      });\n      var done;\n      ws.on('message', function(data) {\n        if (done) {\n          return;\n        }\n        done = true;\n        callback && callback(JSON.parse(data));\n        exit();\n      });\n    });\n  } else {\n    if (typeof options == 'string') {\n      options = {\n        url: options,\n        rejectUnauthorized : false\n      };\n    }\n    if (options.isTunnel) {\n      options.agent = httpsOverHttp({\n        proxy: {\n          host: '127.0.0.1',\n          port: config.port,\n          url: options.url,\n          headers: {\n            host: parseUrl(options.url).host,\n            'x-whistle-policy': 'tunnel'\n          }\n        },\n        rejectUnauthorized: false\n      });\n    }\n    (options.isTunnel ? request : requestProxy)(options, function(err, res, body) {\n      try {\n        callback && callback(res, /\\?resBody=/.test(options.url) ? body : (/doNotParseJson/.test(options.url) ? body : JSON.parse(body)), err);\n      } catch(e) {\n        /*eslint no-console: \"off\"*/\n        console.log(options);\n        throw e;\n      }\n      exit();\n    });\n  }\n};\n\nfunction noop() {}\n\nexports.noop = noop;\n\nfunction getTextBySize(size) {\n\n  return new Array(size + 1).join('1');\n}\n\nexports.getTextBySize = getTextBySize;\n\nfunction connect(host, port, callback) {\n  var done;\n  var execCallback = function(err, socket) {\n    if (done) {\n      return;\n    }\n    done = true;\n    callback(err, socket);\n  };\n  var req = http.request({\n    method: 'CONNECT',\n    host: '127.0.0.1',\n    port: config.port,\n    agent: false,\n    headers: {\n      'user-agent': 'test/whistle',\n      'proxy-connection': 'keep-alive',\n      'x-whistle-policy': 'tunnel',\n      'x-forwarded-for': '3.3.3.3',\n      host: host + (port ? ':' + port : '')\n    }\n  });\n  req.on('error', execCallback);\n  req.on('connect', function (res, socket, head) {\n    execCallback(null, socket);\n  });\n  req.end();\n}\n\nfunction proxy(url, callback) {\n  ++count;\n  var options = parseUrl(url);\n  connect(options.hostname, options.port, function(err, socket) {\n    if (err) {\n      if (callback) {\n        callback(err);\n      } else {\n        throw err;\n      }\n      exit();\n      return;\n    }\n\n    options.createConnection = function() {\n      return socket;\n    };\n\n    http.request(options, function(res) {\n      if (callback) {\n        res.on('data', noop);\n        var done;\n        res.on('error', function(err) {\n          if (done) {\n            return;\n          }\n          done = true;\n          callback(err, res);\n          exit();\n        });\n        res.on('end', function() {\n          if (done) {\n            return;\n          }\n          done = true;\n          callback(err, res);\n          exit();\n        });\n      } else {\n        exit();\n      }\n    }).end();\n  });\n}\n\nexports.proxy = proxy;\n\nfunction getValues() {\n  var values = {};\n  var dir = path.join(__dirname, 'assets/values');\n  fs.readdirSync(dir).map(function(name) {\n    values[name] = fs.readFileSync(path.join(dir, name), {encoding: 'utf8'});\n  });\n  return values;\n}\n\nexports.getValues = getValues;\n\nexports.readText = function(file) {\n  return fs.readFileSync(path.join(__dirname, file), {encoding: 'utf8'});\n};\n\nexports.setPath = function(str) {\n  return str.split('$__dirname$').join(__dirname);\n};\n"
  }
]